mirror of
https://github.com/4ian/GDevelop.git
synced 2025-10-15 10:19:04 +00:00
[TileMap] Collision mask object (#3313)
* The collision mask is read from the tile maps exported by Tiled 1.9+. * Several collision masks can be used for instance to have platforms and ladders. * Take a look to the wiki for more details https://wiki.gdevelop.io/gdevelop5/objects/tilemap
This commit is contained in:
@@ -1032,6 +1032,7 @@ namespace gdjs {
|
||||
) {
|
||||
continue;
|
||||
}
|
||||
|
||||
if (
|
||||
gdjs.RuntimeObject.collisionTest(
|
||||
this.owner,
|
||||
|
File diff suppressed because it is too large
Load Diff
150
Extensions/TileMap/TileMapRuntimeManager.ts
Normal file
150
Extensions/TileMap/TileMapRuntimeManager.ts
Normal file
@@ -0,0 +1,150 @@
|
||||
/// <reference path="helper/TileMapHelper.d.ts" />
|
||||
namespace gdjs {
|
||||
export interface RuntimeScene {
|
||||
tileMapCollisionMaskManager: gdjs.TileMap.TileMapRuntimeManager;
|
||||
}
|
||||
export namespace TileMap {
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
|
||||
const logger = new gdjs.Logger('Tilemap object');
|
||||
|
||||
/**
|
||||
* A holder to share tile maps across the 2 extension objects.
|
||||
*
|
||||
* Every instance with the same files path in properties will
|
||||
* share the same {@link EditableTileMap} and {@link TileTextureCache}.
|
||||
*
|
||||
* To use a tile map with collisions, a user can create 4 objects:
|
||||
* - one for the the rendering
|
||||
* - one for the solid platforms
|
||||
* - one for the jumpthrus
|
||||
* - one for the ladders
|
||||
*
|
||||
* To avoid to have 4 copies of the same tile map in memory, this manager
|
||||
* puts the tile map in cache and avoid unnecessary parsing.
|
||||
*
|
||||
* @see {@link TileMapManager}
|
||||
*/
|
||||
export class TileMapRuntimeManager {
|
||||
private _runtimeScene: gdjs.RuntimeScene;
|
||||
/**
|
||||
* Delegate that actually manage the caches without anything specific to
|
||||
* GDJS.
|
||||
* It allows to factorize code with the IDE.
|
||||
*/
|
||||
private _manager: TileMapHelper.TileMapManager;
|
||||
/**
|
||||
* @param runtimeScene The scene.
|
||||
*/
|
||||
private constructor(runtimeScene: gdjs.RuntimeScene) {
|
||||
this._runtimeScene = runtimeScene;
|
||||
this._manager = new TileMapHelper.TileMapManager();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param runtimeScene Where to set the manager instance.
|
||||
* @returns The shared manager.
|
||||
*/
|
||||
static getManager(
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
): TileMapRuntimeManager {
|
||||
if (!runtimeScene.tileMapCollisionMaskManager) {
|
||||
// Create the shared manager if necessary.
|
||||
runtimeScene.tileMapCollisionMaskManager = new TileMapRuntimeManager(
|
||||
runtimeScene
|
||||
);
|
||||
}
|
||||
return runtimeScene.tileMapCollisionMaskManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tileMapJsonResourceName The resource name of the tile map.
|
||||
* @param tileSetJsonResourceName The resource name of the tile set.
|
||||
* @param callback A function called when the tile map is parsed.
|
||||
*/
|
||||
getOrLoadTileMap(
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (tileMap: TileMapHelper.EditableTileMap | null) => void
|
||||
): void {
|
||||
this._manager.getOrLoadTileMap(
|
||||
this._loadTiledMap.bind(this),
|
||||
tileMapJsonResourceName,
|
||||
tileSetJsonResourceName,
|
||||
pako,
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param getTexture The method that loads the atlas image file in memory.
|
||||
* @param atlasImageResourceName The resource name of the atlas image.
|
||||
* @param tileMapJsonResourceName The resource name of the tile map.
|
||||
* @param tileSetJsonResourceName The resource name of the tile set.
|
||||
* @param callback A function called when the tiles textures are split.
|
||||
*/
|
||||
getOrLoadTextureCache(
|
||||
getTexture: (textureName: string) => PIXI.BaseTexture<PIXI.Resource>,
|
||||
atlasImageResourceName: string,
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (textureCache: TileMapHelper.TileTextureCache | null) => void
|
||||
): void {
|
||||
this._manager.getOrLoadTextureCache(
|
||||
this._loadTiledMap.bind(this),
|
||||
getTexture,
|
||||
atlasImageResourceName,
|
||||
tileMapJsonResourceName,
|
||||
tileSetJsonResourceName,
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse both JSON and set the content of the tile set in the right
|
||||
* attribute in the tile map to merge both parsed data.
|
||||
*/
|
||||
private _loadTiledMap(
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (tiledMap: TileMapHelper.TiledMap | null) => void
|
||||
): void {
|
||||
this._runtimeScene
|
||||
.getGame()
|
||||
.getJsonManager()
|
||||
.loadJson(tileMapJsonResourceName, (error, tileMapJsonData) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
'An error happened while loading a Tilemap JSON data:',
|
||||
error
|
||||
);
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
const tiledMap = tileMapJsonData as TileMapHelper.TiledMap;
|
||||
if (tileSetJsonResourceName) {
|
||||
this._runtimeScene
|
||||
.getGame()
|
||||
.getJsonManager()
|
||||
.loadJson(tileSetJsonResourceName, (error, tileSetJsonData) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
'An error happened while loading Tileset JSON data:',
|
||||
error
|
||||
);
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
const tileSet = tileSetJsonData as TileMapHelper.TiledTileset;
|
||||
tileSet.firstgid = tiledMap.tilesets[0].firstgid;
|
||||
tiledMap.tilesets = [tileSet];
|
||||
callback(tiledMap);
|
||||
});
|
||||
} else {
|
||||
callback(tiledMap);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
87
Extensions/TileMap/collision/TileMapCollisionMaskRenderer.ts
Normal file
87
Extensions/TileMap/collision/TileMapCollisionMaskRenderer.ts
Normal file
@@ -0,0 +1,87 @@
|
||||
namespace gdjs {
|
||||
export namespace TileMap {
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
|
||||
/**
|
||||
* This render is only useful for debugging purposes.
|
||||
* @see {@link PixiTileMapHelper.updatePixiCollisionMask}, the render used by the GUI.
|
||||
*/
|
||||
export class TileMapCollisionMaskRenderer {
|
||||
_object: gdjs.TileMapCollisionMaskRuntimeObject;
|
||||
_graphics: PIXI.Graphics;
|
||||
|
||||
constructor(
|
||||
runtimeObject: gdjs.TileMapCollisionMaskRuntimeObject,
|
||||
runtimeScene: gdjs.RuntimeScene
|
||||
) {
|
||||
this._object = runtimeObject;
|
||||
this._graphics = new PIXI.Graphics();
|
||||
runtimeScene
|
||||
.getLayer('')
|
||||
.getRenderer()
|
||||
.addRendererObject(this._graphics, runtimeObject.getZOrder());
|
||||
}
|
||||
|
||||
redrawCollisionMask() {
|
||||
this._graphics.clear();
|
||||
if (!this._object._debugMode) {
|
||||
return;
|
||||
}
|
||||
this._graphics.lineStyle(
|
||||
this._object._outlineSize,
|
||||
this._object._outlineColor,
|
||||
this._object._outlineOpacity / 255
|
||||
);
|
||||
for (const polygon of this._object.getHitBoxes()) {
|
||||
const vertices = polygon.vertices;
|
||||
if (vertices.length === 0) continue;
|
||||
|
||||
this._graphics.beginFill(
|
||||
this._object._fillColor,
|
||||
this._object._fillOpacity / 255
|
||||
);
|
||||
this._graphics.moveTo(vertices[0][0], vertices[0][1]);
|
||||
for (let index = 1; index < vertices.length; index++) {
|
||||
this._graphics.lineTo(vertices[index][0], vertices[index][1]);
|
||||
}
|
||||
this._graphics.closePath();
|
||||
this._graphics.endFill();
|
||||
}
|
||||
}
|
||||
|
||||
getRendererObject() {
|
||||
return this._graphics;
|
||||
}
|
||||
|
||||
setWidth(width: float): void {
|
||||
const tileMap = this._object._collisionTileMap;
|
||||
this._graphics.scale.x = width / tileMap.getWidth();
|
||||
this._graphics.pivot.x = width / 2;
|
||||
}
|
||||
|
||||
setHeight(height: float): void {
|
||||
const tileMap = this._object._collisionTileMap;
|
||||
this._graphics.scale.y = height / tileMap.getHeight();
|
||||
this._graphics.pivot.y = height / 2;
|
||||
}
|
||||
|
||||
getWidth(): float {
|
||||
const tileMap = this._object._collisionTileMap;
|
||||
return tileMap.getWidth() * this._graphics.scale.x;
|
||||
}
|
||||
|
||||
getHeight(): float {
|
||||
const tileMap = this._object._collisionTileMap;
|
||||
return tileMap.getHeight() * this._graphics.scale.y;
|
||||
}
|
||||
|
||||
getScaleX(): float {
|
||||
return this._graphics.scale.x;
|
||||
}
|
||||
|
||||
getScaleY(): float {
|
||||
return this._graphics.scale.y;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
747
Extensions/TileMap/collision/TransformedTileMap.ts
Normal file
747
Extensions/TileMap/collision/TransformedTileMap.ts
Normal file
@@ -0,0 +1,747 @@
|
||||
/// <reference path="../helper/TileMapHelper.d.ts" />
|
||||
namespace gdjs {
|
||||
export namespace TileMap {
|
||||
/**
|
||||
* A tile map transformed with an affine transformation.
|
||||
*
|
||||
* @see {@link getHitboxesAround} It gives a fast access to hitboxes for collision handling.
|
||||
*/
|
||||
export class TransformedCollisionTileMap {
|
||||
/**
|
||||
* The model that describes the tile map.
|
||||
*/
|
||||
private _source: TileMapHelper.EditableTileMap;
|
||||
tag: string;
|
||||
private _layers: Map<integer, TransformedCollisionTileMapLayer>;
|
||||
// TODO Tiled allows to offset the layers
|
||||
/**
|
||||
* The transformation from the time map coordinate (in pixels)
|
||||
* to the scene coordinate (in pixels).
|
||||
*/
|
||||
private _transformation: gdjs.AffineTransformation = new gdjs.AffineTransformation();
|
||||
/**
|
||||
* The transformation from the scene coordinate (in pixels)
|
||||
* to the time map coordinate (in pixels).
|
||||
*/
|
||||
private _inverseTransformation: gdjs.AffineTransformation = new gdjs.AffineTransformation();
|
||||
/**
|
||||
* This allows tiles to know if their hitboxes must be updated.
|
||||
* @see {@link TransformedCollisionTile.affineTransformationUpToDateCount}
|
||||
*/
|
||||
_transformationUpToDateCount: integer = 1;
|
||||
/**
|
||||
* An reusable Point to avoid allocations.
|
||||
*/
|
||||
private static readonly workingPoint: FloatPoint = [0, 0];
|
||||
|
||||
/**
|
||||
* @param source The model that describes the tile map.
|
||||
*/
|
||||
constructor(source: TileMapHelper.EditableTileMap, tag: string) {
|
||||
this._source = source;
|
||||
this.tag = tag;
|
||||
this._layers = new Map<integer, TransformedCollisionTileMapLayer>();
|
||||
for (const sourceLayer of source.getLayers()) {
|
||||
// TODO A visitor could be used to avoid a cast.
|
||||
if (!(sourceLayer instanceof TileMapHelper.EditableTileMapLayer)) {
|
||||
// TODO Collision mask for object layers is not handled.
|
||||
continue;
|
||||
}
|
||||
const tileLayer = sourceLayer as TileMapHelper.EditableTileMapLayer;
|
||||
this._layers.set(
|
||||
tileLayer.id,
|
||||
new TransformedCollisionTileMapLayer(this, tileLayer)
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The transformation from the time map coordinate (in pixels)
|
||||
* to the scene coordinate (in pixels).
|
||||
*/
|
||||
getTransformation(): gdjs.AffineTransformation {
|
||||
return this._transformation;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param transformation the transformation from the time map coordinate
|
||||
* (in pixels) to the scene coordinate (in pixels).
|
||||
*/
|
||||
setTransformation(transformation: gdjs.AffineTransformation) {
|
||||
this._transformation = transformation;
|
||||
|
||||
const inverseTransformation = this._inverseTransformation;
|
||||
inverseTransformation.copyFrom(transformation);
|
||||
inverseTransformation.invert();
|
||||
|
||||
this._invalidate();
|
||||
}
|
||||
|
||||
private _invalidate() {
|
||||
this._transformationUpToDateCount =
|
||||
(this._transformationUpToDateCount + 1) % Number.MAX_SAFE_INTEGER;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile map width in pixels.
|
||||
*/
|
||||
getWidth() {
|
||||
return this._source.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile map height in pixels.
|
||||
*/
|
||||
getHeight() {
|
||||
return this._source.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile width in pixels.
|
||||
*/
|
||||
getTileHeight() {
|
||||
return this._source.getTileHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile height in pixels.
|
||||
*/
|
||||
getTileWidth() {
|
||||
return this._source.getTileWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The number of tile columns in the map.
|
||||
*/
|
||||
getDimensionX() {
|
||||
return this._source.getDimensionX();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The number of tile rows in the map.
|
||||
*/
|
||||
getDimensionY() {
|
||||
return this._source.getDimensionY();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tileId The tile identifier
|
||||
* @returns The tile definition form the tile set.
|
||||
*/
|
||||
getTileDefinition(tileId: integer) {
|
||||
return this._source.getTileDefinition(tileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param layerId The layer identifier.
|
||||
* @returns the layer
|
||||
*/
|
||||
getLayer(layerId: integer): TransformedCollisionTileMapLayer | undefined {
|
||||
return this._layers.get(layerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns All the layers of the tile map.
|
||||
*/
|
||||
getLayers(): Iterable<TransformedCollisionTileMapLayer> {
|
||||
return this._layers.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a point is inside a tile with a given tag.
|
||||
*
|
||||
* It doesn't use the tile hitboxes.
|
||||
* It only check the point is inside the tile square.
|
||||
*
|
||||
* @param x The X coordinate of the point to check.
|
||||
* @param y The Y coordinate of the point to check.
|
||||
* @param tag The tile tag
|
||||
* @returns true when the point is inside a tile with a given tag.
|
||||
*/
|
||||
pointIsInsideTile(x: float, y: float, tag: string): boolean {
|
||||
const workingPoint: FloatPoint =
|
||||
TransformedCollisionTileMap.workingPoint;
|
||||
workingPoint[0] = x;
|
||||
workingPoint[1] = y;
|
||||
this._inverseTransformation.transform(workingPoint, workingPoint);
|
||||
return this._source.pointIsInsideTile(
|
||||
workingPoint[0],
|
||||
workingPoint[1],
|
||||
tag
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag The tile tag.
|
||||
* @param left The left border of the area in the scene.
|
||||
* @param top The top border of the area in the scene.
|
||||
* @param right The right border of the area in the scene.
|
||||
* @param bottom The left border of the area in the scene.
|
||||
* @returns At least all the hitboxes from the given area
|
||||
* where tiles have the right tag.
|
||||
*
|
||||
* @see {@link gdjs.RuntimeObject.getHitboxesAround}
|
||||
*/
|
||||
getHitboxesAround(
|
||||
tag: string,
|
||||
left: float,
|
||||
top: float,
|
||||
right: float,
|
||||
bottom: float
|
||||
): Iterable<gdjs.Polygon> {
|
||||
// Return the hitboxes from the tiles that overlap
|
||||
// the AABB of the area in the tile map basis.
|
||||
// Some of these tiles are not event in the given area
|
||||
// but this is a good trade of between the number of
|
||||
// useless returned hitboxes and the time to find them.
|
||||
|
||||
// Transform the vertices of the area
|
||||
// from the scene basis to the tile map basis.
|
||||
const inverseTransformation = this._inverseTransformation;
|
||||
const workingPoint: FloatPoint =
|
||||
TransformedCollisionTileMap.workingPoint;
|
||||
|
||||
workingPoint[0] = left;
|
||||
workingPoint[1] = top;
|
||||
inverseTransformation.transform(workingPoint, workingPoint);
|
||||
const topLeftX = workingPoint[0];
|
||||
const topLeftY = workingPoint[1];
|
||||
|
||||
workingPoint[0] = right;
|
||||
workingPoint[1] = top;
|
||||
inverseTransformation.transform(workingPoint, workingPoint);
|
||||
const topRightX = workingPoint[0];
|
||||
const topRightY = workingPoint[1];
|
||||
|
||||
workingPoint[0] = right;
|
||||
workingPoint[1] = bottom;
|
||||
inverseTransformation.transform(workingPoint, workingPoint);
|
||||
const bottomRightX = workingPoint[0];
|
||||
const bottomRightY = workingPoint[1];
|
||||
|
||||
workingPoint[0] = left;
|
||||
workingPoint[1] = bottom;
|
||||
inverseTransformation.transform(workingPoint, workingPoint);
|
||||
const bottomLeftX = workingPoint[0];
|
||||
const bottomLeftY = workingPoint[1];
|
||||
|
||||
// Calculate the AABB of the area in the tile map basis.
|
||||
const xMin = Math.max(
|
||||
0,
|
||||
Math.floor(
|
||||
Math.min(topLeftX, topRightX, bottomRightX, bottomLeftX) /
|
||||
this._source.getTileWidth()
|
||||
)
|
||||
);
|
||||
const xMax = Math.min(
|
||||
this.getDimensionX() - 1,
|
||||
Math.floor(
|
||||
Math.max(topLeftX, topRightX, bottomRightX, bottomLeftX) /
|
||||
this._source.getTileWidth()
|
||||
)
|
||||
);
|
||||
const yMin = Math.max(
|
||||
0,
|
||||
Math.floor(
|
||||
Math.min(topLeftY, topRightY, bottomRightY, bottomLeftY) /
|
||||
this._source.getTileHeight()
|
||||
)
|
||||
);
|
||||
const yMax = Math.min(
|
||||
this.getDimensionY() - 1,
|
||||
Math.floor(
|
||||
Math.max(topLeftY, topRightY, bottomRightY, bottomLeftY) /
|
||||
this._source.getTileHeight()
|
||||
)
|
||||
);
|
||||
|
||||
return this.getHitboxes(tag, xMin, yMin, xMax, yMax);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag The tile tag.
|
||||
* @param xMin The fist column to include.
|
||||
* @param yMin The fist row to include.
|
||||
* @param xMax The last column to include.
|
||||
* @param yMax The last row to include.
|
||||
* @returns All the hitboxes from the tiles overlapping
|
||||
* the given area where tiles have the right tag.
|
||||
*/
|
||||
getHitboxes(
|
||||
tag: string,
|
||||
xMin: integer,
|
||||
yMin: integer,
|
||||
xMax: integer,
|
||||
yMax: integer
|
||||
): Iterable<gdjs.Polygon> {
|
||||
return new MapCollisionMaskIterable(this, tag, xMin, yMin, xMax, yMax);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag The tile tag.
|
||||
* @returns All the hitboxes from the tiles having the right tag.
|
||||
*/
|
||||
getAllHitboxes(tag: string): Iterable<gdjs.Polygon> {
|
||||
return this.getHitboxes(
|
||||
tag,
|
||||
0,
|
||||
0,
|
||||
this._source.getDimensionX() - 1,
|
||||
this._source.getDimensionY() - 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable over the tile hitboxes of a given area and tag.
|
||||
*/
|
||||
class MapCollisionMaskIterable implements Iterable<gdjs.Polygon> {
|
||||
map: TransformedCollisionTileMap;
|
||||
tag: string;
|
||||
xMin: integer;
|
||||
yMin: integer;
|
||||
xMax: integer;
|
||||
yMax: integer;
|
||||
|
||||
/**
|
||||
* Avoid to allocate an empty iterator each time
|
||||
* the iterable is initialized.
|
||||
*/
|
||||
static emptyItr: Iterator<gdjs.Polygon> = {
|
||||
next: () => ({ value: undefined, done: true }),
|
||||
};
|
||||
|
||||
/**
|
||||
* @param map The tile map.
|
||||
* @param tag The tile tag.
|
||||
* @param xMin The fist column to include.
|
||||
* @param yMin The fist row to include.
|
||||
* @param xMax The last column to include.
|
||||
* @param yMax The last row to include.
|
||||
*/
|
||||
constructor(
|
||||
map: TransformedCollisionTileMap,
|
||||
tag: string,
|
||||
xMin: integer,
|
||||
yMin: integer,
|
||||
xMax: integer,
|
||||
yMax: integer
|
||||
) {
|
||||
this.map = map;
|
||||
this.tag = tag;
|
||||
this.xMin = xMin;
|
||||
this.yMin = yMin;
|
||||
this.xMax = xMax;
|
||||
this.yMax = yMax;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
// Flatten the iterable of each layers into one.
|
||||
let layerItr = this.map.getLayers()[Symbol.iterator]();
|
||||
let listItr: Iterator<gdjs.Polygon> = MapCollisionMaskIterable.emptyItr;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
let listNext = listItr.next();
|
||||
while (listNext.done) {
|
||||
const layerNext = layerItr.next();
|
||||
if (layerNext.done) {
|
||||
return listNext;
|
||||
}
|
||||
listItr = layerNext.value
|
||||
.getHitboxes(
|
||||
this.tag,
|
||||
this.xMin,
|
||||
this.yMin,
|
||||
this.xMax,
|
||||
this.yMax
|
||||
)
|
||||
[Symbol.iterator]();
|
||||
listNext = listItr.next();
|
||||
}
|
||||
return listNext;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile map layer transformed with an affine transformation.
|
||||
*/
|
||||
export class TransformedCollisionTileMapLayer {
|
||||
/**
|
||||
* The time map that contains this layer.
|
||||
*/
|
||||
readonly tileMap: TransformedCollisionTileMap;
|
||||
/**
|
||||
* The model that describes the tile map.
|
||||
*/
|
||||
readonly _source: TileMapHelper.EditableTileMapLayer;
|
||||
private readonly _tiles: TransformedCollisionTile[][];
|
||||
|
||||
/**
|
||||
* @param tileMap The time map that contains this layer.
|
||||
* @param source The model that describes the tile map.
|
||||
*/
|
||||
constructor(
|
||||
tileMap: TransformedCollisionTileMap,
|
||||
source: TileMapHelper.EditableTileMapLayer
|
||||
) {
|
||||
this.tileMap = tileMap;
|
||||
this._source = source;
|
||||
this._tiles = [];
|
||||
const dimX = this._source.getDimensionX();
|
||||
const dimY = this._source.getDimensionY();
|
||||
this._tiles.length = dimY;
|
||||
for (let y = 0; y < dimY; y++) {
|
||||
this._tiles[y] = [];
|
||||
this._tiles[y].length = dimX;
|
||||
for (let x = 0; x < dimX; x++) {
|
||||
this._tiles[y][x] = new TransformedCollisionTile(this, x, y);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @return The tile from the tile set.
|
||||
*/
|
||||
get(x: integer, y: integer): TransformedCollisionTile | undefined {
|
||||
const row = this._tiles[y];
|
||||
return row ? row[x] : undefined;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of tile columns in the layer.
|
||||
*/
|
||||
getDimensionX() {
|
||||
return this._tiles.length === 0 ? 0 : this._tiles[0].length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of tile rows in the layer.
|
||||
*/
|
||||
getDimensionY() {
|
||||
return this._tiles.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The layer width in pixels.
|
||||
*/
|
||||
getWidth() {
|
||||
return this._source.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The layer height in pixels.
|
||||
*/
|
||||
getHeight() {
|
||||
return this._source.getHeight();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped diagonally.
|
||||
*/
|
||||
isFlippedDiagonally(x: integer, y: integer) {
|
||||
return this._source.isFlippedDiagonally(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped vertically.
|
||||
*/
|
||||
isFlippedVertically(x: integer, y: integer) {
|
||||
return this._source.isFlippedVertically(x, y);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped horizontally.
|
||||
*/
|
||||
isFlippedHorizontally(x: integer, y: integer) {
|
||||
return this._source.isFlippedHorizontally(x, y);
|
||||
}
|
||||
/**
|
||||
* @param tag The tile tag.
|
||||
* @param xMin The fist column to include.
|
||||
* @param yMin The fist row to include.
|
||||
* @param xMax The last column to include.
|
||||
* @param yMax The last row to include.
|
||||
* @returns All the hitboxes from the tiles overlapping
|
||||
* the given area where tiles have the right tag.
|
||||
*/
|
||||
getHitboxes(
|
||||
tag: string,
|
||||
xMin: integer,
|
||||
yMin: integer,
|
||||
xMax: integer,
|
||||
yMax: integer
|
||||
): Iterable<gdjs.Polygon> {
|
||||
return new LayerCollisionMaskIterable(
|
||||
this,
|
||||
tag,
|
||||
xMin,
|
||||
yMin,
|
||||
xMax,
|
||||
yMax
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tag The tile tag.
|
||||
* @returns All the hitboxes from the tiles having the right tag.
|
||||
*/
|
||||
getAllHitboxes(tag: string): Iterable<gdjs.Polygon> {
|
||||
return this.getHitboxes(
|
||||
tag,
|
||||
0,
|
||||
0,
|
||||
this.getDimensionX() - 1,
|
||||
this.getDimensionY() - 1
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Iterable over the tile hitboxes of a given area and tag.
|
||||
*/
|
||||
class LayerCollisionMaskIterable implements Iterable<gdjs.Polygon> {
|
||||
layer: TransformedCollisionTileMapLayer;
|
||||
tag: string;
|
||||
xMin: integer;
|
||||
yMin: integer;
|
||||
xMax: integer;
|
||||
yMax: integer;
|
||||
|
||||
/**
|
||||
* Avoid to allocate an empty iterator each time
|
||||
* the iterable is initialized.
|
||||
*/
|
||||
static emptyItr: Iterator<gdjs.Polygon> = {
|
||||
next: () => ({ value: undefined, done: true }),
|
||||
};
|
||||
|
||||
/**
|
||||
* @param map The tile map.
|
||||
* @param tag The tile tag.
|
||||
* @param xMin The fist column to include.
|
||||
* @param yMin The fist row to include.
|
||||
* @param xMax The last column to include.
|
||||
* @param yMax The last row to include.
|
||||
*/
|
||||
constructor(
|
||||
layer: TransformedCollisionTileMapLayer,
|
||||
tag: string,
|
||||
xMin: integer,
|
||||
yMin: integer,
|
||||
xMax: integer,
|
||||
yMax: integer
|
||||
) {
|
||||
this.layer = layer;
|
||||
this.tag = tag;
|
||||
this.xMin = xMin;
|
||||
this.yMin = yMin;
|
||||
this.xMax = xMax;
|
||||
this.yMax = yMax;
|
||||
}
|
||||
|
||||
[Symbol.iterator]() {
|
||||
// Flatten the iterable of each tile into one.
|
||||
|
||||
// xMin and yMin next increment
|
||||
let x = this.xMax;
|
||||
let y = this.yMin - 1;
|
||||
let polygonItr: Iterator<gdjs.Polygon> =
|
||||
LayerCollisionMaskIterable.emptyItr;
|
||||
|
||||
return {
|
||||
next: () => {
|
||||
let listNext = polygonItr.next();
|
||||
while (listNext.done) {
|
||||
x++;
|
||||
if (x > this.xMax) {
|
||||
y++;
|
||||
x = this.xMin;
|
||||
}
|
||||
if (y > this.yMax) {
|
||||
// done
|
||||
return listNext;
|
||||
}
|
||||
const tile = this.layer.get(x, y);
|
||||
if (!tile) {
|
||||
continue;
|
||||
}
|
||||
const definition = tile.getDefinition();
|
||||
if (!definition) {
|
||||
continue;
|
||||
}
|
||||
if (definition.hasTag(this.tag)) {
|
||||
polygonItr = tile.getHitboxes()[Symbol.iterator]();
|
||||
listNext = polygonItr.next();
|
||||
}
|
||||
}
|
||||
return listNext;
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile transformed with an affine transformation.
|
||||
*/
|
||||
class TransformedCollisionTile {
|
||||
/**
|
||||
* The layer that contains this tile.
|
||||
*/
|
||||
readonly layer: TransformedCollisionTileMapLayer;
|
||||
/**
|
||||
* The column index in the layer.
|
||||
*/
|
||||
readonly x: integer;
|
||||
/**
|
||||
* The row index in the layer.
|
||||
*/
|
||||
readonly y: integer;
|
||||
private readonly hitBoxes: gdjs.Polygon[];
|
||||
private affineTransformationUpToDateCount: integer = 0;
|
||||
|
||||
/**
|
||||
* An reusable AffineTransformation to avoid allocations.
|
||||
*/
|
||||
private static readonly workingTransformation: gdjs.AffineTransformation = new gdjs.AffineTransformation();
|
||||
|
||||
/**
|
||||
*
|
||||
* @param layer The layer that contains this tile.
|
||||
* @param x The column index in the layer.
|
||||
* @param y The row index in the layer.
|
||||
*/
|
||||
constructor(
|
||||
layer: TransformedCollisionTileMapLayer,
|
||||
x: integer,
|
||||
y: integer
|
||||
) {
|
||||
this.layer = layer;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
const definition = this.getDefinition();
|
||||
this.hitBoxes = [];
|
||||
if (definition) {
|
||||
const tag = this.layer.tileMap.tag;
|
||||
const definitionHitboxes = definition.getHitBoxes(tag);
|
||||
if (definitionHitboxes) {
|
||||
this.hitBoxes.length = definitionHitboxes.length;
|
||||
for (
|
||||
let polygonIndex = 0;
|
||||
polygonIndex < this.hitBoxes.length;
|
||||
polygonIndex++
|
||||
) {
|
||||
const polygon = new gdjs.Polygon();
|
||||
this.hitBoxes[polygonIndex] = polygon;
|
||||
polygon.vertices.length = definitionHitboxes[polygonIndex].length;
|
||||
for (
|
||||
let vertexIndex = 0;
|
||||
vertexIndex < polygon.vertices.length;
|
||||
vertexIndex++
|
||||
) {
|
||||
polygon.vertices[vertexIndex] = [0, 0];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile definition from the tile set.
|
||||
*/
|
||||
getDefinition(): TileMapHelper.TileDefinition {
|
||||
return this.layer.tileMap.getTileDefinition(
|
||||
this.layer._source.get(this.x, this.y)!
|
||||
)!;
|
||||
}
|
||||
|
||||
private _isHitboxesUpToDate() {
|
||||
return (
|
||||
this.affineTransformationUpToDateCount ===
|
||||
this.layer.tileMap._transformationUpToDateCount
|
||||
);
|
||||
}
|
||||
|
||||
private _setHitboxesUpToDate() {
|
||||
this.affineTransformationUpToDateCount = this.layer.tileMap._transformationUpToDateCount;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The hitboxes of this tile in the scene basis.
|
||||
*/
|
||||
getHitboxes(): Polygon[] {
|
||||
if (this._isHitboxesUpToDate()) {
|
||||
return this.hitBoxes;
|
||||
}
|
||||
|
||||
const definition = this.getDefinition();
|
||||
if (!definition) {
|
||||
this._setHitboxesUpToDate();
|
||||
// It should already be []
|
||||
this.hitBoxes.length = 0;
|
||||
return this.hitBoxes;
|
||||
}
|
||||
const tag = this.layer.tileMap.tag;
|
||||
const definitionHitboxes = definition.getHitBoxes(tag);
|
||||
if (!definitionHitboxes) {
|
||||
this._setHitboxesUpToDate();
|
||||
// It should already be []
|
||||
this.hitBoxes.length = 0;
|
||||
return this.hitBoxes;
|
||||
}
|
||||
|
||||
const layerTransformation = this.layer.tileMap.getTransformation();
|
||||
const width = this.layer.tileMap.getTileWidth();
|
||||
const height = this.layer.tileMap.getTileHeight();
|
||||
|
||||
const tileTransformation =
|
||||
TransformedCollisionTile.workingTransformation;
|
||||
tileTransformation.setToTranslation(width * this.x, height * this.y);
|
||||
if (this.layer.isFlippedHorizontally(this.x, this.y)) {
|
||||
tileTransformation.flipX(width / 2);
|
||||
}
|
||||
if (this.layer.isFlippedVertically(this.x, this.y)) {
|
||||
tileTransformation.flipY(height / 2);
|
||||
}
|
||||
if (this.layer.isFlippedDiagonally(this.x, this.y)) {
|
||||
tileTransformation.flipDiagonally();
|
||||
}
|
||||
tileTransformation.preConcatenate(layerTransformation);
|
||||
|
||||
// The tile map can't change at runtime so the existing arrays can be
|
||||
// reused safely.
|
||||
for (
|
||||
let polygonIndex = 0;
|
||||
polygonIndex < this.hitBoxes.length;
|
||||
polygonIndex++
|
||||
) {
|
||||
const defPolygon = definitionHitboxes[polygonIndex];
|
||||
const polygon = this.hitBoxes[polygonIndex];
|
||||
|
||||
for (
|
||||
let vertexIndex = 0;
|
||||
vertexIndex < polygon.vertices.length;
|
||||
vertexIndex++
|
||||
) {
|
||||
const defVertex = defPolygon[vertexIndex];
|
||||
const vertex = polygon.vertices[vertexIndex];
|
||||
|
||||
tileTransformation.transform(defVertex, vertex);
|
||||
}
|
||||
}
|
||||
this._setHitboxesUpToDate();
|
||||
return this.hitBoxes;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
1
Extensions/TileMap/helper/README.md
Normal file
1
Extensions/TileMap/helper/README.md
Normal file
@@ -0,0 +1 @@
|
||||
This library sources are located in [SharedLibs/TileMapHelper/](../../../SharedLibs/TileMapHelper/).
|
23
Extensions/TileMap/helper/TileMapHelper.d.ts
vendored
Normal file
23
Extensions/TileMap/helper/TileMapHelper.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
import {
|
||||
EditableTileMap,
|
||||
EditableTileMapLayer,
|
||||
TileDefinition,
|
||||
TiledMap,
|
||||
TiledTileset,
|
||||
TileMapManager,
|
||||
TileTextureCache,
|
||||
PixiTileMapHelper,
|
||||
} from './dts/index';
|
||||
|
||||
declare global {
|
||||
namespace TileMapHelper {
|
||||
export { EditableTileMap };
|
||||
export { EditableTileMapLayer };
|
||||
export { TileDefinition };
|
||||
export { TiledMap };
|
||||
export { TiledTileset };
|
||||
export { TileMapManager };
|
||||
export { TileTextureCache };
|
||||
export { PixiTileMapHelper };
|
||||
}
|
||||
}
|
2
Extensions/TileMap/helper/TileMapHelper.js
Normal file
2
Extensions/TileMap/helper/TileMapHelper.js
Normal file
File diff suppressed because one or more lines are too long
1
Extensions/TileMap/helper/TileMapHelper.js.map
Normal file
1
Extensions/TileMap/helper/TileMapHelper.js.map
Normal file
File diff suppressed because one or more lines are too long
23
Extensions/TileMap/helper/dts/index.d.ts
vendored
Normal file
23
Extensions/TileMap/helper/dts/index.d.ts
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
/**
|
||||
* @packageDocumentation
|
||||
* @module TileMapHelper
|
||||
*/
|
||||
import { TiledMap, TiledTileset } from './tiled/TiledFormat';
|
||||
import {
|
||||
EditableTileMap,
|
||||
EditableTileMapLayer,
|
||||
TileDefinition,
|
||||
} from './model/TileMapModel';
|
||||
import { TileMapManager } from './render/TileMapManager';
|
||||
import { TileTextureCache } from './render/TileTextureCache';
|
||||
import { PixiTileMapHelper } from './render/TileMapPixiHelper';
|
||||
export * from './model/CommonTypes';
|
||||
export { EditableTileMap };
|
||||
export { EditableTileMapLayer };
|
||||
export { TileDefinition };
|
||||
export { TiledMap };
|
||||
export { TiledTileset };
|
||||
export { TileMapManager };
|
||||
export { TileTextureCache };
|
||||
export { PixiTileMapHelper };
|
||||
//# sourceMappingURL=index.d.ts.map
|
1
Extensions/TileMap/helper/dts/index.d.ts.map
Normal file
1
Extensions/TileMap/helper/dts/index.d.ts.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,QAAQ,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAC7D,OAAO,EACL,eAAe,EACf,oBAAoB,EACpB,cAAc,EACf,MAAM,sBAAsB,CAAC;AAE9B,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AACzD,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,4BAA4B,CAAC;AAE/D,cAAc,qBAAqB,CAAC;AAEpC,OAAO,EAAE,eAAe,EAAE,CAAC;AAC3B,OAAO,EAAE,oBAAoB,EAAE,CAAC;AAChC,OAAO,EAAE,cAAc,EAAE,CAAC;AAE1B,OAAO,EAAE,QAAQ,EAAE,CAAC;AACpB,OAAO,EAAE,YAAY,EAAE,CAAC;AAExB,OAAO,EAAE,cAAc,EAAE,CAAC;AAC1B,OAAO,EAAE,gBAAgB,EAAE,CAAC;AAC5B,OAAO,EAAE,iBAAiB,EAAE,CAAC"}
|
5
Extensions/TileMap/helper/dts/model/CommonTypes.d.ts
vendored
Normal file
5
Extensions/TileMap/helper/dts/model/CommonTypes.d.ts
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
export declare type integer = number;
|
||||
export declare type float = number;
|
||||
export declare type FloatPoint = [float, float];
|
||||
export declare type PolygonVertices = FloatPoint[];
|
||||
//# sourceMappingURL=CommonTypes.d.ts.map
|
1
Extensions/TileMap/helper/dts/model/CommonTypes.d.ts.map
Normal file
1
Extensions/TileMap/helper/dts/model/CommonTypes.d.ts.map
Normal file
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"CommonTypes.d.ts","sourceRoot":"","sources":["../../src/model/CommonTypes.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,OAAO,MAAM,OAAO,GAAG,MAAM,CAAC;AACrC,MAAM,CAAC,OAAO,MAAM,KAAK,GAAG,MAAM,CAAC;AACnC,oBAAY,UAAU,GAAG,CAAC,KAAK,EAAE,KAAK,CAAC,CAAC;AAExC,oBAAY,eAAe,GAAG,UAAU,EAAE,CAAC"}
|
315
Extensions/TileMap/helper/dts/model/TileMapModel.d.ts
vendored
Normal file
315
Extensions/TileMap/helper/dts/model/TileMapModel.d.ts
vendored
Normal file
@@ -0,0 +1,315 @@
|
||||
import { PolygonVertices, integer, float } from './CommonTypes';
|
||||
/**
|
||||
* A tile map model.
|
||||
*
|
||||
* Tile map files are parsed into this model by {@link TiledTileMapLoader}.
|
||||
* This model is used for rending ({@link TileMapRuntimeObjectPixiRenderer})
|
||||
* and hitboxes handling ({@link TransformedCollisionTileMap}).
|
||||
* This allows to support new file format with only a new parser.
|
||||
*/
|
||||
export declare class EditableTileMap {
|
||||
private _tileSet;
|
||||
private _layers;
|
||||
/**
|
||||
* The width of a tile.
|
||||
*/
|
||||
private readonly tileWidth;
|
||||
/**
|
||||
* The height of a tile.
|
||||
*/
|
||||
private readonly tileHeight;
|
||||
/**
|
||||
* The number of tile columns in the map.
|
||||
*/
|
||||
private readonly dimX;
|
||||
/**
|
||||
* The number of tile rows in the map.
|
||||
*/
|
||||
private readonly dimY;
|
||||
/**
|
||||
* @param tileWidth The width of a tile.
|
||||
* @param tileHeight The height of a tile.
|
||||
* @param dimX The number of tile columns in the map.
|
||||
* @param dimY The number of tile rows in the map.
|
||||
* @param tileSet The tile set.
|
||||
*/
|
||||
constructor(
|
||||
tileWidth: integer,
|
||||
tileHeight: integer,
|
||||
dimX: integer,
|
||||
dimY: integer,
|
||||
tileSet: Map<integer, TileDefinition>
|
||||
);
|
||||
/**
|
||||
* @returns The tile map width in pixels.
|
||||
*/
|
||||
getWidth(): integer;
|
||||
/**
|
||||
* @returns The tile map height in pixels.
|
||||
*/
|
||||
getHeight(): integer;
|
||||
/**
|
||||
* @returns The tile width in pixels.
|
||||
*/
|
||||
getTileHeight(): integer;
|
||||
/**
|
||||
* @returns The tile height in pixels.
|
||||
*/
|
||||
getTileWidth(): integer;
|
||||
/**
|
||||
* @returns The number of tile columns in the map.
|
||||
*/
|
||||
getDimensionX(): integer;
|
||||
/**
|
||||
* @returns The number of tile rows in the map.
|
||||
*/
|
||||
getDimensionY(): integer;
|
||||
/**
|
||||
* @param tileId The tile identifier
|
||||
* @returns The tile definition form the tile set.
|
||||
*/
|
||||
getTileDefinition(tileId: integer): TileDefinition | undefined;
|
||||
/**
|
||||
* @returns All the tile definitions form the tile set.
|
||||
*/
|
||||
getTileDefinitions(): Iterable<TileDefinition>;
|
||||
/**
|
||||
* @param id The identifier of the new layer.
|
||||
* @returns The new layer.
|
||||
*/
|
||||
addTileLayer(id: integer): EditableTileMapLayer;
|
||||
/**
|
||||
* @param id The identifier of the new layer.
|
||||
* @returns The new layer.
|
||||
*/
|
||||
addObjectLayer(id: integer): EditableObjectLayer;
|
||||
/**
|
||||
* @returns All the layers of the tile map.
|
||||
*/
|
||||
getLayers(): Iterable<AbstractEditableLayer>;
|
||||
/**
|
||||
* Check if a point is inside a tile with a given tag.
|
||||
*
|
||||
* It doesn't use the tile hitboxes.
|
||||
* It only check the point is inside the tile square.
|
||||
*
|
||||
* @param x The X coordinate of the point to check.
|
||||
* @param y The Y coordinate of the point to check.
|
||||
* @param tag The tile tag
|
||||
* @returns true when the point is inside a tile with a given tag.
|
||||
*/
|
||||
pointIsInsideTile(x: float, y: float, tag: string): boolean;
|
||||
}
|
||||
/**
|
||||
* A tile map layer.
|
||||
*/
|
||||
declare abstract class AbstractEditableLayer {
|
||||
/**
|
||||
* The layer tile map.
|
||||
*/
|
||||
readonly tileMap: EditableTileMap;
|
||||
/**
|
||||
* The layer identifier.
|
||||
*/
|
||||
readonly id: integer;
|
||||
private visible;
|
||||
/**
|
||||
* @param tileMap The layer tile map.
|
||||
* @param id The layer identifier.
|
||||
*/
|
||||
constructor(tileMap: EditableTileMap, id: integer);
|
||||
setVisible(visible: boolean): void;
|
||||
/**
|
||||
* @returns true if the layer is visible.
|
||||
*/
|
||||
isVisible(): boolean;
|
||||
}
|
||||
/**
|
||||
* A layer where tiles are placed with pixel coordinates.
|
||||
*/
|
||||
export declare class EditableObjectLayer extends AbstractEditableLayer {
|
||||
readonly objects: TileObject[];
|
||||
/**
|
||||
* @param tileMap The layer tile map.
|
||||
* @param id The layer identifier.
|
||||
*/
|
||||
constructor(tileMap: EditableTileMap, id: integer);
|
||||
add(object: TileObject): void;
|
||||
}
|
||||
/**
|
||||
* A tile that is placed with pixel coordinates.
|
||||
*/
|
||||
export declare class TileObject {
|
||||
/**
|
||||
* The tile identifier in the tile set.
|
||||
*/
|
||||
private tileId;
|
||||
/**
|
||||
* The coordinate of the tile left side.
|
||||
*/
|
||||
readonly x: float;
|
||||
/**
|
||||
* The coordinate of the tile top side.
|
||||
*/
|
||||
readonly y: float;
|
||||
/**
|
||||
* @param x The coordinate of the tile left side.
|
||||
* @param y The coordinate of the tile top side.
|
||||
* @param tileId The tile identifier in the tile set.
|
||||
*/
|
||||
constructor(x: float, y: float, tileId: integer);
|
||||
/**
|
||||
* @return The tile identifier in the tile set.
|
||||
*/
|
||||
getTileId(): integer;
|
||||
setFlippedHorizontally(flippedHorizontally: boolean): void;
|
||||
setFlippedVertically(flippedVertically: boolean): void;
|
||||
setFlippedDiagonally(flippedDiagonally: boolean): void;
|
||||
/**
|
||||
* @returns true if the tile is flipped horizontally.
|
||||
*/
|
||||
isFlippedHorizontally(): boolean;
|
||||
/**
|
||||
* @returns true if the tile is flipped vertically.
|
||||
*/
|
||||
isFlippedVertically(): boolean;
|
||||
/**
|
||||
* @returns true if the tile is flipped diagonally.
|
||||
*/
|
||||
isFlippedDiagonally(): boolean;
|
||||
}
|
||||
/**
|
||||
* A tile map layer with tile organized in grid.
|
||||
*/
|
||||
export declare class EditableTileMapLayer extends AbstractEditableLayer {
|
||||
private readonly _tiles;
|
||||
/**
|
||||
* @param tileMap The layer tile map.
|
||||
* @param id The layer identifier.
|
||||
*/
|
||||
constructor(tileMap: EditableTileMap, id: integer);
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param tileId The tile identifier in the tile set.
|
||||
*/
|
||||
setTile(x: integer, y: integer, tileId: integer): void;
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
*/
|
||||
removeTile(x: integer, y: integer): void;
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param flippedHorizontally true if the tile is flipped horizontally.
|
||||
*/
|
||||
setFlippedHorizontally(
|
||||
x: integer,
|
||||
y: integer,
|
||||
flippedHorizontally: boolean
|
||||
): void;
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param flippedVertically true if the tile is flipped vertically.
|
||||
*/
|
||||
setFlippedVertically(
|
||||
x: integer,
|
||||
y: integer,
|
||||
flippedVertically: boolean
|
||||
): void;
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param flippedDiagonally true if the tile is flipped diagonally.
|
||||
*/
|
||||
setFlippedDiagonally(
|
||||
x: integer,
|
||||
y: integer,
|
||||
flippedDiagonally: boolean
|
||||
): void;
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped horizontally.
|
||||
*/
|
||||
isFlippedHorizontally(x: integer, y: integer): boolean;
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped vertically.
|
||||
*/
|
||||
isFlippedVertically(x: integer, y: integer): boolean;
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped diagonally.
|
||||
*/
|
||||
isFlippedDiagonally(x: integer, y: integer): boolean;
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns The tile identifier from the tile set.
|
||||
*/
|
||||
get(x: integer, y: integer): integer | undefined;
|
||||
/**
|
||||
* The number of tile columns in the layer.
|
||||
*/
|
||||
getDimensionX(): integer;
|
||||
/**
|
||||
* The number of tile rows in the layer.
|
||||
*/
|
||||
getDimensionY(): integer;
|
||||
/**
|
||||
* @returns The layer width in pixels.
|
||||
*/
|
||||
getWidth(): integer;
|
||||
/**
|
||||
* @returns The layer height in pixels.
|
||||
*/
|
||||
getHeight(): integer;
|
||||
}
|
||||
/**
|
||||
* A tile definition from the tile set.
|
||||
*/
|
||||
export declare class TileDefinition {
|
||||
/**
|
||||
* There will probably be at most 4 tags on a tile.
|
||||
* An array lookup should take less time than using a Map.
|
||||
*/
|
||||
private readonly taggedHitBoxes;
|
||||
private readonly animationLength;
|
||||
/**
|
||||
* @param animationLength The number of frame in the tile animation.
|
||||
*/
|
||||
constructor(animationLength: integer);
|
||||
/**
|
||||
* Add a polygon for the collision layer
|
||||
* @param tag The tag to allow collision layer filtering.
|
||||
* @param polygon The polygon to use for collisions.
|
||||
*/
|
||||
add(tag: string, polygon: PolygonVertices): void;
|
||||
/**
|
||||
* This property is used by {@link TransformedCollisionTileMap}
|
||||
* to make collision classes.
|
||||
* @param tag The tag to allow collision layer filtering.
|
||||
* @returns true if this tile contains any polygon with the given tag.
|
||||
*/
|
||||
hasTag(tag: string): boolean;
|
||||
/**
|
||||
* The hitboxes positioning is done by {@link TransformedCollisionTileMap}.
|
||||
* @param tag The tag to allow collision layer filtering.
|
||||
* @returns The hit boxes for this tile.
|
||||
*/
|
||||
getHitBoxes(tag: string): PolygonVertices[] | undefined;
|
||||
/**
|
||||
* Animated tiles have a limitation:
|
||||
* they are only able to use frames arranged horizontally one next
|
||||
* to each other on the atlas.
|
||||
* @returns The number of frame in the tile animation.
|
||||
*/
|
||||
getAnimationLength(): integer;
|
||||
}
|
||||
export {};
|
||||
//# sourceMappingURL=TileMapModel.d.ts.map
|
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TileMapModel.d.ts","sourceRoot":"","sources":["../../src/model/TileMapModel.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,eAAe,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,eAAe,CAAC;AAEhE;;;;;;;GAOG;AACH,qBAAa,eAAe;IAC1B,OAAO,CAAC,QAAQ,CAA+B;IAC/C,OAAO,CAAC,OAAO,CAA+B;IAC9C;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,SAAS,CAAU;IACpC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,UAAU,CAAU;IACrC;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAC/B;;OAEG;IACH,OAAO,CAAC,QAAQ,CAAC,IAAI,CAAU;IAE/B;;;;;;OAMG;gBAED,SAAS,EAAE,OAAO,EAClB,UAAU,EAAE,OAAO,EACnB,IAAI,EAAE,OAAO,EACb,IAAI,EAAE,OAAO,EAGb,OAAO,EAAE,GAAG,CAAC,OAAO,EAAE,cAAc,CAAC;IAUvC;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,YAAY,IAAI,OAAO;IAIvB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;;OAGG;IACH,iBAAiB,CAAC,MAAM,EAAE,OAAO,GAAG,cAAc,GAAG,SAAS;IAI9D;;OAEG;IACH,kBAAkB,IAAI,QAAQ,CAAC,cAAc,CAAC;IAI9C;;;OAGG;IACH,YAAY,CAAC,EAAE,EAAE,OAAO,GAAG,oBAAoB;IAM/C;;;OAGG;IACH,cAAc,CAAC,EAAE,EAAE,OAAO,GAAG,mBAAmB;IAMhD;;OAEG;IACH,SAAS,IAAI,QAAQ,CAAC,qBAAqB,CAAC;IAI5C;;;;;;;;;;OAUG;IACH,iBAAiB,CAAC,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,GAAG,EAAE,MAAM,GAAG,OAAO;CAmB5D;AAED;;GAEG;AACH,uBAAe,qBAAqB;IAClC;;OAEG;IACH,QAAQ,CAAC,OAAO,EAAE,eAAe,CAAC;IAClC;;OAEG;IACH,QAAQ,CAAC,EAAE,EAAE,OAAO,CAAC;IACrB,OAAO,CAAC,OAAO,CAAiB;IAEhC;;;OAGG;gBACS,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,OAAO;IAKjD,UAAU,CAAC,OAAO,EAAE,OAAO,GAAG,IAAI;IAIlC;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB;AAED;;GAEG;AACH,qBAAa,mBAAoB,SAAQ,qBAAqB;IAC5D,QAAQ,CAAC,OAAO,EAAE,UAAU,EAAE,CAAC;IAE/B;;;OAGG;gBACS,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,OAAO;IAKjD,GAAG,CAAC,MAAM,EAAE,UAAU,GAAG,IAAI;CAG9B;AAED;;GAEG;AACH,qBAAa,UAAU;IACrB;;OAEG;IACH,OAAO,CAAC,MAAM,CAAU;IACxB;;OAEG;IACH,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC;IAClB;;OAEG;IACH,QAAQ,CAAC,CAAC,EAAE,KAAK,CAAC;IAElB;;;;OAIG;gBACS,CAAC,EAAE,KAAK,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,EAAE,OAAO;IAM/C;;OAEG;IACH,SAAS,IAAI,OAAO;IAIpB,sBAAsB,CAAC,mBAAmB,EAAE,OAAO,GAAG,IAAI;IAO1D,oBAAoB,CAAC,iBAAiB,EAAE,OAAO,GAAG,IAAI;IAOtD,oBAAoB,CAAC,iBAAiB,EAAE,OAAO,GAAG,IAAI;IAOtD;;OAEG;IACH,qBAAqB,IAAI,OAAO;IAIhC;;OAEG;IACH,mBAAmB,IAAI,OAAO;IAI9B;;OAEG;IACH,mBAAmB,IAAI,OAAO;CAG/B;AAiED;;GAEG;AACH,qBAAa,oBAAqB,SAAQ,qBAAqB;IAC7D,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAoB;IAE3C;;;OAGG;gBACS,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,OAAO;IASjD;;;;OAIG;IACH,OAAO,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,EAAE,MAAM,EAAE,OAAO,GAAG,IAAI;IAUtD;;;OAGG;IACH,UAAU,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,IAAI;IAKxC;;;;OAIG;IACH,sBAAsB,CACpB,CAAC,EAAE,OAAO,EACV,CAAC,EAAE,OAAO,EACV,mBAAmB,EAAE,OAAO,GAC3B,IAAI;IAWP;;;;OAIG;IACH,oBAAoB,CAClB,CAAC,EAAE,OAAO,EACV,CAAC,EAAE,OAAO,EACV,iBAAiB,EAAE,OAAO,GACzB,IAAI;IAWP;;;;OAIG;IACH,oBAAoB,CAClB,CAAC,EAAE,OAAO,EACV,CAAC,EAAE,OAAO,EACV,iBAAiB,EAAE,OAAO,GACzB,IAAI;IAWP;;;;OAIG;IACH,qBAAqB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAItD;;;;OAIG;IACH,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIpD;;;;OAIG;IACH,mBAAmB,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO;IAIpD;;;;OAIG;IACH,GAAG,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,EAAE,OAAO,GAAG,OAAO,GAAG,SAAS;IAUhD;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,aAAa,IAAI,OAAO;IAIxB;;OAEG;IACH,QAAQ,IAAI,OAAO;IAInB;;OAEG;IACH,SAAS,IAAI,OAAO;CAGrB;AAED;;GAEG;AACH,qBAAa,cAAc;IACzB;;;OAGG;IACH,OAAO,CAAC,QAAQ,CAAC,cAAc,CAG3B;IACJ,OAAO,CAAC,QAAQ,CAAC,eAAe,CAAU;IAE1C;;OAEG;gBACS,eAAe,EAAE,OAAO;IAKpC;;;;OAIG;IACH,GAAG,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,EAAE,eAAe,GAAG,IAAI;IAShD;;;;;OAKG;IACH,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,OAAO;IAI5B;;;;OAIG;IACH,WAAW,CAAC,GAAG,EAAE,MAAM,GAAG,eAAe,EAAE,GAAG,SAAS;IAOvD;;;;;OAKG;IACH,kBAAkB,IAAI,OAAO;CAG9B"}
|
27
Extensions/TileMap/helper/dts/render/ResourceCache.d.ts
vendored
Normal file
27
Extensions/TileMap/helper/dts/render/ResourceCache.d.ts
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
/**
|
||||
* A cache of resources identified by a string.
|
||||
*
|
||||
* It ensures that a resource is never load twice.
|
||||
*/
|
||||
export declare class ResourceCache<T> {
|
||||
private _cachedValues;
|
||||
/**
|
||||
* Several calls can happen before the resource is loaded.
|
||||
* This allows to stack them.
|
||||
*/
|
||||
private _callbacks;
|
||||
constructor();
|
||||
/**
|
||||
* Return a resource through a call back.
|
||||
* @param key the resource identifier.
|
||||
* @param load load the resource in case of cache default.
|
||||
* Note that the load callback is used by `getOrLoad` and not by the caller.
|
||||
* @param callback called when the resource is ready.
|
||||
*/
|
||||
getOrLoad(
|
||||
key: string,
|
||||
load: (callback: (value: T | null) => void) => void,
|
||||
callback: (value: T | null) => void
|
||||
): void;
|
||||
}
|
||||
//# sourceMappingURL=ResourceCache.d.ts.map
|
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"ResourceCache.d.ts","sourceRoot":"","sources":["../../src/render/ResourceCache.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AACH,qBAAa,aAAa,CAAC,CAAC;IAC1B,OAAO,CAAC,aAAa,CAAiB;IAEtC;;;OAGG;IACH,OAAO,CAAC,UAAU,CAAgD;;IAOlE;;;;;;OAMG;IACH,SAAS,CACP,GAAG,EAAE,MAAM,EACX,IAAI,EAAE,CAAC,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,KAAK,IAAI,EACnD,QAAQ,EAAE,CAAC,KAAK,EAAE,CAAC,GAAG,IAAI,KAAK,IAAI,GAClC,IAAI;CA+BR"}
|
61
Extensions/TileMap/helper/dts/render/TileMapManager.d.ts
vendored
Normal file
61
Extensions/TileMap/helper/dts/render/TileMapManager.d.ts
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
import { TiledMap } from '../tiled/TiledFormat';
|
||||
import { EditableTileMap } from '../model/TileMapModel';
|
||||
import { TileTextureCache } from './TileTextureCache';
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
/**
|
||||
* A holder to share tile maps across the 2 extension objects.
|
||||
*
|
||||
* Every instance with the same files path in properties will
|
||||
* share the same {@link EditableTileMap} and {@link TileTextureCache}.
|
||||
*
|
||||
* @see {@link TileMapRuntimeManager}
|
||||
*/
|
||||
export declare class TileMapManager {
|
||||
private _tileMapCache;
|
||||
private _textureCacheCaches;
|
||||
constructor();
|
||||
/**
|
||||
* @param instanceHolder Where to set the manager instance.
|
||||
* @returns The shared manager.
|
||||
*/
|
||||
static getManager(instanceHolder: Object): TileMapManager;
|
||||
/**
|
||||
* @param loadTiledMap The method that loads the Tiled JSON file in memory.
|
||||
* @param tileMapJsonResourceName The resource name of the tile map.
|
||||
* @param tileSetJsonResourceName The resource name of the tile set.
|
||||
* @param pako The zlib library.
|
||||
* @param callback A function called when the tile map is parsed.
|
||||
*/
|
||||
getOrLoadTileMap(
|
||||
loadTiledMap: (
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (tiledMap: TiledMap | null) => void
|
||||
) => void,
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
pako: any,
|
||||
callback: (tileMap: EditableTileMap | null) => void
|
||||
): void;
|
||||
/**
|
||||
* @param loadTiledMap The method that loads the Tiled JSON file in memory.
|
||||
* @param getTexture The method that loads the atlas image file in memory.
|
||||
* @param atlasImageResourceName The resource name of the atlas image.
|
||||
* @param tileMapJsonResourceName The resource name of the tile map.
|
||||
* @param tileSetJsonResourceName The resource name of the tile set.
|
||||
* @param callback A function called when the tiles textures are split.
|
||||
*/
|
||||
getOrLoadTextureCache(
|
||||
loadTiledMap: (
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (tiledMap: TiledMap | null) => void
|
||||
) => void,
|
||||
getTexture: (textureName: string) => PIXI.BaseTexture<PIXI.Resource>,
|
||||
atlasImageResourceName: string,
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (textureCache: TileTextureCache | null) => void
|
||||
): void;
|
||||
}
|
||||
//# sourceMappingURL=TileMapManager.d.ts.map
|
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TileMapManager.d.ts","sourceRoot":"","sources":["../../src/render/TileMapManager.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAEhD,OAAO,EAAE,eAAe,EAAE,MAAM,uBAAuB,CAAC;AACxD,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAGtD,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAEpC;;;;;;;GAOG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,aAAa,CAAiC;IACtD,OAAO,CAAC,mBAAmB,CAAkC;;IAO7D;;;OAGG;IACH,MAAM,CAAC,UAAU,CAAC,cAAc,EAAE,MAAM,GAAG,cAAc;IAWzD;;;;;;OAMG;IACH,gBAAgB,CACd,YAAY,EAAE,CACZ,uBAAuB,EAAE,MAAM,EAC/B,uBAAuB,EAAE,MAAM,EAC/B,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,KAC1C,IAAI,EACT,uBAAuB,EAAE,MAAM,EAC/B,uBAAuB,EAAE,MAAM,EAC/B,IAAI,EAAE,GAAG,EACT,QAAQ,EAAE,CAAC,OAAO,EAAE,eAAe,GAAG,IAAI,KAAK,IAAI,GAClD,IAAI;IAwBP;;;;;;;OAOG;IACH,qBAAqB,CACnB,YAAY,EAAE,CACZ,uBAAuB,EAAE,MAAM,EAC/B,uBAAuB,EAAE,MAAM,EAC/B,QAAQ,EAAE,CAAC,QAAQ,EAAE,QAAQ,GAAG,IAAI,KAAK,IAAI,KAC1C,IAAI,EACT,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,EACpE,sBAAsB,EAAE,MAAM,EAC9B,uBAAuB,EAAE,MAAM,EAC/B,uBAAuB,EAAE,MAAM,EAC/B,QAAQ,EAAE,CAAC,YAAY,EAAE,gBAAgB,GAAG,IAAI,KAAK,IAAI,GACxD,IAAI;CAoCR"}
|
54
Extensions/TileMap/helper/dts/render/TileMapPixiHelper.d.ts
vendored
Normal file
54
Extensions/TileMap/helper/dts/render/TileMapPixiHelper.d.ts
vendored
Normal file
@@ -0,0 +1,54 @@
|
||||
import { integer, float } from '../model/CommonTypes';
|
||||
import { TiledMap } from '../tiled/TiledFormat';
|
||||
import { EditableTileMap } from '../model/TileMapModel';
|
||||
import { TileTextureCache } from './TileTextureCache';
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
export declare class PixiTileMapHelper {
|
||||
/**
|
||||
* Split an atlas image into Pixi textures.
|
||||
*
|
||||
* @param tiledMap A tile map exported from Tiled.
|
||||
* @param atlasTexture The texture containing the whole tile set.
|
||||
* @param getTexture A getter to load a texture. Used if atlasTexture is not specified.
|
||||
* @returns A textures cache.
|
||||
*/
|
||||
static parseAtlas(
|
||||
tiledMap: TiledMap,
|
||||
atlasTexture: PIXI.BaseTexture<PIXI.Resource> | null,
|
||||
getTexture: (textureName: string) => PIXI.BaseTexture<PIXI.Resource>
|
||||
): TileTextureCache | null;
|
||||
/**
|
||||
* Re-renders the tile map whenever its rendering settings have been changed
|
||||
*
|
||||
* @param pixiTileMap the tile map renderer
|
||||
* @param tileMap the tile map model
|
||||
* @param textureCache the tile set textures
|
||||
* @param displayMode What to display:
|
||||
* - only a single layer (`index`)
|
||||
* - only visible layers (`visible`)
|
||||
* - everything (`all`).
|
||||
* @param layerIndex If `displayMode` is set to `index`, the layer index to be
|
||||
* displayed.
|
||||
*/
|
||||
static updatePixiTileMap(
|
||||
untypedPixiTileMap: any,
|
||||
tileMap: EditableTileMap,
|
||||
textureCache: TileTextureCache,
|
||||
displayMode: 'index' | 'visible' | 'all',
|
||||
layerIndex: number
|
||||
): void;
|
||||
/**
|
||||
* Re-renders the collision mask
|
||||
*/
|
||||
static updatePixiCollisionMask(
|
||||
pixiGraphics: PIXI.Graphics,
|
||||
tileMap: EditableTileMap,
|
||||
typeFilter: string,
|
||||
outlineSize: integer,
|
||||
outlineColor: integer,
|
||||
outlineOpacity: float,
|
||||
fillColor: integer,
|
||||
fillOpacity: float
|
||||
): void;
|
||||
}
|
||||
//# sourceMappingURL=TileMapPixiHelper.d.ts.map
|
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TileMapPixiHelper.d.ts","sourceRoot":"","sources":["../../src/render/TileMapPixiHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAC;AACtD,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EAEL,eAAe,EAEhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,gBAAgB,EAAE,MAAM,oBAAoB,CAAC;AAEtD,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAGpC,qBAAa,iBAAiB;IAC5B;;;;;;;OAOG;IACH,MAAM,CAAC,UAAU,CACf,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GAAG,IAAI,EACpD,UAAU,EAAE,CAAC,WAAW,EAAE,MAAM,KAAK,IAAI,CAAC,WAAW,CAAC,IAAI,CAAC,QAAQ,CAAC,GACnE,gBAAgB,GAAG,IAAI;IA+E1B;;;;;;;;;;;;OAYG;IACH,MAAM,CAAC,iBAAiB,CACtB,kBAAkB,EAAE,GAAG,EACvB,OAAO,EAAE,eAAe,EACxB,YAAY,EAAE,gBAAgB,EAC9B,WAAW,EAAE,OAAO,GAAG,SAAS,GAAG,KAAK,EACxC,UAAU,EAAE,MAAM,GACjB,IAAI;IA4EP;;OAEG;IACH,MAAM,CAAC,uBAAuB,CAC5B,YAAY,EAAE,IAAI,CAAC,QAAQ,EAC3B,OAAO,EAAE,eAAe,EACxB,UAAU,EAAE,MAAM,EAClB,WAAW,EAAE,OAAO,EACpB,YAAY,EAAE,OAAO,EACrB,cAAc,EAAE,KAAK,EACrB,SAAS,EAAE,OAAO,EAClB,WAAW,EAAE,KAAK,GACjB,IAAI;CA6DR"}
|
44
Extensions/TileMap/helper/dts/render/TileTextureCache.d.ts
vendored
Normal file
44
Extensions/TileMap/helper/dts/render/TileTextureCache.d.ts
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
import { integer } from '../model/CommonTypes';
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
/**
|
||||
* A cache to access the tile images.
|
||||
*
|
||||
* It's created by {@link PixiTileMapHelper.parseAtlas}
|
||||
* and used by {@link PixiTileMapHelper.updatePixiTileMap}.
|
||||
*/
|
||||
export declare class TileTextureCache {
|
||||
private static readonly flippedHorizontallyFlag;
|
||||
private static readonly flippedVerticallyFlag;
|
||||
private static readonly flippedDiagonallyFlag;
|
||||
private readonly _textures;
|
||||
constructor();
|
||||
setTexture(
|
||||
tileId: integer,
|
||||
flippedHorizontally: boolean,
|
||||
flippedVertically: boolean,
|
||||
flippedDiagonally: boolean,
|
||||
texture: PIXI.Texture
|
||||
): void;
|
||||
/**
|
||||
* Return the texture to use for the tile with the specified uid, which can contains
|
||||
* information about rotation in bits 32, 31 and 30
|
||||
* (see https://doc.mapeditor.org/en/stable/reference/tmx-map-format/).
|
||||
*
|
||||
* @param tileId The tile identifier
|
||||
* @param flippedHorizontally true if the tile is flipped horizontally.
|
||||
* @param flippedVertically true if the tile is flipped vertically.
|
||||
* @param flippedDiagonally true if the tile is flipped diagonally.
|
||||
* @returns The texture for the given tile identifier and orientation.
|
||||
*/
|
||||
findTileTexture(
|
||||
tileId: integer,
|
||||
flippedHorizontally: boolean,
|
||||
flippedVertically: boolean,
|
||||
flippedDiagonally: boolean
|
||||
): PIXI.Texture | undefined;
|
||||
/**
|
||||
* @return the Tiled tile global uniq identifier.
|
||||
*/
|
||||
private _getGlobalId;
|
||||
}
|
||||
//# sourceMappingURL=TileTextureCache.d.ts.map
|
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TileTextureCache.d.ts","sourceRoot":"","sources":["../../src/render/TileTextureCache.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAE/C,OAAO,IAAI,GAAG,gBAAgB,CAAC,IAAI,CAAC;AAEpC;;;;;GAKG;AACH,qBAAa,gBAAgB;IAC3B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,uBAAuB,CAAc;IAC7D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAC3D,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,qBAAqB,CAAc;IAE3D,OAAO,CAAC,QAAQ,CAAC,SAAS,CAA6B;;IAMvD,UAAU,CACR,MAAM,EAAE,OAAO,EACf,mBAAmB,EAAE,OAAO,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,EAC1B,OAAO,EAAE,IAAI,CAAC,OAAO,GACpB,IAAI;IAUP;;;;;;;;;;OAUG;IACH,eAAe,CACb,MAAM,EAAE,OAAO,EACf,mBAAmB,EAAE,OAAO,EAC5B,iBAAiB,EAAE,OAAO,EAC1B,iBAAiB,EAAE,OAAO,GACzB,IAAI,CAAC,OAAO,GAAG,SAAS;IA8D3B;;OAEG;IACH,OAAO,CAAC,YAAY;CAkBrB"}
|
339
Extensions/TileMap/helper/dts/tiled/Tiled.d.ts
vendored
Normal file
339
Extensions/TileMap/helper/dts/tiled/Tiled.d.ts
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
import { float, integer } from '../model/CommonTypes';
|
||||
/**
|
||||
* Tiled JSON format.
|
||||
*/
|
||||
export declare type TiledMap = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
|
||||
backgroundcolor?: string;
|
||||
/** The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) */
|
||||
compressionlevel: integer;
|
||||
/** Number of tile rows */
|
||||
height: integer;
|
||||
/** Length of the side of a hex tile in pixels (hexagonal maps only) */
|
||||
hexsidelength?: integer;
|
||||
/** Whether the map has infinite dimensions */
|
||||
infinite: boolean;
|
||||
/** Array of {@link TiledLayer} */
|
||||
layers: Array<TiledLayer>;
|
||||
/** Auto-increments for each layer */
|
||||
nextlayerid: integer;
|
||||
/** Auto-increments for each placed object */
|
||||
nextobjectid: integer;
|
||||
/** `orthogonal`, `isometric`, `staggered` or `hexagonal` */
|
||||
orientation: string;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** `right-down` (the default), `right-up`, `left-down` or `left-up` (currently only supported for orthogonal maps) */
|
||||
renderorder: string;
|
||||
/** `x` or `y` (staggered / hexagonal maps only) */
|
||||
staggeraxis?: string;
|
||||
/** `odd` or `even` (staggered / hexagonal maps only) */
|
||||
staggerindex?: string;
|
||||
/** The Tiled version used to save the file */
|
||||
tiledversion: string;
|
||||
/** Map grid height */
|
||||
tileheight: integer;
|
||||
/** Array of {@link TiledTileset} */
|
||||
tilesets: Array<TiledTileset>;
|
||||
/** Map grid width */
|
||||
tilewidth: integer;
|
||||
/** `map` (since 1.0) */
|
||||
type: string;
|
||||
/** The JSON format version (previously a number, saved as string since 1.6) */
|
||||
version: string;
|
||||
/** Number of tile columns */
|
||||
width: integer;
|
||||
};
|
||||
export declare type TiledLayer = {
|
||||
/** Array of {@link TiledChunk} (optional). `tilelayer` only. */
|
||||
chunks?: Array<TiledChunk>;
|
||||
/** `zlib`, `gzip`, `zstd` (since Tiled 1.3) or empty (default). `tilelayer` only. */
|
||||
compression?: string;
|
||||
/** Array of `unsigned`, `integer` (GIDs) or base64-encoded data. `tilelayer` only.*/
|
||||
data?: Array<integer> | string;
|
||||
/** `topdown` (default) or `index`. `objectgroup` only. */
|
||||
draworder?: string;
|
||||
/** `csv` (default) or `base64`. `tilelayer` only. */
|
||||
encoding?: string;
|
||||
/** Row count. Same as map height for fixed-size maps. */
|
||||
height?: integer;
|
||||
/** Incremental ID - unique across all layers */
|
||||
id?: integer;
|
||||
/** Image used by this layer. `imagelayer` only. */
|
||||
image?: string;
|
||||
/** Array of {@link TiledLayer}. `group` only. */
|
||||
layers?: Array<TiledLayer>;
|
||||
/** Name assigned to this layer */
|
||||
name: string;
|
||||
/** Array of {@link TiledObject}. `objectgroup` only. */
|
||||
objects?: Array<TiledObject>;
|
||||
/** Horizontal layer offset in pixels (default: 0) */
|
||||
offsetx?: float;
|
||||
/** Vertical layer offset in pixels (default: 0) */
|
||||
offsety?: float;
|
||||
/** Value between 0 and 1 */
|
||||
opacity: float;
|
||||
/** Horizontal {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
|
||||
parallaxx?: float;
|
||||
/** Vertical {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
|
||||
parallaxy?: float;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** X coordinate where layer content starts (for infinite maps) */
|
||||
startx?: integer;
|
||||
/** Y coordinate where layer content starts (for infinite maps) */
|
||||
starty?: integer;
|
||||
/** Hex-formatted {@link tint color} (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional). */
|
||||
tintcolor?: string;
|
||||
/** Hex-formatted color (#RRGGBB) (optional). `imagelayer` only. */
|
||||
transparentcolor?: string;
|
||||
/** `tilelayer`, `objectgroup`, `imagelayer` or `group` */
|
||||
type: string;
|
||||
/** Whether layer is shown or hidden in editor */
|
||||
visible: boolean;
|
||||
/** Column count. Same as map width for fixed-size maps. */
|
||||
width?: integer;
|
||||
/** Horizontal layer offset in tiles. Always 0. */
|
||||
x: integer;
|
||||
/** Vertical layer offset in tiles. Always 0. */
|
||||
y: integer;
|
||||
};
|
||||
export declare type TiledChunk = {
|
||||
/** Array of `unsigned` `integer` (GIDs) or base64-encoded data */
|
||||
data: Array<integer> | string;
|
||||
/** Height in tiles */
|
||||
height: integer;
|
||||
/** Width in tiles */
|
||||
width: integer;
|
||||
/** X coordinate in tiles */
|
||||
x: integer;
|
||||
/** Y coordinate in tiles */
|
||||
y: integer;
|
||||
};
|
||||
export declare type TiledObject = {
|
||||
/** Used to mark an object as an ellipse */
|
||||
ellipse?: boolean;
|
||||
/** Global tile ID, only if object represents a tile */
|
||||
gid?: integer;
|
||||
/** Height in pixels. */
|
||||
height: float;
|
||||
/** Incremental ID, unique across all objects */
|
||||
id: integer;
|
||||
/** String assigned to name field in editor */
|
||||
name: string;
|
||||
/** Used to mark an object as a point */
|
||||
point?: boolean;
|
||||
/** Array of {@link TiledPoint}, in case the object is a polygon */
|
||||
polygon?: Array<TiledPoint>;
|
||||
/** Array of {@link TiledPoint}, in case the object is a polyline */
|
||||
polyline?: Array<TiledPoint>;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** Angle in degrees clockwise */
|
||||
rotation: float;
|
||||
/** Reference to a template file, in case object is a {@link template instance} */
|
||||
template?: string;
|
||||
/** Only used for text objects */
|
||||
text?: Text;
|
||||
/** String assigned to type Tiledfield in editor */
|
||||
type: string;
|
||||
/** Whether object is shown in editor. */
|
||||
visible: boolean;
|
||||
/** Width in pixels. */
|
||||
width: float;
|
||||
/** X coordinate in pixels */
|
||||
x: float;
|
||||
/** Y coordinate in pixels */
|
||||
y: float;
|
||||
};
|
||||
export declare type TiledText = {
|
||||
/** Whether to use a bold font (default: `false`) */
|
||||
bold: boolean;
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (default: `#000000`) */
|
||||
color: string;
|
||||
/** Font family (default: `sans-serif`) */
|
||||
fontfamily: string;
|
||||
/** Horizontal alignment (`center`, `right`, `justify` or `left` (default)) */
|
||||
halign: string;
|
||||
/** Whether to use an italic font (default: `false`) */
|
||||
italic: boolean;
|
||||
/** Whether to use kerning when placing characters (default: `true`) */
|
||||
kerning: boolean;
|
||||
/** Pixel size of font (default: 16) */
|
||||
pixelsize: integer;
|
||||
/** Whether to strike out the text (default: `false`) */
|
||||
strikeout: boolean;
|
||||
/** Text */
|
||||
text: string;
|
||||
/** Whether to underline the text (default: `false`) */
|
||||
underline: boolean;
|
||||
/** Vertical alignment (`center`, `bottom` or `top` (default)) */
|
||||
valign: string;
|
||||
/** Whether the text is wrapped within the object bounds (default: `false`) */
|
||||
wrap: boolean;
|
||||
};
|
||||
export declare type TiledTileset = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
|
||||
backgroundcolor?: string;
|
||||
/** The number of tile columns in the tileset */
|
||||
columns: integer;
|
||||
/** GID corresponding to the first tile in the set */
|
||||
firstgid: integer;
|
||||
/** (optional) */
|
||||
grid?: TiledGrid;
|
||||
/** Image used for tiles in this set */
|
||||
image: string;
|
||||
/** Height of source image in pixels */
|
||||
imageheight: integer;
|
||||
/** Width of source image in pixels */
|
||||
imagewidth: integer;
|
||||
/** Buffer between image edge and first tile (pixels) */
|
||||
margin: integer;
|
||||
/** Name given to this tileset */
|
||||
name: string;
|
||||
/** Alignment to use for tile objects (`unspecified` (default), `topleft`, `top`, `topright`, `left`, `center`, `right`, `bottomleft`, `bottom` or `bottomright`) (since 1.4) */
|
||||
objectalignment?: string;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** The external file that contains this tilesets data */
|
||||
source?: string;
|
||||
/** Spacing between adjacent tiles in image (pixels) */
|
||||
spacing: integer;
|
||||
/** Array of {@link TiledTerrain} (optional) */
|
||||
terrains?: Array<TiledTerrain>;
|
||||
/** The number of tiles in this tileset */
|
||||
tilecount: integer;
|
||||
/** The Tiled version used to save the file */
|
||||
tiledversion: string;
|
||||
/** Maximum height of tiles in this set */
|
||||
tileheight: integer;
|
||||
/** (optional) */
|
||||
tileoffset?: TileOffset;
|
||||
/** Array of {@link TiledTileDefinition} (optional) */
|
||||
tiles?: Array<TiledTileDefinition>;
|
||||
/** Maximum width of tiles in this set */
|
||||
tilewidth: integer;
|
||||
/** Allowed transformations (optional) */
|
||||
transformations?: TiledTransformations;
|
||||
/** Hex-formatted color (#RRGGBB) (optional) */
|
||||
transparentcolor?: string;
|
||||
/** `tileset` (for tileset files, since 1.0) */
|
||||
type: string;
|
||||
/** The JSON format version (previously a number, saved as string since 1.6) */
|
||||
version: string;
|
||||
/** Array of {@link TiledWangSet} (since 1.1.5) */
|
||||
wangsets?: Array<TiledWangSet>;
|
||||
};
|
||||
export declare type TiledGrid = {
|
||||
/** Cell height of tile grid */
|
||||
height: integer;
|
||||
/** `orthogonal` (default) or `isometric` */
|
||||
orientation: string;
|
||||
/** Cell width of tile grid */
|
||||
width: integer;
|
||||
};
|
||||
export declare type TileOffset = {
|
||||
/** Horizontal offset in pixels */
|
||||
x: integer;
|
||||
/** Vertical offset in pixels (positive is down) */
|
||||
y: integer;
|
||||
};
|
||||
export declare type TiledTransformations = {
|
||||
/** Tiles can be flipped horizontally */
|
||||
hflip: boolean;
|
||||
/** Tiles can be flipped vertically */
|
||||
vflip: boolean;
|
||||
/** Tiles can be rotated in 90-degree increments */
|
||||
rotate: boolean;
|
||||
/** Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations */
|
||||
preferuntransformed: boolean;
|
||||
};
|
||||
export declare type TiledTileDefinition = {
|
||||
/** Array of {@link TiledTiles} */
|
||||
animation?: Array<TiledTileDefinition>;
|
||||
/** Local ID of the tile */
|
||||
id: integer;
|
||||
/** Image representing this tile (optional) */
|
||||
image?: string;
|
||||
/** Height of the tile image in pixels */
|
||||
imageheight?: integer;
|
||||
/** Width of the tile image in pixels */
|
||||
imagewidth?: integer;
|
||||
/** Layer with type Tiled`objectgroup`, when collision shapes are specified (optional) */
|
||||
objectgroup?: TiledLayer;
|
||||
/** Percentage chance this tile is chosen when competing with others in the editor (optional) */
|
||||
probability?: float;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** Index of terrain for each corner of tile (optional) */
|
||||
terrain?: Array<integer>;
|
||||
/** The type of the tile (optional) */
|
||||
type?: string;
|
||||
};
|
||||
export declare type TiledFrame = {
|
||||
/** Frame duration in milliseconds */
|
||||
duration: integer;
|
||||
/** Local tile ID representing this frame */
|
||||
tileid: integer;
|
||||
};
|
||||
export declare type TiledTerrain = {
|
||||
/** Name of terrain */
|
||||
name: string;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
/** Local ID of tile representing terrain */
|
||||
tile: integer;
|
||||
};
|
||||
export declare type TiledWangSet = {
|
||||
/** Array of {@link TiledWangColor} */
|
||||
colors: Array<TiledWangColor>;
|
||||
/** Name of the Wang set */
|
||||
name: string;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
/** Local ID of tile representing the Wang set */
|
||||
tile: integer;
|
||||
/** Array of {@link TiledWangTile} */
|
||||
wangtiles: Array<TiledWangTile>;
|
||||
};
|
||||
export declare type TiledWangColor = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) */
|
||||
color: string;
|
||||
/** Name of the Wang color */
|
||||
name: string;
|
||||
/** Probability used when randomizing */
|
||||
probability: float;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
/** Local ID of tile representing the Wang color */
|
||||
tile: integer;
|
||||
};
|
||||
export declare type TiledWangTile = {
|
||||
/** Local ID of tile */
|
||||
tileid: integer;
|
||||
/** Array of Wang color indexes (`uchar[8]`) */
|
||||
wangid: Array<integer>;
|
||||
};
|
||||
export declare type TiledObjectTemplate = {
|
||||
/** `template` */
|
||||
type: string;
|
||||
/** External tileset used by the template (optional) */
|
||||
tileset?: TiledTileset;
|
||||
/** The object instantiated by this template */
|
||||
object: Object;
|
||||
};
|
||||
export declare type TiledProperty = {
|
||||
/** Name of the property */
|
||||
name: string;
|
||||
/** type of the property (`string` (default), `integer`, `float`, `boolean`, `color` or `file` (since 0.16, with `color` and `file` added in 0.17)) */
|
||||
type: string;
|
||||
/** Value of the property */
|
||||
value: string | number;
|
||||
};
|
||||
export declare type TiledPoint = {
|
||||
/** X coordinate in pixels */
|
||||
x: float;
|
||||
/** Y coordinate in pixels */
|
||||
y: float;
|
||||
};
|
||||
//# sourceMappingURL=Tiled.d.ts.map
|
1
Extensions/TileMap/helper/dts/tiled/Tiled.d.ts.map
Normal file
1
Extensions/TileMap/helper/dts/tiled/Tiled.d.ts.map
Normal file
File diff suppressed because one or more lines are too long
339
Extensions/TileMap/helper/dts/tiled/TiledFormat.d.ts
vendored
Normal file
339
Extensions/TileMap/helper/dts/tiled/TiledFormat.d.ts
vendored
Normal file
@@ -0,0 +1,339 @@
|
||||
import { float, integer } from '../model/CommonTypes';
|
||||
/**
|
||||
* Tiled JSON format (https://www.mapeditor.org/).
|
||||
*/
|
||||
export declare type TiledMap = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
|
||||
backgroundcolor?: string;
|
||||
/** The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) */
|
||||
compressionlevel: integer;
|
||||
/** Number of tile rows */
|
||||
height: integer;
|
||||
/** Length of the side of a hex tile in pixels (hexagonal maps only) */
|
||||
hexsidelength?: integer;
|
||||
/** Whether the map has infinite dimensions */
|
||||
infinite: boolean;
|
||||
/** Array of {@link TiledLayer} */
|
||||
layers: Array<TiledLayer>;
|
||||
/** Auto-increments for each layer */
|
||||
nextlayerid: integer;
|
||||
/** Auto-increments for each placed object */
|
||||
nextobjectid: integer;
|
||||
/** `orthogonal`, `isometric`, `staggered` or `hexagonal` */
|
||||
orientation: string;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** `right-down` (the default), `right-up`, `left-down` or `left-up` (currently only supported for orthogonal maps) */
|
||||
renderorder: string;
|
||||
/** `x` or `y` (staggered / hexagonal maps only) */
|
||||
staggeraxis?: string;
|
||||
/** `odd` or `even` (staggered / hexagonal maps only) */
|
||||
staggerindex?: string;
|
||||
/** The Tiled version used to save the file */
|
||||
tiledversion: string;
|
||||
/** Map grid height */
|
||||
tileheight: integer;
|
||||
/** Array of {@link TiledTileset} */
|
||||
tilesets: Array<TiledTileset>;
|
||||
/** Map grid width */
|
||||
tilewidth: integer;
|
||||
/** `map` (since 1.0) */
|
||||
type: string;
|
||||
/** The JSON format version (previously a number, saved as string since 1.6) */
|
||||
version: string;
|
||||
/** Number of tile columns */
|
||||
width: integer;
|
||||
};
|
||||
export declare type TiledLayer = {
|
||||
/** Array of {@link TiledChunk} (optional). `tilelayer` only. */
|
||||
chunks?: Array<TiledChunk>;
|
||||
/** `zlib`, `gzip`, `zstd` (since Tiled 1.3) or empty (default). `tilelayer` only. */
|
||||
compression?: string;
|
||||
/** Array of `unsigned`, `integer` (GIDs) or base64-encoded data. `tilelayer` only.*/
|
||||
data?: Array<integer> | string;
|
||||
/** `topdown` (default) or `index`. `objectgroup` only. */
|
||||
draworder?: string;
|
||||
/** `csv` (default) or `base64`. `tilelayer` only. */
|
||||
encoding?: string;
|
||||
/** Row count. Same as map height for fixed-size maps. */
|
||||
height?: integer;
|
||||
/** Incremental ID - unique across all layers */
|
||||
id?: integer;
|
||||
/** Image used by this layer. `imagelayer` only. */
|
||||
image?: string;
|
||||
/** Array of {@link TiledLayer}. `group` only. */
|
||||
layers?: Array<TiledLayer>;
|
||||
/** Name assigned to this layer */
|
||||
name: string;
|
||||
/** Array of {@link TiledObject}. `objectgroup` only. */
|
||||
objects?: Array<TiledObject>;
|
||||
/** Horizontal layer offset in pixels (default: 0) */
|
||||
offsetx?: float;
|
||||
/** Vertical layer offset in pixels (default: 0) */
|
||||
offsety?: float;
|
||||
/** Value between 0 and 1 */
|
||||
opacity: float;
|
||||
/** Horizontal {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
|
||||
parallaxx?: float;
|
||||
/** Vertical {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
|
||||
parallaxy?: float;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** X coordinate where layer content starts (for infinite maps) */
|
||||
startx?: integer;
|
||||
/** Y coordinate where layer content starts (for infinite maps) */
|
||||
starty?: integer;
|
||||
/** Hex-formatted {@link tint color} (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional). */
|
||||
tintcolor?: string;
|
||||
/** Hex-formatted color (#RRGGBB) (optional). `imagelayer` only. */
|
||||
transparentcolor?: string;
|
||||
/** `tilelayer`, `objectgroup`, `imagelayer` or `group` */
|
||||
type: string;
|
||||
/** Whether layer is shown or hidden in editor */
|
||||
visible: boolean;
|
||||
/** Column count. Same as map width for fixed-size maps. */
|
||||
width?: integer;
|
||||
/** Horizontal layer offset in tiles. Always 0. */
|
||||
x: integer;
|
||||
/** Vertical layer offset in tiles. Always 0. */
|
||||
y: integer;
|
||||
};
|
||||
export declare type TiledChunk = {
|
||||
/** Array of `unsigned` `integer` (GIDs) or base64-encoded data */
|
||||
data: Array<integer> | string;
|
||||
/** Height in tiles */
|
||||
height: integer;
|
||||
/** Width in tiles */
|
||||
width: integer;
|
||||
/** X coordinate in tiles */
|
||||
x: integer;
|
||||
/** Y coordinate in tiles */
|
||||
y: integer;
|
||||
};
|
||||
export declare type TiledObject = {
|
||||
/** The class of the object (renamed from type since 1.9, optional) */
|
||||
class?: string;
|
||||
/** Used to mark an object as an ellipse */
|
||||
ellipse?: boolean;
|
||||
/** Global tile ID, only if object represents a tile */
|
||||
gid?: integer;
|
||||
/** Height in pixels. */
|
||||
height: float;
|
||||
/** Incremental ID, unique across all objects */
|
||||
id: integer;
|
||||
/** String assigned to name field in editor */
|
||||
name: string;
|
||||
/** Used to mark an object as a point */
|
||||
point?: boolean;
|
||||
/** Array of {@link TiledPoint}, in case the object is a polygon */
|
||||
polygon?: Array<TiledPoint>;
|
||||
/** Array of {@link TiledPoint}, in case the object is a polyline */
|
||||
polyline?: Array<TiledPoint>;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** Angle in degrees clockwise */
|
||||
rotation: float;
|
||||
/** Reference to a template file, in case object is a {@link template instance} */
|
||||
template?: string;
|
||||
/** Only used for text objects */
|
||||
text?: Text;
|
||||
/** Whether object is shown in editor. */
|
||||
visible: boolean;
|
||||
/** Width in pixels. */
|
||||
width: float;
|
||||
/** X coordinate in pixels */
|
||||
x: float;
|
||||
/** Y coordinate in pixels */
|
||||
y: float;
|
||||
};
|
||||
export declare type TiledText = {
|
||||
/** Whether to use a bold font (default: `false`) */
|
||||
bold: boolean;
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (default: `#000000`) */
|
||||
color: string;
|
||||
/** Font family (default: `sans-serif`) */
|
||||
fontfamily: string;
|
||||
/** Horizontal alignment (`center`, `right`, `justify` or `left` (default)) */
|
||||
halign: string;
|
||||
/** Whether to use an italic font (default: `false`) */
|
||||
italic: boolean;
|
||||
/** Whether to use kerning when placing characters (default: `true`) */
|
||||
kerning: boolean;
|
||||
/** Pixel size of font (default: 16) */
|
||||
pixelsize: integer;
|
||||
/** Whether to strike out the text (default: `false`) */
|
||||
strikeout: boolean;
|
||||
/** Text */
|
||||
text: string;
|
||||
/** Whether to underline the text (default: `false`) */
|
||||
underline: boolean;
|
||||
/** Vertical alignment (`center`, `bottom` or `top` (default)) */
|
||||
valign: string;
|
||||
/** Whether the text is wrapped within the object bounds (default: `false`) */
|
||||
wrap: boolean;
|
||||
};
|
||||
export declare type TiledTileset = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
|
||||
backgroundcolor?: string;
|
||||
/** The number of tile columns in the tileset */
|
||||
columns: integer;
|
||||
/** GID corresponding to the first tile in the set */
|
||||
firstgid: integer;
|
||||
/** (optional) */
|
||||
grid?: TiledGrid;
|
||||
/** Image used for tiles in this set */
|
||||
image: string;
|
||||
/** Height of source image in pixels */
|
||||
imageheight: integer;
|
||||
/** Width of source image in pixels */
|
||||
imagewidth: integer;
|
||||
/** Buffer between image edge and first tile (pixels) */
|
||||
margin: integer;
|
||||
/** Name given to this tileset */
|
||||
name: string;
|
||||
/** Alignment to use for tile objects (`unspecified` (default), `topleft`, `top`, `topright`, `left`, `center`, `right`, `bottomleft`, `bottom` or `bottomright`) (since 1.4) */
|
||||
objectalignment?: string;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** The external file that contains this tilesets data */
|
||||
source?: string;
|
||||
/** Spacing between adjacent tiles in image (pixels) */
|
||||
spacing: integer;
|
||||
/** Array of {@link TiledTerrain} (optional) */
|
||||
terrains?: Array<TiledTerrain>;
|
||||
/** The number of tiles in this tileset */
|
||||
tilecount: integer;
|
||||
/** The Tiled version used to save the file */
|
||||
tiledversion: string;
|
||||
/** Maximum height of tiles in this set */
|
||||
tileheight: integer;
|
||||
/** (optional) */
|
||||
tileoffset?: TileOffset;
|
||||
/** Array of {@link TiledTileDefinition} (optional) */
|
||||
tiles?: Array<TiledTileDefinition>;
|
||||
/** Maximum width of tiles in this set */
|
||||
tilewidth: integer;
|
||||
/** Allowed transformations (optional) */
|
||||
transformations?: TiledTransformations;
|
||||
/** Hex-formatted color (#RRGGBB) (optional) */
|
||||
transparentcolor?: string;
|
||||
/** `tileset` (for tileset files, since 1.0) */
|
||||
type: string;
|
||||
/** The JSON format version (previously a number, saved as string since 1.6) */
|
||||
version: string;
|
||||
/** Array of {@link TiledWangSet} (since 1.1.5) */
|
||||
wangsets?: Array<TiledWangSet>;
|
||||
};
|
||||
export declare type TiledGrid = {
|
||||
/** Cell height of tile grid */
|
||||
height: integer;
|
||||
/** `orthogonal` (default) or `isometric` */
|
||||
orientation: string;
|
||||
/** Cell width of tile grid */
|
||||
width: integer;
|
||||
};
|
||||
export declare type TileOffset = {
|
||||
/** Horizontal offset in pixels */
|
||||
x: integer;
|
||||
/** Vertical offset in pixels (positive is down) */
|
||||
y: integer;
|
||||
};
|
||||
export declare type TiledTransformations = {
|
||||
/** Tiles can be flipped horizontally */
|
||||
hflip: boolean;
|
||||
/** Tiles can be flipped vertically */
|
||||
vflip: boolean;
|
||||
/** Tiles can be rotated in 90-degree increments */
|
||||
rotate: boolean;
|
||||
/** Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations */
|
||||
preferuntransformed: boolean;
|
||||
};
|
||||
export declare type TiledTileDefinition = {
|
||||
/** Array of {@link TiledTiles} */
|
||||
animation?: Array<TiledTileDefinition>;
|
||||
/** The class of the tile (renamed from type since 1.9, optional) */
|
||||
class?: string;
|
||||
/** Local ID of the tile */
|
||||
id: integer;
|
||||
/** Image representing this tile (optional) */
|
||||
image?: string;
|
||||
/** Height of the tile image in pixels */
|
||||
imageheight?: integer;
|
||||
/** Width of the tile image in pixels */
|
||||
imagewidth?: integer;
|
||||
/** Layer with type Tiled`objectgroup`, when collision shapes are specified (optional) */
|
||||
objectgroup?: TiledLayer;
|
||||
/** Percentage chance this tile is chosen when competing with others in the editor (optional) */
|
||||
probability?: float;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
/** Index of terrain for each corner of tile (optional) */
|
||||
terrain?: Array<integer>;
|
||||
};
|
||||
export declare type TiledFrame = {
|
||||
/** Frame duration in milliseconds */
|
||||
duration: integer;
|
||||
/** Local tile ID representing this frame */
|
||||
tileid: integer;
|
||||
};
|
||||
export declare type TiledTerrain = {
|
||||
/** Name of terrain */
|
||||
name: string;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
/** Local ID of tile representing terrain */
|
||||
tile: integer;
|
||||
};
|
||||
export declare type TiledWangSet = {
|
||||
/** Array of {@link TiledWangColor} */
|
||||
colors: Array<TiledWangColor>;
|
||||
/** Name of the Wang set */
|
||||
name: string;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
/** Local ID of tile representing the Wang set */
|
||||
tile: integer;
|
||||
/** Array of {@link TiledWangTile} */
|
||||
wangtiles: Array<TiledWangTile>;
|
||||
};
|
||||
export declare type TiledWangColor = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) */
|
||||
color: string;
|
||||
/** Name of the Wang color */
|
||||
name: string;
|
||||
/** Probability used when randomizing */
|
||||
probability: float;
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
/** Local ID of tile representing the Wang color */
|
||||
tile: integer;
|
||||
};
|
||||
export declare type TiledWangTile = {
|
||||
/** Local ID of tile */
|
||||
tileid: integer;
|
||||
/** Array of Wang color indexes (`uchar[8]`) */
|
||||
wangid: Array<integer>;
|
||||
};
|
||||
export declare type TiledObjectTemplate = {
|
||||
/** `template` */
|
||||
type: string;
|
||||
/** External tileset used by the template (optional) */
|
||||
tileset?: TiledTileset;
|
||||
/** The object instantiated by this template */
|
||||
object: Object;
|
||||
};
|
||||
export declare type TiledProperty = {
|
||||
/** Name of the property */
|
||||
name: string;
|
||||
/** type of the property (`string` (default), `integer`, `float`, `boolean`, `color` or `file` (since 0.16, with `color` and `file` added in 0.17)) */
|
||||
type: string;
|
||||
/** Value of the property */
|
||||
value: string | number;
|
||||
};
|
||||
export declare type TiledPoint = {
|
||||
/** X coordinate in pixels */
|
||||
x: float;
|
||||
/** Y coordinate in pixels */
|
||||
y: float;
|
||||
};
|
||||
//# sourceMappingURL=TiledFormat.d.ts.map
|
1
Extensions/TileMap/helper/dts/tiled/TiledFormat.d.ts.map
Normal file
1
Extensions/TileMap/helper/dts/tiled/TiledFormat.d.ts.map
Normal file
File diff suppressed because one or more lines are too long
37
Extensions/TileMap/helper/dts/tiled/TiledLoaderHelper.d.ts
vendored
Normal file
37
Extensions/TileMap/helper/dts/tiled/TiledLoaderHelper.d.ts
vendored
Normal file
@@ -0,0 +1,37 @@
|
||||
import { integer } from '../model/CommonTypes';
|
||||
import { TiledLayer } from './TiledFormat';
|
||||
/**
|
||||
* Decodes a layer data, which can sometimes be store as a compressed base64 string
|
||||
* by Tiled.
|
||||
* See https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#data.
|
||||
* @param pako The zlib library.
|
||||
* @param layer The layer data from a Tiled JSON.
|
||||
* @returns The decoded layer data.
|
||||
*/
|
||||
export declare const decodeBase64LayerData: (
|
||||
pako: any,
|
||||
layer: TiledLayer
|
||||
) => number[];
|
||||
export declare type TiledGID = {
|
||||
id: integer;
|
||||
flippedHorizontally: boolean;
|
||||
flippedVertically: boolean;
|
||||
flippedDiagonally: boolean;
|
||||
};
|
||||
/**
|
||||
* Extract information about the rotation of a tile from the tile id.
|
||||
* @param globalTileUid The Tiled tile global uniq identifier.
|
||||
* @returns The tile identifier and orientation.
|
||||
*/
|
||||
export declare const extractTileUidFlippedStates: (
|
||||
globalTileUid: integer
|
||||
) => TiledGID;
|
||||
/**
|
||||
* Tiled use 0 as null, we do too but it's black boxed.
|
||||
* This is why the id needs to be decremented.
|
||||
* @return the tile identifier used in {@link TilMapModel}.
|
||||
*/
|
||||
export declare const getTileIdFromTiledGUI: (
|
||||
tiledGUI: number | undefined
|
||||
) => number | undefined;
|
||||
//# sourceMappingURL=TiledLoaderHelper.d.ts.map
|
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TiledLoaderHelper.d.ts","sourceRoot":"","sources":["../../src/tiled/TiledLoaderHelper.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAC;AAC/C,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C;;;;;;;GAOG;AACH,eAAO,MAAM,qBAAqB,SAAU,GAAG,SAAS,UAAU,aAgDjE,CAAC;AAEF,oBAAY,QAAQ,GAAG;IACrB,EAAE,EAAE,OAAO,CAAC;IACZ,mBAAmB,EAAE,OAAO,CAAC;IAC7B,iBAAiB,EAAE,OAAO,CAAC;IAC3B,iBAAiB,EAAE,OAAO,CAAC;CAC5B,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,2BAA2B,kBACvB,OAAO,KACrB,QAuBF,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,qBAAqB,aACtB,MAAM,GAAG,SAAS,KAC3B,MAAM,GAAG,SAAwD,CAAC"}
|
9
Extensions/TileMap/helper/dts/tiled/TiledTileMapLoader.d.ts
vendored
Normal file
9
Extensions/TileMap/helper/dts/tiled/TiledTileMapLoader.d.ts
vendored
Normal file
@@ -0,0 +1,9 @@
|
||||
import { EditableTileMap } from '../model/TileMapModel';
|
||||
import { TiledMap } from './TiledFormat';
|
||||
/**
|
||||
* It creates a {@link EditableTileMap} from a Tiled JSON.
|
||||
*/
|
||||
export declare class TiledTileMapLoader {
|
||||
static load(pako: any, tiledMap: TiledMap): EditableTileMap | null;
|
||||
}
|
||||
//# sourceMappingURL=TiledTileMapLoader.d.ts.map
|
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TiledTileMapLoader.d.ts","sourceRoot":"","sources":["../../src/tiled/TiledTileMapLoader.ts"],"names":[],"mappings":"AACA,OAAO,EACL,eAAe,EAGhB,MAAM,uBAAuB,CAAC;AAC/B,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAC;AAOzC;;GAEG;AACH,qBAAa,kBAAkB;IAC7B,MAAM,CAAC,IAAI,CAAC,IAAI,EAAE,GAAG,EAAE,QAAQ,EAAE,QAAQ,GAAG,eAAe,GAAG,IAAI;CA2KnE"}
|
2
Extensions/TileMap/helper/dts/tiled/TiledTileMapLoader.spec.d.ts
vendored
Normal file
2
Extensions/TileMap/helper/dts/tiled/TiledTileMapLoader.spec.d.ts
vendored
Normal file
@@ -0,0 +1,2 @@
|
||||
export {};
|
||||
//# sourceMappingURL=TiledTileMapLoader.spec.d.ts.map
|
@@ -0,0 +1 @@
|
||||
{"version":3,"file":"TiledTileMapLoader.spec.d.ts","sourceRoot":"","sources":["../../src/tiled/TiledTileMapLoader.spec.ts"],"names":[],"mappings":""}
|
166
Extensions/TileMap/pako/dist/pako.d.ts
vendored
Normal file
166
Extensions/TileMap/pako/dist/pako.d.ts
vendored
Normal file
@@ -0,0 +1,166 @@
|
||||
// Type definitions for pako 1.0
|
||||
// Project: https://github.com/nodeca/pako
|
||||
// Definitions by: Denis Cappellin <https://github.com/cappellin>
|
||||
// Caleb Eggensperger <https://github.com/calebegg>
|
||||
// Muhammet Öztürk <https://github.com/hlthi>
|
||||
// Definitions: https://github.com/DefinitelyTyped/DefinitelyTyped
|
||||
|
||||
export = Pako;
|
||||
export as namespace pako;
|
||||
|
||||
declare namespace Pako {
|
||||
enum FlushValues {
|
||||
Z_NO_FLUSH = 0,
|
||||
Z_PARTIAL_FLUSH = 1,
|
||||
Z_SYNC_FLUSH = 2,
|
||||
Z_FULL_FLUSH = 3,
|
||||
Z_FINISH = 4,
|
||||
Z_BLOCK = 5,
|
||||
Z_TREES = 6,
|
||||
}
|
||||
|
||||
enum StrategyValues {
|
||||
Z_FILTERED = 1,
|
||||
Z_HUFFMAN_ONLY = 2,
|
||||
Z_RLE = 3,
|
||||
Z_FIXED = 4,
|
||||
Z_DEFAULT_STRATEGY = 0,
|
||||
}
|
||||
|
||||
enum ReturnCodes {
|
||||
Z_OK = 0,
|
||||
Z_STREAM_END = 1,
|
||||
Z_NEED_DICT = 2,
|
||||
Z_ERRNO = -1,
|
||||
Z_STREAM_ERROR = -2,
|
||||
Z_DATA_ERROR = -3,
|
||||
Z_BUF_ERROR = -5,
|
||||
}
|
||||
|
||||
interface DeflateOptions {
|
||||
level?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined;
|
||||
windowBits?: number | undefined;
|
||||
memLevel?: number | undefined;
|
||||
strategy?: StrategyValues | undefined;
|
||||
dictionary?: any;
|
||||
raw?: boolean | undefined;
|
||||
to?: 'string' | undefined;
|
||||
chunkSize?: number | undefined;
|
||||
gzip?: boolean | undefined;
|
||||
header?: Header | undefined;
|
||||
}
|
||||
|
||||
interface DeflateFunctionOptions {
|
||||
level?: -1 | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | undefined;
|
||||
windowBits?: number | undefined;
|
||||
memLevel?: number | undefined;
|
||||
strategy?: StrategyValues | undefined;
|
||||
dictionary?: any;
|
||||
raw?: boolean | undefined;
|
||||
to?: 'string' | undefined;
|
||||
}
|
||||
|
||||
interface InflateOptions {
|
||||
windowBits?: number | undefined;
|
||||
dictionary?: any;
|
||||
raw?: boolean | undefined;
|
||||
to?: 'string' | undefined;
|
||||
chunkSize?: number | undefined;
|
||||
}
|
||||
|
||||
interface InflateFunctionOptions {
|
||||
windowBits?: number | undefined;
|
||||
raw?: boolean | undefined;
|
||||
to?: 'string' | undefined;
|
||||
}
|
||||
|
||||
interface Header {
|
||||
text?: boolean | undefined;
|
||||
time?: number | undefined;
|
||||
os?: number | undefined;
|
||||
extra?: number[] | undefined;
|
||||
name?: string | undefined;
|
||||
comment?: string | undefined;
|
||||
hcrc?: boolean | undefined;
|
||||
}
|
||||
|
||||
type Data = Uint8Array | number[] | string;
|
||||
|
||||
/**
|
||||
* Compress data with deflate algorithm and options.
|
||||
*/
|
||||
function deflate(
|
||||
data: Data,
|
||||
options: DeflateFunctionOptions & { to: 'string' }
|
||||
): string;
|
||||
function deflate(data: Data, options?: DeflateFunctionOptions): Uint8Array;
|
||||
|
||||
/**
|
||||
* The same as deflate, but creates raw data, without wrapper (header and adler32 crc).
|
||||
*/
|
||||
function deflateRaw(
|
||||
data: Data,
|
||||
options: DeflateFunctionOptions & { to: 'string' }
|
||||
): string;
|
||||
function deflateRaw(data: Data, options?: DeflateFunctionOptions): Uint8Array;
|
||||
|
||||
/**
|
||||
* The same as deflate, but create gzip wrapper instead of deflate one.
|
||||
*/
|
||||
function gzip(
|
||||
data: Data,
|
||||
options: DeflateFunctionOptions & { to: 'string' }
|
||||
): string;
|
||||
function gzip(data: Data, options?: DeflateFunctionOptions): Uint8Array;
|
||||
|
||||
/**
|
||||
* Decompress data with inflate/ungzip and options. Autodetect format via wrapper header
|
||||
* by default. That's why we don't provide separate ungzip method.
|
||||
*/
|
||||
function inflate(
|
||||
data: Data,
|
||||
options: InflateFunctionOptions & { to: 'string' }
|
||||
): string;
|
||||
function inflate(data: Data, options?: InflateFunctionOptions): Uint8Array;
|
||||
|
||||
/**
|
||||
* The same as inflate, but creates raw data, without wrapper (header and adler32 crc).
|
||||
*/
|
||||
function inflateRaw(
|
||||
data: Data,
|
||||
options: InflateFunctionOptions & { to: 'string' }
|
||||
): string;
|
||||
function inflateRaw(data: Data, options?: InflateFunctionOptions): Uint8Array;
|
||||
|
||||
/**
|
||||
* Just shortcut to inflate, because it autodetects format by header.content. Done for convenience.
|
||||
*/
|
||||
function ungzip(
|
||||
data: Data,
|
||||
options: InflateFunctionOptions & { to: 'string' }
|
||||
): string;
|
||||
function ungzip(data: Data, options?: InflateFunctionOptions): Uint8Array;
|
||||
|
||||
// https://github.com/nodeca/pako/blob/893381abcafa10fa2081ce60dae7d4d8e873a658/lib/deflate.js
|
||||
class Deflate {
|
||||
constructor(options?: DeflateOptions);
|
||||
err: ReturnCodes;
|
||||
msg: string;
|
||||
result: Uint8Array | number[];
|
||||
onData(chunk: Data): void;
|
||||
onEnd(status: number): void;
|
||||
push(data: Data | ArrayBuffer, mode?: FlushValues | boolean): boolean;
|
||||
}
|
||||
|
||||
// https://github.com/nodeca/pako/blob/893381abcafa10fa2081ce60dae7d4d8e873a658/lib/inflate.js
|
||||
class Inflate {
|
||||
constructor(options?: InflateOptions);
|
||||
header?: Header | undefined;
|
||||
err: ReturnCodes;
|
||||
msg: string;
|
||||
result: Data;
|
||||
onData(chunk: Data): void;
|
||||
onEnd(status: number): void;
|
||||
push(data: Data | ArrayBuffer, mode?: FlushValues | boolean): boolean;
|
||||
}
|
||||
}
|
@@ -1,470 +0,0 @@
|
||||
// @ts-check
|
||||
(function (root, factory) {
|
||||
// @ts-ignore
|
||||
if (typeof exports === 'object' && typeof exports.nodeName !== 'string') {
|
||||
// CommonJS
|
||||
factory(exports);
|
||||
} else {
|
||||
// Browser globals
|
||||
// @ts-ignore
|
||||
factory((root.PixiTileMapHelper = {}));
|
||||
}
|
||||
})(typeof self !== 'undefined' ? self : this, function (exports) {
|
||||
/** @typedef {GlobalPIXIModule.PIXI.Texture} PIXI.Texture */
|
||||
/** @typedef {GlobalPIXIModule.PIXI.BaseTexture} PIXI.BaseTexture */
|
||||
/** @typedef {GlobalPIXIModule.PIXI.Rectangle} PIXI.Rectangle */
|
||||
const PIXI = GlobalPIXIModule.PIXI;
|
||||
|
||||
/**
|
||||
* Information about one or more tiles. Loosely based on
|
||||
* https://doc.mapeditor.org/en/stable/reference/json-map-format/#tile-definition.
|
||||
*
|
||||
* @typedef {{
|
||||
"id": number,
|
||||
"terrain"?: Array<number>,
|
||||
"animation"?: Array<{duration: number, tileid: number}>
|
||||
}} TiledDataTile
|
||||
*/
|
||||
|
||||
/**
|
||||
* Information about a layer. Loosely based on
|
||||
* https://doc.mapeditor.org/en/stable/reference/json-map-format/#layer.
|
||||
*
|
||||
* @typedef {{
|
||||
"compression"?:"zlib" | "gzip" | "zstd",
|
||||
"data":Array<number> | string,
|
||||
"encoding"?:"base64",
|
||||
"height":number,
|
||||
"id":number,
|
||||
"name": string,
|
||||
"opacity": number,
|
||||
"type": string,
|
||||
"visible":boolean,
|
||||
"width":number,
|
||||
"objects": Array<{ gid: number, x: number, y: number, visible: boolean }>
|
||||
}} TiledDataLayer
|
||||
*/
|
||||
|
||||
/**
|
||||
* Data to render a tile map. Loosely based on the merge of a Tiled
|
||||
* map and tileset.
|
||||
*
|
||||
* @typedef {{
|
||||
width: number,
|
||||
height: number,
|
||||
tileWidth: number,
|
||||
tileHeight: number,
|
||||
atlasTexture: PIXI.BaseTexture,
|
||||
textureCache: Object<number, PIXI.Texture | null>,
|
||||
layers: Array<TiledDataLayer>,
|
||||
tiles: Array<TiledDataTile>,
|
||||
}} GenericPixiTileMapData
|
||||
*/
|
||||
|
||||
/**
|
||||
* The Tilesets that are ready to be used
|
||||
* with Pixi Tilemap, indexed by their id.
|
||||
* @type {Object<string, GenericPixiTileMapData>}
|
||||
*/
|
||||
const loadedGenericPixiTileMapData = {};
|
||||
|
||||
/**
|
||||
* Parse a Tiled map JSON file,
|
||||
* exported from Tiled (https://www.mapeditor.org/)
|
||||
* into a generic tile map data (`GenericPixiTileMapData`).
|
||||
*
|
||||
* @param {Object} tiledData A JS object representing a map exported from Tiled.
|
||||
* @param {?PIXI.BaseTexture} atlasTexture
|
||||
* @param {(textureName: string) => PIXI.BaseTexture} getTexture A getter to load a texture. Used if atlasTexture is not specified.
|
||||
* @returns {?GenericPixiTileMapData}
|
||||
*/
|
||||
const parseTiledData = (tiledData, atlasTexture, getTexture) => {
|
||||
if (!tiledData.tiledversion) {
|
||||
console.warn(
|
||||
"The loaded Tiled map does not contain a 'tiledversion' key. Are you sure this file has been exported from Tiled (mapeditor.org)?"
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// We only handle tileset embedded in the tilemap. Warn if it's not the case.
|
||||
if (!tiledData.tilesets.length || 'source' in tiledData.tilesets[0]) {
|
||||
console.warn(
|
||||
"The loaded Tiled map seems not to contain any tileset data (nothing in 'tilesets' key)."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const {
|
||||
tilewidth,
|
||||
tileheight,
|
||||
tilecount,
|
||||
tiles,
|
||||
image,
|
||||
columns,
|
||||
spacing,
|
||||
margin,
|
||||
} = tiledData.tilesets[0];
|
||||
if (!atlasTexture) atlasTexture = getTexture(image);
|
||||
|
||||
// We try to detect what size Tiled is expecting.
|
||||
const rows = tilecount / columns;
|
||||
const expectedAtlasWidth =
|
||||
tilewidth * columns + spacing * (columns - 1) + margin * 2;
|
||||
const expectedAtlasHeight =
|
||||
tileheight * rows + spacing * (rows - 1) + margin * 2;
|
||||
if (
|
||||
(atlasTexture.width !== 1 && expectedAtlasWidth !== atlasTexture.width) ||
|
||||
(atlasTexture.height !== 1 && expectedAtlasHeight !== atlasTexture.height)
|
||||
) {
|
||||
const expectedSize = expectedAtlasWidth + 'x' + expectedAtlasHeight;
|
||||
const actualSize = atlasTexture.width + 'x' + atlasTexture.height;
|
||||
console.warn(
|
||||
'It seems the atlas file was resized, which is not supported. It should be ' +
|
||||
expectedSize +
|
||||
"px, but it's " +
|
||||
actualSize +
|
||||
' px.'
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prepare the textures pointing to the base "Atlas" Texture for each tile.
|
||||
// Note that this cache can be augmented later with rotated/flipped
|
||||
// versions of the tile textures.
|
||||
/** @type {Object<number, PIXI.Texture | null>} */
|
||||
const textureCache = {};
|
||||
for (let frame = 0; frame <= tilecount; frame++) {
|
||||
const columnMultiplier = Math.floor((frame - 1) % columns);
|
||||
const rowMultiplier = Math.floor((frame - 1) / columns);
|
||||
const x = margin + columnMultiplier * (tilewidth + spacing);
|
||||
const y = margin + rowMultiplier * (tileheight + spacing);
|
||||
|
||||
try {
|
||||
const rect = new PIXI.Rectangle(x, y, tilewidth, tileheight);
|
||||
// @ts-ignore - atlasTexture is never null here.
|
||||
const texture = new PIXI.Texture(atlasTexture, rect);
|
||||
|
||||
textureCache[frame] = texture;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'An error occurred while creating a PIXI.Texture to be used in a TileMap:',
|
||||
error
|
||||
);
|
||||
textureCache[frame] = null;
|
||||
}
|
||||
}
|
||||
|
||||
/** @type {GenericPixiTileMapData} */
|
||||
const tileMapData = {
|
||||
width: atlasTexture.width,
|
||||
height: atlasTexture.height,
|
||||
tileWidth: tilewidth,
|
||||
tileHeight: tileheight,
|
||||
atlasTexture: atlasTexture,
|
||||
textureCache: textureCache,
|
||||
layers: tiledData.layers,
|
||||
tiles: tiles,
|
||||
};
|
||||
return tileMapData;
|
||||
};
|
||||
|
||||
/**
|
||||
* Decodes a layer data, which can sometimes be store as a compressed base64 string
|
||||
* by Tiled.
|
||||
* See https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#data.
|
||||
*/
|
||||
const decodeBase64LayerData = (layer, pako) => {
|
||||
const { data, compression } = layer;
|
||||
let index = 4;
|
||||
const decodedData = [];
|
||||
let step1 = atob(data)
|
||||
.split('')
|
||||
.map(function (x) {
|
||||
return x.charCodeAt(0);
|
||||
});
|
||||
try {
|
||||
const decodeString = (str, index) =>
|
||||
(str.charCodeAt(index) +
|
||||
(str.charCodeAt(index + 1) << 8) +
|
||||
(str.charCodeAt(index + 2) << 16) +
|
||||
(str.charCodeAt(index + 3) << 24)) >>>
|
||||
0;
|
||||
const decodeArray = (arr, index) =>
|
||||
(arr[index] +
|
||||
(arr[index + 1] << 8) +
|
||||
(arr[index + 2] << 16) +
|
||||
(arr[index + 3] << 24)) >>>
|
||||
0;
|
||||
|
||||
if (compression === 'zlib') {
|
||||
const binData = new Uint8Array(step1);
|
||||
step1 = pako.inflate(binData);
|
||||
while (index <= step1.length) {
|
||||
decodedData.push(decodeArray(step1, index - 4));
|
||||
index += 4;
|
||||
}
|
||||
} else if (compression === 'zstd') {
|
||||
console.error(
|
||||
'Zstandard compression is not supported for layers in a Tilemap. Use instead zlib compression or no compression.'
|
||||
);
|
||||
return null;
|
||||
} else {
|
||||
while (index <= step1.length) {
|
||||
decodedData.push(decodeString(step1, index - 4));
|
||||
index += 4;
|
||||
}
|
||||
}
|
||||
return decodedData;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'Failed to decompress and unzip base64 layer.data string',
|
||||
error
|
||||
);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract information about the rotation of a tile from the tile id.
|
||||
* @param {number} globalTileUid
|
||||
* @returns {[number, boolean, boolean, boolean]}
|
||||
*/
|
||||
const extractTileUidFlippedStates = (globalTileUid) => {
|
||||
const FLIPPED_HORIZONTALLY_FLAG = 0x80000000;
|
||||
const FLIPPED_VERTICALLY_FLAG = 0x40000000;
|
||||
const FLIPPED_DIAGONALLY_FLAG = 0x20000000;
|
||||
|
||||
const flippedHorizontally = globalTileUid & FLIPPED_HORIZONTALLY_FLAG;
|
||||
const flippedVertically = globalTileUid & FLIPPED_VERTICALLY_FLAG;
|
||||
const flippedDiagonally = globalTileUid & FLIPPED_DIAGONALLY_FLAG;
|
||||
const tileUid =
|
||||
globalTileUid &
|
||||
~(
|
||||
FLIPPED_HORIZONTALLY_FLAG |
|
||||
FLIPPED_VERTICALLY_FLAG |
|
||||
FLIPPED_DIAGONALLY_FLAG
|
||||
);
|
||||
|
||||
return [tileUid, !!flippedHorizontally, !!flippedVertically, !!flippedDiagonally];
|
||||
};
|
||||
|
||||
/**
|
||||
* Return the texture to use for the tile with the specified uid, which can contains
|
||||
* information about rotation in bits 32, 31 and 30
|
||||
* (see https://doc.mapeditor.org/en/stable/reference/tmx-map-format/).
|
||||
*
|
||||
* @param {Object<number, PIXI.Texture | null>} textureCache
|
||||
* @param {number} globalTileUid
|
||||
* @returns {?PIXI.Texture}
|
||||
*/
|
||||
const findTileTextureInCache = (textureCache, globalTileUid) => {
|
||||
if (globalTileUid === 0) return null;
|
||||
|
||||
if (textureCache[globalTileUid]) {
|
||||
return textureCache[globalTileUid];
|
||||
}
|
||||
|
||||
// If the texture is not in the cache, it's potentially because its ID
|
||||
// is a flipped/rotated version of another ID.
|
||||
const flippedStates = extractTileUidFlippedStates(globalTileUid);
|
||||
const tileUid = flippedStates[0];
|
||||
const flippedHorizontally = flippedStates[1];
|
||||
const flippedVertically = flippedStates[2];
|
||||
const flippedDiagonally = flippedStates[3];
|
||||
|
||||
if (tileUid === 0) return null;
|
||||
|
||||
// If the tile still can't be found in the cache, it means the ID we got
|
||||
// is invalid.
|
||||
const unflippedTexture = textureCache[tileUid];
|
||||
if (!unflippedTexture) return null;
|
||||
|
||||
// Clone the unflipped texture and save it in the cache
|
||||
const frame = unflippedTexture.frame.clone();
|
||||
const orig = unflippedTexture.orig.clone();
|
||||
if (flippedDiagonally) {
|
||||
const width = orig.width;
|
||||
orig.width = orig.height;
|
||||
orig.height = width;
|
||||
}
|
||||
const trim = orig.clone();
|
||||
|
||||
// Get the rotation "D8" number.
|
||||
// See https://pixijs.io/examples/#/textures/texture-rotate.js
|
||||
let rotate = 0;
|
||||
if (flippedDiagonally) {
|
||||
rotate = 10;
|
||||
if (!flippedHorizontally && flippedVertically) {
|
||||
rotate = 2;
|
||||
} else if (flippedHorizontally && !flippedVertically) {
|
||||
rotate = 6;
|
||||
} else if (flippedHorizontally && flippedVertically) {
|
||||
rotate = 14;
|
||||
}
|
||||
} else {
|
||||
rotate = 0;
|
||||
if (!flippedHorizontally && flippedVertically) {
|
||||
rotate = 8;
|
||||
} else if (flippedHorizontally && !flippedVertically) {
|
||||
rotate = 12;
|
||||
} else if (flippedHorizontally && flippedVertically) {
|
||||
rotate = 4;
|
||||
}
|
||||
}
|
||||
|
||||
const flippedTexture = new PIXI.Texture(
|
||||
unflippedTexture.baseTexture,
|
||||
frame,
|
||||
orig,
|
||||
trim,
|
||||
rotate
|
||||
);
|
||||
return (textureCache[globalTileUid] = flippedTexture);
|
||||
};
|
||||
|
||||
/**
|
||||
* Re-renders the tilemap whenever its rendering settings have been changed
|
||||
*
|
||||
* @param {any} pixiTileMap
|
||||
* @param {GenericPixiTileMapData} genericTileMapData
|
||||
* @param {'index' | 'visible' | 'all'} displayMode What to display: only a single layer (`index`), only visible layers (`visible`) or everyhing (`all`).
|
||||
* @param {number} layerIndex If `displayMode` is set to `index`, the layer index to be displayed.
|
||||
* @param {any} pako The Pako library object, to decompress the layer data.
|
||||
*/
|
||||
exports.updatePixiTileMap = (
|
||||
pixiTileMap,
|
||||
genericTileMapData,
|
||||
displayMode,
|
||||
layerIndex,
|
||||
pako
|
||||
) => {
|
||||
if (!pixiTileMap || !genericTileMapData) return;
|
||||
pixiTileMap.clear();
|
||||
|
||||
genericTileMapData.layers.forEach(function (layer, index) {
|
||||
if (displayMode === 'index' && layerIndex !== index) return;
|
||||
else if (displayMode === 'visible' && !layer.visible) return;
|
||||
|
||||
if (layer.type === 'objectgroup') {
|
||||
layer.objects.forEach(function (object) {
|
||||
const { gid, x, y, visible } = object;
|
||||
if (displayMode === 'visible' && !visible) return;
|
||||
if (genericTileMapData.textureCache[gid]) {
|
||||
pixiTileMap.addFrame(
|
||||
genericTileMapData.textureCache[gid],
|
||||
x,
|
||||
y - genericTileMapData.tileHeight
|
||||
);
|
||||
}
|
||||
});
|
||||
} else if (layer.type === 'tilelayer') {
|
||||
let tileSlotIndex = 0;
|
||||
let layerData = layer.data;
|
||||
|
||||
if (layer.encoding === 'base64') {
|
||||
// @ts-ignore
|
||||
layerData = decodeBase64LayerData(layer, pako);
|
||||
if (!layerData) {
|
||||
console.warn('Failed to uncompress layer.data');
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
for (let i = 0; i < layer.height; i++) {
|
||||
for (let j = 0; j < layer.width; j++) {
|
||||
const xPos = genericTileMapData.tileWidth * j;
|
||||
const yPos = genericTileMapData.tileHeight * i;
|
||||
|
||||
// The "globalTileUid" is the tile UID with encoded
|
||||
// bits about the flipping/rotation of the tile.
|
||||
/** @type {number} */
|
||||
// @ts-ignore
|
||||
const globalTileUid = layerData[tileSlotIndex];
|
||||
|
||||
// Extract the tile UID and the texture.
|
||||
const tileUid = extractTileUidFlippedStates(globalTileUid)[0];
|
||||
const tileTexture = findTileTextureInCache(
|
||||
genericTileMapData.textureCache,
|
||||
globalTileUid
|
||||
);
|
||||
if (tileTexture) {
|
||||
const tileData =
|
||||
genericTileMapData.tiles &&
|
||||
genericTileMapData.tiles.find(function (tile) {
|
||||
return tile.id === tileUid - 1;
|
||||
});
|
||||
|
||||
const pixiTilemapFrame = pixiTileMap.addFrame(
|
||||
tileTexture,
|
||||
xPos,
|
||||
yPos
|
||||
);
|
||||
|
||||
// Animated tiles have a limitation:
|
||||
// they are only able to use frames arranged horizontally one next
|
||||
// to each other on the atlas.
|
||||
if (tileData && tileData.animation) {
|
||||
pixiTilemapFrame.tileAnimX(
|
||||
genericTileMapData.tileWidth,
|
||||
tileData.animation.length
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
tileSlotIndex += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
/**
|
||||
* Load the given data, exported from Tiled, into a generic tilemap data (`GenericPixiTileMapData`),
|
||||
* which can then be used to update a PIXI.Tilemap (see `updatePixiTileMap`).
|
||||
*
|
||||
* Later on, this could potentially be refactored to support other data structures
|
||||
* (LDtk, for example: https://github.com/deepnight/ldtk).
|
||||
*
|
||||
* @param {(textureName: string) => PIXI.BaseTexture} getTexture A getter to load a texture. Used if atlasTexture is not specified.
|
||||
* @param {Object} tiledData A JS object representing a map exported from Tiled.
|
||||
* @param {string} atlasImageResourceName The name of the resource to pass to `getTexture` to load the atlas.
|
||||
* @param {string} tilemapResourceName The name of the tilemap resource - used to index internally the loaded tilemap data.
|
||||
* @param {string} tilesetResourceName The name of the tileset resource - used to index internally the loaded tilemap data.
|
||||
* @returns {?GenericPixiTileMapData}
|
||||
*/
|
||||
exports.loadPixiTileMapData = (
|
||||
getTexture,
|
||||
tiledData,
|
||||
atlasImageResourceName,
|
||||
tilemapResourceName,
|
||||
tilesetResourceName
|
||||
) => {
|
||||
const requestedTileMapDataId =
|
||||
tilemapResourceName +
|
||||
'@' +
|
||||
tilesetResourceName +
|
||||
'@' +
|
||||
atlasImageResourceName;
|
||||
|
||||
// If the tilemap data is already in the cache, use it directly.
|
||||
if (loadedGenericPixiTileMapData[requestedTileMapDataId]) {
|
||||
return loadedGenericPixiTileMapData[requestedTileMapDataId];
|
||||
}
|
||||
|
||||
const atlasTexture = atlasImageResourceName
|
||||
? getTexture(atlasImageResourceName)
|
||||
: null;
|
||||
const genericPixiTileMapData = parseTiledData(
|
||||
tiledData,
|
||||
atlasTexture,
|
||||
getTexture
|
||||
);
|
||||
|
||||
if (genericPixiTileMapData)
|
||||
loadedGenericPixiTileMapData[
|
||||
requestedTileMapDataId
|
||||
] = genericPixiTileMapData;
|
||||
return genericPixiTileMapData;
|
||||
};
|
||||
});
|
29
Extensions/TileMap/pixi-tilemap/dist/global-pixi-tilemap.d.ts
vendored
Normal file
29
Extensions/TileMap/pixi-tilemap/dist/global-pixi-tilemap.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
import {
|
||||
CanvasTileRenderer,
|
||||
CompositeRectTileLayer,
|
||||
GraphicsLayer,
|
||||
IMultiTextureOptions,
|
||||
MultiTextureResource,
|
||||
RectTileGeom,
|
||||
RectTileLayer,
|
||||
RectTileShader,
|
||||
TileRenderer,
|
||||
ZLayer,
|
||||
} from './pixi-tilemap';
|
||||
|
||||
declare global {
|
||||
namespace PIXI {
|
||||
export namespace tilemap {
|
||||
export { CanvasTileRenderer };
|
||||
export { CompositeRectTileLayer };
|
||||
export { GraphicsLayer };
|
||||
export { IMultiTextureOptions };
|
||||
export { MultiTextureResource };
|
||||
export { RectTileGeom };
|
||||
export { RectTileLayer };
|
||||
export { RectTileShader };
|
||||
export { TileRenderer };
|
||||
export { ZLayer };
|
||||
}
|
||||
}
|
||||
}
|
262
Extensions/TileMap/pixi-tilemap/dist/pixi-tilemap.d.ts
vendored
Normal file
262
Extensions/TileMap/pixi-tilemap/dist/pixi-tilemap.d.ts
vendored
Normal file
@@ -0,0 +1,262 @@
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
|
||||
export declare class CanvasTileRenderer {
|
||||
renderer: PIXI.Renderer;
|
||||
tileAnim: number[];
|
||||
dontUseTransform: boolean;
|
||||
constructor(renderer: PIXI.Renderer);
|
||||
}
|
||||
|
||||
export declare class CompositeRectTileLayer extends PIXI.Container {
|
||||
constructor(
|
||||
zIndex?: number,
|
||||
bitmaps?: Array<PIXI.Texture>,
|
||||
texPerChild?: number
|
||||
);
|
||||
z: number;
|
||||
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
|
||||
zIndex: number;
|
||||
modificationMarker: number;
|
||||
shadowColor: Float32Array;
|
||||
_globalMat: PIXI.Matrix;
|
||||
_lastLayer: RectTileLayer;
|
||||
texPerChild: number;
|
||||
tileAnim: number[];
|
||||
initialize(
|
||||
zIndex?: number,
|
||||
bitmaps?: Array<PIXI.Texture>,
|
||||
texPerChild?: number
|
||||
): void;
|
||||
setBitmaps(bitmaps: Array<PIXI.Texture>): void;
|
||||
clear(): void;
|
||||
addRect(
|
||||
textureIndex: number,
|
||||
u: number,
|
||||
v: number,
|
||||
x: number,
|
||||
y: number,
|
||||
tileWidth: number,
|
||||
tileHeight: number,
|
||||
animX?: number,
|
||||
animY?: number,
|
||||
rotate?: number,
|
||||
animWidth?: number,
|
||||
animHeight?: number
|
||||
): this;
|
||||
tileRotate(rotate: number): this;
|
||||
tileAnimX(offset: number, count: number): this;
|
||||
tileAnimY(offset: number, count: number): this;
|
||||
addFrame(
|
||||
texture_: PIXI.Texture | String | number,
|
||||
x: number,
|
||||
y: number,
|
||||
animX?: number,
|
||||
animY?: number,
|
||||
animWidth?: number,
|
||||
animHeight?: number
|
||||
): this;
|
||||
renderCanvas(renderer: any): void;
|
||||
render(renderer: PIXI.Renderer): void;
|
||||
isModified(anim: boolean): boolean;
|
||||
clearModify(): void;
|
||||
}
|
||||
|
||||
export declare const Constant: {
|
||||
maxTextures: number;
|
||||
bufferSize: number;
|
||||
boundSize: number;
|
||||
boundCountPerBuffer: number;
|
||||
use32bitIndex: boolean;
|
||||
SCALE_MODE: PIXI.SCALE_MODES;
|
||||
DO_CLEAR: boolean;
|
||||
};
|
||||
|
||||
export declare function fillSamplers(
|
||||
shader: TilemapShader,
|
||||
maxTextures: number
|
||||
): void;
|
||||
|
||||
export declare function generateFragmentSrc(
|
||||
maxTextures: number,
|
||||
fragmentSrc: string
|
||||
): string;
|
||||
|
||||
export declare function generateSampleSrc(maxTextures: number): string;
|
||||
|
||||
export declare class GraphicsLayer extends PIXI.Graphics {
|
||||
constructor(zIndex: number);
|
||||
renderCanvas(renderer: any): void;
|
||||
isModified(anim: boolean): boolean;
|
||||
clearModify(): void;
|
||||
}
|
||||
|
||||
export declare interface IMultiTextureOptions {
|
||||
boundCountPerBuffer: number;
|
||||
boundSize: number;
|
||||
bufferSize: number;
|
||||
DO_CLEAR?: boolean;
|
||||
}
|
||||
|
||||
export declare class MultiTextureResource extends PIXI.Resource {
|
||||
constructor(options: IMultiTextureOptions);
|
||||
DO_CLEAR: boolean;
|
||||
boundSize: number;
|
||||
_clearBuffer: Uint8Array;
|
||||
bind(baseTexture: PIXI.BaseTexture): void;
|
||||
baseTex: PIXI.BaseTexture;
|
||||
boundSprites: Array<PIXI.Sprite>;
|
||||
dirties: Array<number>;
|
||||
setTexture(ind: number, texture: PIXI.Texture): void;
|
||||
upload(
|
||||
renderer: PIXI.Renderer,
|
||||
texture: PIXI.BaseTexture,
|
||||
glTexture: PIXI.GLTexture
|
||||
): boolean;
|
||||
}
|
||||
|
||||
export declare const pixi_tilemap: {
|
||||
CanvasTileRenderer: typeof CanvasTileRenderer;
|
||||
CompositeRectTileLayer: typeof CompositeRectTileLayer;
|
||||
Constant: {
|
||||
maxTextures: number;
|
||||
bufferSize: number;
|
||||
boundSize: number;
|
||||
boundCountPerBuffer: number;
|
||||
use32bitIndex: boolean;
|
||||
SCALE_MODE: PIXI.SCALE_MODES;
|
||||
DO_CLEAR: boolean;
|
||||
};
|
||||
GraphicsLayer: typeof GraphicsLayer;
|
||||
MultiTextureResource: typeof MultiTextureResource;
|
||||
RectTileLayer: typeof RectTileLayer;
|
||||
TilemapShader: typeof TilemapShader;
|
||||
RectTileShader: typeof RectTileShader;
|
||||
RectTileGeom: typeof RectTileGeom;
|
||||
TileRenderer: typeof TileRenderer;
|
||||
ZLayer: typeof ZLayer;
|
||||
};
|
||||
|
||||
export declare const POINT_STRUCT_SIZE = 12;
|
||||
|
||||
export declare class RectTileGeom extends PIXI.Geometry {
|
||||
vertSize: number;
|
||||
vertPerQuad: number;
|
||||
stride: number;
|
||||
lastTimeAccess: number;
|
||||
constructor();
|
||||
buf: PIXI.Buffer;
|
||||
}
|
||||
|
||||
export declare class RectTileLayer extends PIXI.Container {
|
||||
constructor(zIndex: number, texture: PIXI.Texture | Array<PIXI.Texture>);
|
||||
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
|
||||
zIndex: number;
|
||||
modificationMarker: number;
|
||||
_$_localBounds: PIXI.Bounds;
|
||||
shadowColor: Float32Array;
|
||||
_globalMat: PIXI.Matrix;
|
||||
pointsBuf: Array<number>;
|
||||
hasAnim: boolean;
|
||||
textures: Array<PIXI.Texture>;
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
compositeParent: boolean;
|
||||
initialize(
|
||||
zIndex: number,
|
||||
textures: PIXI.Texture | Array<PIXI.Texture>
|
||||
): void;
|
||||
clear(): void;
|
||||
addFrame(
|
||||
texture_: PIXI.Texture | String | number,
|
||||
x: number,
|
||||
y: number,
|
||||
animX: number,
|
||||
animY: number
|
||||
): boolean;
|
||||
addRect(
|
||||
textureIndex: number,
|
||||
u: number,
|
||||
v: number,
|
||||
x: number,
|
||||
y: number,
|
||||
tileWidth: number,
|
||||
tileHeight: number,
|
||||
animX?: number,
|
||||
animY?: number,
|
||||
rotate?: number,
|
||||
animCountX?: number,
|
||||
animCountY?: number
|
||||
): this;
|
||||
tileRotate(rotate: number): void;
|
||||
tileAnimX(offset: number, count: number): void;
|
||||
tileAnimY(offset: number, count: number): void;
|
||||
renderCanvas(renderer: any): void;
|
||||
renderCanvasCore(renderer: any): void;
|
||||
vbId: number;
|
||||
vb: RectTileGeom;
|
||||
vbBuffer: ArrayBuffer;
|
||||
vbArray: Float32Array;
|
||||
vbInts: Uint32Array;
|
||||
destroyVb(): void;
|
||||
render(renderer: PIXI.Renderer): void;
|
||||
renderWebGLCore(renderer: PIXI.Renderer, plugin: TileRenderer): void;
|
||||
isModified(anim: boolean): boolean;
|
||||
clearModify(): void;
|
||||
protected _calculateBounds(): void;
|
||||
getLocalBounds(rect?: PIXI.Rectangle): PIXI.Rectangle;
|
||||
destroy(options?: any): void;
|
||||
}
|
||||
|
||||
export declare class RectTileShader extends TilemapShader {
|
||||
constructor(maxTextures: number);
|
||||
}
|
||||
|
||||
export declare abstract class TilemapShader extends PIXI.Shader {
|
||||
maxTextures: number;
|
||||
constructor(maxTextures: number, shaderVert: string, shaderFrag: string);
|
||||
}
|
||||
|
||||
export declare class TileRenderer extends PIXI.ObjectRenderer {
|
||||
renderer: PIXI.Renderer;
|
||||
gl: WebGLRenderingContext;
|
||||
sn: number;
|
||||
indexBuffer: PIXI.Buffer;
|
||||
ibLen: number;
|
||||
tileAnim: number[];
|
||||
texLoc: Array<number>;
|
||||
rectShader: RectTileShader;
|
||||
texResources: Array<MultiTextureResource>;
|
||||
constructor(renderer: PIXI.Renderer);
|
||||
initBounds(): void;
|
||||
bindTexturesWithoutRT(
|
||||
renderer: PIXI.Renderer,
|
||||
shader: TilemapShader,
|
||||
textures: Array<PIXI.Texture>
|
||||
): void;
|
||||
bindTextures(
|
||||
renderer: PIXI.Renderer,
|
||||
shader: TilemapShader,
|
||||
textures: Array<PIXI.Texture>
|
||||
): void;
|
||||
start(): void;
|
||||
createVb(): RectTileGeom;
|
||||
checkIndexBuffer(size: number, vb?: RectTileGeom): void;
|
||||
getShader(): TilemapShader;
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export declare class ZLayer extends PIXI.Container {
|
||||
constructor(tilemap: PIXI.Container, zIndex: number);
|
||||
tilemap: any;
|
||||
z: number;
|
||||
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
|
||||
zIndex: number;
|
||||
_previousLayers: number;
|
||||
canvasBuffer: HTMLCanvasElement;
|
||||
_tempRender: any;
|
||||
_lastAnimationFrame: number;
|
||||
layerTransform: PIXI.Matrix;
|
||||
clear(): void;
|
||||
cacheIfDirty(): void;
|
||||
renderCanvas(renderer: any): void;
|
||||
}
|
@@ -0,0 +1,238 @@
|
||||
// @ts-check
|
||||
describe('gdjs.TileMapCollisionMaskRuntimeObject', function () {
|
||||
const createScene = (framePerSecond = 60) => {
|
||||
const runtimeGame = new gdjs.RuntimeGame({
|
||||
variables: [],
|
||||
// @ts-ignore - missing properties.
|
||||
properties: { windowWidth: 800, windowHeight: 600 },
|
||||
resources: {
|
||||
resources: [
|
||||
{
|
||||
file: 'base/tests-utils/simple-tiled-map/SmallTiledMap.json',
|
||||
kind: 'json',
|
||||
metadata: '',
|
||||
name: 'SmallTiledMap.json',
|
||||
userAdded: true,
|
||||
alwaysLoaded: true,
|
||||
},
|
||||
{
|
||||
file: 'base/tests-utils/simple-tiled-map/MiniTiledSet.json',
|
||||
kind: 'json',
|
||||
metadata: '',
|
||||
name: 'MiniTiledSet.json',
|
||||
userAdded: true,
|
||||
alwaysLoaded: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
});
|
||||
const runtimeScene = new gdjs.RuntimeScene(runtimeGame);
|
||||
runtimeScene.loadFromScene({
|
||||
layers: [
|
||||
{
|
||||
name: '',
|
||||
visibility: true,
|
||||
effects: [],
|
||||
cameras: [],
|
||||
|
||||
ambientLightColorR: 0,
|
||||
ambientLightColorG: 0,
|
||||
ambientLightColorB: 0,
|
||||
isLightingLayer: false,
|
||||
followBaseLayerCamera: true,
|
||||
},
|
||||
],
|
||||
variables: [],
|
||||
r: 0,
|
||||
v: 0,
|
||||
b: 0,
|
||||
mangledName: 'Scene1',
|
||||
name: 'Scene1',
|
||||
stopSoundsOnStartup: false,
|
||||
title: '',
|
||||
behaviorsSharedData: [],
|
||||
objects: [],
|
||||
instances: [],
|
||||
});
|
||||
setFramesPerSecond(runtimeScene, framePerSecond);
|
||||
return runtimeScene;
|
||||
};
|
||||
const setFramesPerSecond = (runtimeScene, framePerSecond) => {
|
||||
runtimeScene._timeManager.getElapsedTime = function () {
|
||||
return 1000 / framePerSecond;
|
||||
};
|
||||
};
|
||||
|
||||
const addTileMapCollisionMask = (runtimeScene) => {
|
||||
const tileMap = new gdjs.TileMapCollisionMaskRuntimeObject(runtimeScene, {
|
||||
name: 'tilemap',
|
||||
type: 'TileMap::CollisionMask',
|
||||
behaviors: [],
|
||||
effects: [],
|
||||
content: {
|
||||
tilemapJsonFile: 'SmallTiledMap.json',
|
||||
tilesetJsonFile: 'MiniTiledSet.json',
|
||||
layerIndex: 0,
|
||||
collisionMaskTag: 'obstacle',
|
||||
debugMode: false,
|
||||
fillColor: '#ffffff',
|
||||
outlineColor: '#ffffff',
|
||||
fillOpacity: 1,
|
||||
outlineOpacity: 1,
|
||||
outlineSize: 1,
|
||||
},
|
||||
});
|
||||
runtimeScene.addObject(tileMap);
|
||||
return tileMap;
|
||||
};
|
||||
|
||||
const addObject = (runtimeScene) => {
|
||||
const object = new gdjs.TestRuntimeObject(runtimeScene, {
|
||||
name: 'obj1',
|
||||
type: '',
|
||||
behaviors: [],
|
||||
effects: [],
|
||||
variables: [],
|
||||
});
|
||||
object.setCustomWidthAndHeight(8, 8);
|
||||
runtimeScene.addObject(object);
|
||||
return object;
|
||||
};
|
||||
|
||||
const delay = (ms) => new Promise((res) => setTimeout(res, ms));
|
||||
|
||||
let runtimeScene;
|
||||
/**
|
||||
* @type {gdjs.TileMapCollisionMaskRuntimeObject}
|
||||
*/
|
||||
let tileMap;
|
||||
beforeEach(async function () {
|
||||
runtimeScene = createScene();
|
||||
tileMap = addTileMapCollisionMask(runtimeScene);
|
||||
// TODO find a clean way to wait for the json to be read.
|
||||
for (
|
||||
let index = 0;
|
||||
index < 200 && tileMap._collisionTileMap.getDimensionX() === 0;
|
||||
index++
|
||||
) {
|
||||
await delay(5);
|
||||
}
|
||||
if (tileMap._collisionTileMap.getDimensionX() === 0) {
|
||||
throw new Error('Timeout reading the tile map JSON file.');
|
||||
}
|
||||
});
|
||||
|
||||
it('can be measured', function () {
|
||||
tileMap.setPosition(100, 200);
|
||||
|
||||
expect(tileMap.getWidth()).to.be(32);
|
||||
expect(tileMap.getHeight()).to.be(16);
|
||||
expect(tileMap.getCenterX()).to.be(16);
|
||||
expect(tileMap.getCenterY()).to.be(8);
|
||||
});
|
||||
|
||||
/**
|
||||
* insideObject usually use the AABB of the object.
|
||||
* But, in case of a tile map, it makes more sense to look each tile individually.
|
||||
* It returns true when there is an hitbox in the tile.
|
||||
*/
|
||||
it('can detect a point inside the collision mask', function () {
|
||||
tileMap.setPosition(100, 200);
|
||||
|
||||
// The point is in the black square with an hitbox.
|
||||
expect(tileMap.insideObject(104, 204)).to.be(true);
|
||||
expect(tileMap.isCollidingWithPoint(104, 204)).to.be(true);
|
||||
|
||||
// The point is in wite square without any hitbox.
|
||||
expect(tileMap.insideObject(112, 212)).to.be(false);
|
||||
expect(tileMap.isCollidingWithPoint(112, 212)).to.be(false);
|
||||
|
||||
// The point is in black triangle part of the square that has an hitbox.
|
||||
expect(tileMap.insideObject(102, 210)).to.be(true);
|
||||
expect(tileMap.isCollidingWithPoint(102, 210)).to.be(true);
|
||||
|
||||
// The point is in white triangle part of the square that has no hitbox.
|
||||
expect(tileMap.insideObject(106, 214)).to.be(true);
|
||||
expect(tileMap.isCollidingWithPoint(106, 214)).to.be(false);
|
||||
});
|
||||
|
||||
it('can detect collisions with an object', function () {
|
||||
tileMap.setPosition(100, 200);
|
||||
|
||||
const object = addObject(runtimeScene);
|
||||
|
||||
object.setPosition(96, 196);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
|
||||
|
||||
object.setPosition(90, 190);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
|
||||
false
|
||||
);
|
||||
|
||||
object.setPosition(115, 207);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
|
||||
|
||||
object.setPosition(116, 208);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('can check collisions with an object on empty tiles without any issue', function () {
|
||||
tileMap.setPosition(100, 200);
|
||||
|
||||
const object = addObject(runtimeScene);
|
||||
|
||||
object.setPosition(116, 208);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('can detect collisions with an object on flipped tiles', function () {
|
||||
tileMap.setPosition(100, 200);
|
||||
|
||||
const object = addObject(runtimeScene);
|
||||
|
||||
// The object is over the black triangle.
|
||||
object.setPosition(118, 214);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
|
||||
|
||||
// The object is over the red triangle without touching a black polygon.
|
||||
object.setPosition(130, 204);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it("can detect collisions with an object when it's rotated", function () {
|
||||
tileMap.setPosition(100, 200);
|
||||
tileMap.setAngle(90);
|
||||
|
||||
const object = addObject(runtimeScene);
|
||||
|
||||
object.setPosition(123, 185);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
|
||||
|
||||
object.setPosition(124, 184);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
|
||||
false
|
||||
);
|
||||
});
|
||||
|
||||
it('can detect collisions with an object when it has a custom size', function () {
|
||||
tileMap.setPosition(100, 200);
|
||||
tileMap.setWidth(2 * tileMap.getWidth());
|
||||
tileMap.setHeight(2 * tileMap.getHeight());
|
||||
|
||||
const object = addObject(runtimeScene);
|
||||
|
||||
object.setPosition(163, 231);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(true);
|
||||
|
||||
object.setPosition(164, 232);
|
||||
expect(gdjs.RuntimeObject.collisionTest(object, tileMap, true)).to.be(
|
||||
false
|
||||
);
|
||||
});
|
||||
});
|
472
Extensions/TileMap/tilemapcollisionmaskruntimeobject.ts
Normal file
472
Extensions/TileMap/tilemapcollisionmaskruntimeobject.ts
Normal file
@@ -0,0 +1,472 @@
|
||||
/// <reference path="helper/TileMapHelper.d.ts" />
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Tilemap object');
|
||||
|
||||
/**
|
||||
* An object that handle hitboxes for a tile map.
|
||||
* @extends gdjs.RuntimeObject
|
||||
*/
|
||||
export class TileMapCollisionMaskRuntimeObject extends gdjs.RuntimeObject {
|
||||
private _tilemapJsonFile: string;
|
||||
private _tilesetJsonFile: string;
|
||||
private _renderer: gdjs.TileMap.TileMapCollisionMaskRenderer;
|
||||
_collisionTileMap: gdjs.TileMap.TransformedCollisionTileMap;
|
||||
/**
|
||||
* The tiles are filtered according to this tag.
|
||||
*
|
||||
* This allows have multiple objects with different usage
|
||||
* for the same tile map.
|
||||
* For instance, platforms, jumpthru, ladder, spike, water...
|
||||
*/
|
||||
private _collisionMaskTag: string;
|
||||
private _tileMapManager: gdjs.TileMap.TileMapRuntimeManager;
|
||||
|
||||
/**
|
||||
* When set to true, the hitboxes will be shown.
|
||||
*/
|
||||
_debugMode: boolean;
|
||||
_fillColor: integer;
|
||||
_outlineColor: integer;
|
||||
_fillOpacity: float;
|
||||
_outlineOpacity: float;
|
||||
_outlineSize: float;
|
||||
|
||||
/**
|
||||
* If the owner moves, the hitboxes vertices
|
||||
* will have to be transformed again.
|
||||
*/
|
||||
private _transformationIsUpToDate: boolean = false;
|
||||
|
||||
constructor(runtimeScene: gdjs.RuntimeScene, objectData) {
|
||||
super(runtimeScene, objectData);
|
||||
this._tilemapJsonFile = objectData.content.tilemapJsonFile;
|
||||
this._tilesetJsonFile = objectData.content.tilesetJsonFile;
|
||||
this._collisionMaskTag = objectData.content.collisionMaskTag;
|
||||
this._debugMode = objectData.content.debugMode;
|
||||
this._fillColor = gdjs.rgbOrHexStringToNumber(
|
||||
objectData.content.fillColor
|
||||
);
|
||||
this._outlineColor = gdjs.rgbOrHexStringToNumber(
|
||||
objectData.content.outlineColor
|
||||
);
|
||||
this._fillOpacity = objectData.content.fillOpacity;
|
||||
this._outlineOpacity = objectData.content.outlineOpacity;
|
||||
this._outlineSize = objectData.content.outlineSize;
|
||||
this._tileMapManager = gdjs.TileMap.TileMapRuntimeManager.getManager(
|
||||
runtimeScene
|
||||
);
|
||||
const editableTileMap = new TileMapHelper.EditableTileMap(
|
||||
1,
|
||||
1,
|
||||
0,
|
||||
0,
|
||||
new Map()
|
||||
);
|
||||
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
|
||||
editableTileMap,
|
||||
this._collisionMaskTag
|
||||
);
|
||||
this._renderer = new gdjs.TileMap.TileMapCollisionMaskRenderer(
|
||||
this,
|
||||
runtimeScene
|
||||
);
|
||||
this._updateTileMap();
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
}
|
||||
|
||||
getRendererObject() {
|
||||
return this._renderer.getRendererObject();
|
||||
}
|
||||
|
||||
getVisibilityAABB() {
|
||||
return null;
|
||||
}
|
||||
|
||||
updateFromObjectData(oldObjectData: any, newObjectData: any): boolean {
|
||||
if (
|
||||
oldObjectData.content.tilemapJsonFile !==
|
||||
newObjectData.content.tilemapJsonFile
|
||||
) {
|
||||
this.setTilemapJsonFile(newObjectData.content.tilemapJsonFile);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.tilesetJsonFile !==
|
||||
newObjectData.content.tilesetJsonFile
|
||||
) {
|
||||
this.setTilesetJsonFile(newObjectData.content.tilesetJsonFile);
|
||||
}
|
||||
if (oldObjectData.content.debugMode !== newObjectData.content.debugMode) {
|
||||
this.setDebugMode(newObjectData.content.debugMode);
|
||||
}
|
||||
if (oldObjectData.content.fillColor !== newObjectData.content.fillColor) {
|
||||
this.setFillColor(
|
||||
gdjs.rgbOrHexStringToNumber(newObjectData.content.fillColor)
|
||||
);
|
||||
}
|
||||
if (
|
||||
oldObjectData.content.outlineColor !==
|
||||
newObjectData.content.outlineColor
|
||||
) {
|
||||
this.setOutlineColor(
|
||||
gdjs.rgbOrHexStringToNumber(newObjectData.content.outlineColor)
|
||||
);
|
||||
}
|
||||
if (oldObjectData.fillOpacity !== newObjectData.fillOpacity) {
|
||||
this.setFillOpacity(newObjectData.fillOpacity);
|
||||
}
|
||||
if (oldObjectData.outlineOpacity !== newObjectData.outlineOpacity) {
|
||||
this.setOutlineOpacity(newObjectData.outlineOpacity);
|
||||
}
|
||||
if (oldObjectData.outlineSize !== newObjectData.outlineSize) {
|
||||
this.setOutlineSize(newObjectData.outlineSize);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
extraInitializationFromInitialInstance(initialInstanceData): void {
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateTileMap(): void {
|
||||
this._tileMapManager.getOrLoadTileMap(
|
||||
this._tilemapJsonFile,
|
||||
this._tilesetJsonFile,
|
||||
(tileMap: TileMapHelper.EditableTileMap | null) => {
|
||||
if (!tileMap) {
|
||||
// getOrLoadTileMap already log errors.
|
||||
return;
|
||||
}
|
||||
|
||||
this._collisionTileMap = new gdjs.TileMap.TransformedCollisionTileMap(
|
||||
tileMap,
|
||||
this._collisionMaskTag
|
||||
);
|
||||
// The tile map polygons always keep the same references.
|
||||
// It works because the tilemap is never modified.
|
||||
this.hitBoxes = Array.from(
|
||||
this._collisionTileMap.getAllHitboxes(this._collisionMaskTag)
|
||||
);
|
||||
this._renderer.redrawCollisionMask();
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
updateHitBoxes(): void {
|
||||
this.updateTransformation();
|
||||
// Update the RuntimeObject hitboxes attribute.
|
||||
for (const hitboxes of this._collisionTileMap.getAllHitboxes(
|
||||
this._collisionMaskTag
|
||||
)) {
|
||||
// RuntimeObject.hitBoxes contains the same polygons instances as the
|
||||
// hitboxes from the tiles.
|
||||
//
|
||||
// When hitboxes for a tile is asked to the model, they are updated
|
||||
// according to the new object location if needed.
|
||||
// Iterating over all the tiles forces them to update their hitboxes.
|
||||
//
|
||||
// The hitboxes array is built by _updateTileMap().
|
||||
}
|
||||
this.hitBoxesDirty = false;
|
||||
this._renderer.redrawCollisionMask();
|
||||
this.updateAABB();
|
||||
}
|
||||
|
||||
/**
|
||||
* Update the affine transformation according to the object position, size
|
||||
* and angle.
|
||||
*/
|
||||
updateTransformation(): void {
|
||||
if (this._transformationIsUpToDate) {
|
||||
return;
|
||||
}
|
||||
const transformation = this._collisionTileMap.getTransformation();
|
||||
|
||||
const absScaleX = Math.abs(this._renderer.getScaleX());
|
||||
const absScaleY = Math.abs(this._renderer.getScaleY());
|
||||
|
||||
transformation.setToIdentity();
|
||||
|
||||
// Translation
|
||||
transformation.translate(this.x, this.y);
|
||||
|
||||
// Rotation
|
||||
const angleInRadians = (this.angle * Math.PI) / 180;
|
||||
transformation.rotateAround(
|
||||
angleInRadians,
|
||||
this.getCenterX() * absScaleX,
|
||||
this.getCenterY() * absScaleY
|
||||
);
|
||||
|
||||
// Scale
|
||||
transformation.scale(absScaleX, absScaleY);
|
||||
|
||||
this._collisionTileMap.setTransformation(transformation);
|
||||
|
||||
this._transformationIsUpToDate = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* This method is expensive and should not be called.
|
||||
* Prefer using {@link getHitBoxesAround} rather than getHitBoxes.
|
||||
*/
|
||||
getHitBoxes(): gdjs.Polygon[] {
|
||||
if (this.hitBoxesDirty) {
|
||||
this.updateHitBoxes();
|
||||
this.updateAABB();
|
||||
this.hitBoxesDirty = false;
|
||||
}
|
||||
return this.hitBoxes;
|
||||
}
|
||||
|
||||
getHitBoxesAround(left: float, top: float, right: float, bottom: float) {
|
||||
// This implementation doesn't call updateHitBoxes.
|
||||
// It's important for good performances because there is no need to
|
||||
// update the whole collision mask where only a few hitboxes must be
|
||||
// checked.
|
||||
this.updateTransformation();
|
||||
return this._collisionTileMap.getHitboxesAround(
|
||||
this._collisionMaskTag,
|
||||
left,
|
||||
top,
|
||||
right,
|
||||
bottom
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* insideObject usually use the AABB of the object.
|
||||
* But, in case of a tile map, it makes more sense to look each tile individually.
|
||||
* It returns true when there is an hitbox in the tile.
|
||||
*/
|
||||
insideObject(x: float, y: float): boolean {
|
||||
this.updateTransformation();
|
||||
// This is more precise than the default implementation.
|
||||
return this._collisionTileMap.pointIsInsideTile(
|
||||
x,
|
||||
y,
|
||||
this._collisionMaskTag
|
||||
);
|
||||
}
|
||||
|
||||
// This implementation doesn't use updateHitBoxes.
|
||||
// It's important for good performances.
|
||||
updateAABB(): void {
|
||||
if (this.getAngle() === 0) {
|
||||
// Fast computation of AABB for non rotated object
|
||||
this.aabb.min[0] = this.x;
|
||||
this.aabb.min[1] = this.y;
|
||||
this.aabb.max[0] = this.aabb.min[0] + this.getWidth();
|
||||
this.aabb.max[1] = this.aabb.min[1] + this.getHeight();
|
||||
} else {
|
||||
const affineTransformation = this._collisionTileMap.getTransformation();
|
||||
|
||||
const left = 0;
|
||||
const right = this._collisionTileMap.getWidth();
|
||||
const top = 0;
|
||||
const bottom = this._collisionTileMap.getHeight();
|
||||
|
||||
const workingPoint = this.aabb.min;
|
||||
|
||||
workingPoint[0] = left;
|
||||
workingPoint[1] = top;
|
||||
affineTransformation.transform(workingPoint, workingPoint);
|
||||
const topLeftX = workingPoint[0];
|
||||
const topLeftY = workingPoint[1];
|
||||
|
||||
workingPoint[0] = right;
|
||||
workingPoint[1] = top;
|
||||
affineTransformation.transform(workingPoint, workingPoint);
|
||||
const topRightX = workingPoint[0];
|
||||
const topRightY = workingPoint[1];
|
||||
|
||||
workingPoint[0] = right;
|
||||
workingPoint[1] = bottom;
|
||||
affineTransformation.transform(workingPoint, workingPoint);
|
||||
const bottomRightX = workingPoint[0];
|
||||
const bottomRightY = workingPoint[1];
|
||||
|
||||
workingPoint[0] = left;
|
||||
workingPoint[1] = bottom;
|
||||
affineTransformation.transform(workingPoint, workingPoint);
|
||||
const bottomLeftX = workingPoint[0];
|
||||
const bottomLeftY = workingPoint[1];
|
||||
|
||||
this.aabb.min[0] = Math.min(
|
||||
topLeftX,
|
||||
topRightX,
|
||||
bottomRightX,
|
||||
bottomLeftX
|
||||
);
|
||||
this.aabb.max[0] = Math.max(
|
||||
topLeftX,
|
||||
topRightX,
|
||||
bottomRightX,
|
||||
bottomLeftX
|
||||
);
|
||||
this.aabb.min[1] = Math.min(
|
||||
topLeftY,
|
||||
topRightY,
|
||||
bottomRightY,
|
||||
bottomLeftY
|
||||
);
|
||||
this.aabb.max[1] = Math.max(
|
||||
topLeftY,
|
||||
topRightY,
|
||||
bottomRightY,
|
||||
bottomLeftY
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Tilemap json file to display.
|
||||
*/
|
||||
setTilemapJsonFile(tilemapJsonFile: string): void {
|
||||
this._tilemapJsonFile = tilemapJsonFile;
|
||||
this._updateTileMap();
|
||||
}
|
||||
|
||||
getTilemapJsonFile(): string {
|
||||
return this._tilemapJsonFile;
|
||||
}
|
||||
|
||||
isTilemapJsonFile(selectedTilemapJsonFile: string): boolean {
|
||||
return this._tilemapJsonFile === selectedTilemapJsonFile;
|
||||
}
|
||||
|
||||
setTilesetJsonFile(tilesetJsonFile: string) {
|
||||
this._tilesetJsonFile = tilesetJsonFile;
|
||||
this._updateTileMap();
|
||||
}
|
||||
|
||||
getTilesetJsonFile(): string {
|
||||
return this._tilesetJsonFile;
|
||||
}
|
||||
|
||||
isTilesetJsonFile(selectedTilesetJsonFile: string): boolean {
|
||||
return this._tilesetJsonFile === selectedTilesetJsonFile;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the hitboxes are shown.
|
||||
*/
|
||||
getDebugMode(): boolean {
|
||||
return this._debugMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the hitboxes are shown.
|
||||
*/
|
||||
setDebugMode(debugMode: boolean): void {
|
||||
this._debugMode = debugMode;
|
||||
this._renderer.redrawCollisionMask();
|
||||
}
|
||||
|
||||
getFillColor(): integer {
|
||||
return this._fillColor;
|
||||
}
|
||||
|
||||
getOutlineColor(): integer {
|
||||
return this._outlineColor;
|
||||
}
|
||||
|
||||
setFillColor(fillColor: integer): void {
|
||||
this._fillColor = fillColor;
|
||||
}
|
||||
|
||||
setOutlineColor(outlineColor: integer): void {
|
||||
this._outlineColor = outlineColor;
|
||||
}
|
||||
|
||||
setOutlineSize(size: float): void {
|
||||
this._outlineSize = size;
|
||||
}
|
||||
|
||||
getOutlineSize(): float {
|
||||
return this._outlineSize;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param opacity from 0 to 255
|
||||
*/
|
||||
setFillOpacity(opacity: float): void {
|
||||
this._fillOpacity = opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns an opacity value from 0 to 255.
|
||||
*/
|
||||
getFillOpacity(): float {
|
||||
return this._fillOpacity;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param opacity from 0 to 255
|
||||
*/
|
||||
setOutlineOpacity(opacity: float): void {
|
||||
this._outlineOpacity = opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @returns an opacity value from 0 to 255.
|
||||
*/
|
||||
getOutlineOpacity(): float {
|
||||
return this._outlineOpacity;
|
||||
}
|
||||
|
||||
setX(x: float): void {
|
||||
super.setX(x);
|
||||
this._transformationIsUpToDate = false;
|
||||
}
|
||||
|
||||
setY(y: float): void {
|
||||
super.setY(y);
|
||||
this._transformationIsUpToDate = false;
|
||||
}
|
||||
|
||||
setAngle(angle: float): void {
|
||||
super.setAngle(angle);
|
||||
this._transformationIsUpToDate = false;
|
||||
}
|
||||
|
||||
// TODO allow size changes from events?
|
||||
|
||||
setWidth(width: float): void {
|
||||
if (this._renderer.getWidth() === width) return;
|
||||
|
||||
this._renderer.setWidth(width);
|
||||
this.hitBoxesDirty = true;
|
||||
this._transformationIsUpToDate = false;
|
||||
}
|
||||
|
||||
setHeight(height: float): void {
|
||||
if (this._renderer.getHeight() === height) return;
|
||||
|
||||
this._renderer.setHeight(height);
|
||||
this.hitBoxesDirty = true;
|
||||
this._transformationIsUpToDate = false;
|
||||
}
|
||||
|
||||
getWidth(): float {
|
||||
return this._renderer.getWidth();
|
||||
}
|
||||
|
||||
getHeight(): float {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
}
|
||||
gdjs.registerObject(
|
||||
'TileMap::CollisionMask',
|
||||
gdjs.TileMapCollisionMaskRuntimeObject
|
||||
);
|
||||
TileMapCollisionMaskRuntimeObject.supportsReinitialization = false;
|
||||
}
|
@@ -1,3 +1,5 @@
|
||||
/// <reference path="helper/TileMapHelper.d.ts" />
|
||||
/// <reference path="pixi-tilemap/dist/pixi-tilemap.d.ts" />
|
||||
namespace gdjs {
|
||||
const logger = new gdjs.Logger('Tilemap object');
|
||||
|
||||
@@ -10,8 +12,7 @@ namespace gdjs {
|
||||
_object: any;
|
||||
_runtimeScene: gdjs.RuntimeScene;
|
||||
|
||||
// @ts-ignore - pixi-tilemap types to be added.
|
||||
_pixiObject: any;
|
||||
_pixiObject: PIXI.tilemap.CompositeRectTileLayer;
|
||||
|
||||
/**
|
||||
* @param runtimeObject The object to render
|
||||
@@ -25,10 +26,7 @@ namespace gdjs {
|
||||
this._runtimeScene = runtimeScene;
|
||||
|
||||
// Load (or reset)
|
||||
if (this._pixiObject === undefined) {
|
||||
// @ts-ignore - pixi-tilemap types to be added.
|
||||
this._pixiObject = new PIXI.tilemap.CompositeRectTileLayer(0);
|
||||
}
|
||||
this._pixiObject = new PIXI.tilemap.CompositeRectTileLayer(0);
|
||||
this._pixiObject.tileAnim = [0, 0];
|
||||
|
||||
runtimeScene
|
||||
@@ -37,7 +35,6 @@ namespace gdjs {
|
||||
.addRendererObject(this._pixiObject, runtimeObject.getZOrder());
|
||||
this.updateAngle();
|
||||
this.updateOpacity();
|
||||
this.updateTileMap();
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
@@ -45,74 +42,21 @@ namespace gdjs {
|
||||
return this._pixiObject;
|
||||
}
|
||||
|
||||
incrementAnimationFrameX(runtimeScene) {
|
||||
incrementAnimationFrameX(runtimeScene: gdjs.RuntimeScene) {
|
||||
this._pixiObject.tileAnim[0] += 1;
|
||||
}
|
||||
|
||||
_loadTileMapWithTileset(tileMapJsonData, tilesetJsonData) {
|
||||
// @ts-ignore - TODO: Add typings for pixi-tilemap-helper.
|
||||
const pixiTileMapData = PixiTileMapHelper.loadPixiTileMapData(
|
||||
(textureName) =>
|
||||
this._runtimeScene
|
||||
.getGame()
|
||||
.getImageManager()
|
||||
.getPIXITexture(textureName),
|
||||
tilesetJsonData
|
||||
? { ...tileMapJsonData, tilesets: [tilesetJsonData] }
|
||||
: tileMapJsonData,
|
||||
this._object._tilemapAtlasImage,
|
||||
this._object._tilemapJsonFile,
|
||||
this._object._tilesetJsonFile
|
||||
updatePixiTileMap(
|
||||
tileMap: TileMapHelper.EditableTileMap,
|
||||
textureCache: TileMapHelper.TileTextureCache
|
||||
) {
|
||||
TileMapHelper.PixiTileMapHelper.updatePixiTileMap(
|
||||
this._pixiObject,
|
||||
tileMap,
|
||||
textureCache,
|
||||
this._object._displayMode,
|
||||
this._object._layerIndex
|
||||
);
|
||||
if (pixiTileMapData) {
|
||||
// @ts-ignore - TODO: Add typings for pixi-tilemap-helper.
|
||||
PixiTileMapHelper.updatePixiTileMap(
|
||||
this._pixiObject,
|
||||
pixiTileMapData,
|
||||
this._object._displayMode,
|
||||
this._object._layerIndex,
|
||||
// @ts-ignore - TODO: Add typings for pako.
|
||||
pako
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
updateTileMap(): void {
|
||||
this._runtimeScene
|
||||
.getGame()
|
||||
.getJsonManager()
|
||||
.loadJson(this._object._tilemapJsonFile, (error, tileMapJsonData) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
'An error happened while loading a Tilemap JSON data:',
|
||||
error
|
||||
);
|
||||
return;
|
||||
}
|
||||
if (this._object._tilesetJsonFile) {
|
||||
this._runtimeScene
|
||||
.getGame()
|
||||
.getJsonManager()
|
||||
.loadJson(
|
||||
this._object._tilesetJsonFile,
|
||||
(error, tilesetJsonData) => {
|
||||
if (error) {
|
||||
logger.error(
|
||||
'An error happened while loading Tileset JSON data:',
|
||||
error
|
||||
);
|
||||
return;
|
||||
}
|
||||
this._loadTileMapWithTileset(
|
||||
tileMapJsonData,
|
||||
tilesetJsonData
|
||||
);
|
||||
}
|
||||
);
|
||||
} else {
|
||||
this._loadTileMapWithTileset(tileMapJsonData, null);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
updatePosition(): void {
|
||||
@@ -135,13 +79,13 @@ namespace gdjs {
|
||||
this._pixiObject.alpha = this._object._opacity / 255;
|
||||
}
|
||||
|
||||
setWidth(width): void {
|
||||
setWidth(width: float): void {
|
||||
this._pixiObject.width = width / this._pixiObject.scale.x;
|
||||
this._pixiObject.pivot.x = width / 2;
|
||||
this.updatePosition();
|
||||
}
|
||||
|
||||
setHeight(height): void {
|
||||
setHeight(height: float): void {
|
||||
this._pixiObject.height = height / this._pixiObject.scale.y;
|
||||
this._pixiObject.pivot.y = height / 2;
|
||||
this.updatePosition();
|
||||
|
@@ -1,9 +1,10 @@
|
||||
/// <reference path="helper/TileMapHelper.d.ts" />
|
||||
namespace gdjs {
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
|
||||
const logger = new gdjs.Logger('Tilemap object');
|
||||
/**
|
||||
* Displays a Tilemap object (mapeditor.org supported).
|
||||
* @memberof gdjs
|
||||
* @class TileMapRuntimeObject
|
||||
* @extends gdjs.RuntimeObject
|
||||
*/
|
||||
export class TileMapRuntimeObject extends gdjs.RuntimeObject {
|
||||
_frameElapsedTime: float = 0;
|
||||
@@ -15,9 +16,10 @@ namespace gdjs {
|
||||
_layerIndex: integer;
|
||||
_animationSpeedScale: number;
|
||||
_animationFps: number;
|
||||
_renderer: any;
|
||||
_tileMapManager: gdjs.TileMap.TileMapRuntimeManager;
|
||||
_renderer: gdjs.TileMapRuntimeObjectPixiRenderer;
|
||||
|
||||
constructor(runtimeScene, objectData) {
|
||||
constructor(runtimeScene: gdjs.RuntimeScene, objectData) {
|
||||
super(runtimeScene, objectData);
|
||||
this._opacity = objectData.content.opacity;
|
||||
this._tilemapJsonFile = objectData.content.tilemapJsonFile;
|
||||
@@ -27,18 +29,14 @@ namespace gdjs {
|
||||
this._layerIndex = objectData.content.layerIndex;
|
||||
this._animationSpeedScale = objectData.content.animationSpeedScale;
|
||||
this._animationFps = objectData.content.animationFps;
|
||||
if (this._renderer) {
|
||||
gdjs.TileMapRuntimeObjectRenderer.call(
|
||||
this._renderer,
|
||||
this,
|
||||
runtimeScene
|
||||
);
|
||||
} else {
|
||||
this._renderer = new gdjs.TileMapRuntimeObjectRenderer(
|
||||
this,
|
||||
runtimeScene
|
||||
);
|
||||
}
|
||||
this._tileMapManager = gdjs.TileMap.TileMapRuntimeManager.getManager(
|
||||
runtimeScene
|
||||
);
|
||||
this._renderer = new gdjs.TileMapRuntimeObjectRenderer(
|
||||
this,
|
||||
runtimeScene
|
||||
);
|
||||
this._updateTileMap();
|
||||
|
||||
// *ALWAYS* call `this.onCreated()` at the very end of your object constructor.
|
||||
this.onCreated();
|
||||
@@ -48,14 +46,14 @@ namespace gdjs {
|
||||
return this._renderer.getRendererObject();
|
||||
}
|
||||
|
||||
update(runtimeScene): void {
|
||||
update(runtimeScene: gdjs.RuntimeScene): void {
|
||||
if (this._animationSpeedScale <= 0 || this._animationFps === 0) {
|
||||
return;
|
||||
}
|
||||
const elapsedTime = this.getElapsedTime(runtimeScene) / 1000;
|
||||
this._frameElapsedTime += elapsedTime * this._animationSpeedScale;
|
||||
while (this._frameElapsedTime > 1 / this._animationFps) {
|
||||
this._renderer.incrementAnimationFrameX();
|
||||
this._renderer.incrementAnimationFrameX(runtimeScene);
|
||||
this._frameElapsedTime -= 1 / this._animationFps;
|
||||
}
|
||||
}
|
||||
@@ -108,67 +106,101 @@ namespace gdjs {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialize the extra parameters that could be set for an instance.
|
||||
*/
|
||||
extraInitializationFromInitialInstance(initialInstanceData) {
|
||||
extraInitializationFromInitialInstance(initialInstanceData): void {
|
||||
if (initialInstanceData.customSize) {
|
||||
this.setWidth(initialInstanceData.width);
|
||||
this.setHeight(initialInstanceData.height);
|
||||
}
|
||||
}
|
||||
|
||||
private _updateTileMap(): void {
|
||||
this._tileMapManager.getOrLoadTileMap(
|
||||
this._tilemapJsonFile,
|
||||
this._tilesetJsonFile,
|
||||
(tileMap: TileMapHelper.EditableTileMap | null) => {
|
||||
if (!tileMap) {
|
||||
// getOrLoadTileMap already warn.
|
||||
return;
|
||||
}
|
||||
this._tileMapManager.getOrLoadTextureCache(
|
||||
(textureName) =>
|
||||
(this._runtimeScene
|
||||
.getGame()
|
||||
.getImageManager()
|
||||
.getPIXITexture(textureName) as unknown) as PIXI.BaseTexture<
|
||||
PIXI.Resource
|
||||
>,
|
||||
this._tilemapAtlasImage,
|
||||
this._tilemapJsonFile,
|
||||
this._tilesetJsonFile,
|
||||
(textureCache: TileMapHelper.TileTextureCache | null) => {
|
||||
if (!textureCache) {
|
||||
// getOrLoadTextureCache already log warns and errors.
|
||||
return;
|
||||
}
|
||||
this._renderer.updatePixiTileMap(tileMap, textureCache);
|
||||
}
|
||||
);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the Tilemap json file to display.
|
||||
*/
|
||||
setTilemapJsonFile(tilemapJsonFile): void {
|
||||
setTilemapJsonFile(tilemapJsonFile: string): void {
|
||||
this._tilemapJsonFile = tilemapJsonFile;
|
||||
this._renderer.updateTileMap();
|
||||
this._updateTileMap();
|
||||
}
|
||||
|
||||
getTilemapJsonFile() {
|
||||
getTilemapJsonFile(): string {
|
||||
return this._tilemapJsonFile;
|
||||
}
|
||||
|
||||
isTilemapJsonFile(selectedTilemapJsonFile): boolean {
|
||||
isTilemapJsonFile(selectedTilemapJsonFile: string): boolean {
|
||||
return this._tilemapJsonFile === selectedTilemapJsonFile;
|
||||
}
|
||||
|
||||
setTilesetJsonFile(tilesetJsonFile) {
|
||||
setTilesetJsonFile(tilesetJsonFile: string): void {
|
||||
this._tilesetJsonFile = tilesetJsonFile;
|
||||
this._renderer.updateTileMap();
|
||||
this._updateTileMap();
|
||||
}
|
||||
getTilesetJsonFile() {
|
||||
|
||||
getTilesetJsonFile(): string {
|
||||
return this._tilesetJsonFile;
|
||||
}
|
||||
setAnimationFps(animationFps) {
|
||||
|
||||
setAnimationFps(animationFps: float) {
|
||||
this._animationFps = animationFps;
|
||||
}
|
||||
getAnimationFps() {
|
||||
|
||||
getAnimationFps(): float {
|
||||
return this._animationFps;
|
||||
}
|
||||
isTilesetJsonFile(selectedTilesetJsonFile) {
|
||||
|
||||
isTilesetJsonFile(selectedTilesetJsonFile: string): boolean {
|
||||
return this._tilesetJsonFile === selectedTilesetJsonFile;
|
||||
}
|
||||
isDisplayMode(selectedDisplayMode) {
|
||||
|
||||
isDisplayMode(selectedDisplayMode: string): boolean {
|
||||
return this._displayMode === selectedDisplayMode;
|
||||
}
|
||||
|
||||
setDisplayMode(displayMode): void {
|
||||
setDisplayMode(displayMode: string): void {
|
||||
this._displayMode = displayMode;
|
||||
this._renderer.updateTileMap();
|
||||
this._updateTileMap();
|
||||
}
|
||||
|
||||
getDisplayMode() {
|
||||
getDisplayMode(): string {
|
||||
return this._displayMode;
|
||||
}
|
||||
|
||||
setLayerIndex(layerIndex): void {
|
||||
this._layerIndex = layerIndex;
|
||||
this._renderer.updateTileMap();
|
||||
this._updateTileMap();
|
||||
}
|
||||
|
||||
getLayerIndex() {
|
||||
getLayerIndex(): integer {
|
||||
return this._layerIndex;
|
||||
}
|
||||
|
||||
@@ -176,14 +208,10 @@ namespace gdjs {
|
||||
this._animationSpeedScale = animationSpeedScale;
|
||||
}
|
||||
|
||||
getAnimationSpeedScale() {
|
||||
getAnimationSpeedScale(): float {
|
||||
return this._animationSpeedScale;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the width of the object.
|
||||
* @param width The new width.
|
||||
*/
|
||||
setWidth(width: float): void {
|
||||
if (this._renderer.getWidth() === width) return;
|
||||
|
||||
@@ -191,10 +219,6 @@ namespace gdjs {
|
||||
this.hitBoxesDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the height of the object.
|
||||
* @param height The new height.
|
||||
*/
|
||||
setHeight(height: float): void {
|
||||
if (this._renderer.getHeight() === height) return;
|
||||
|
||||
@@ -202,28 +226,16 @@ namespace gdjs {
|
||||
this.hitBoxesDirty = true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object position on X axis.
|
||||
* @param x The new position X of the object.
|
||||
*/
|
||||
setX(x: float): void {
|
||||
super.setX(x);
|
||||
this._renderer.updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set object position on Y axis.
|
||||
* @param y The new position Y of the object.
|
||||
*/
|
||||
setY(y: float): void {
|
||||
super.setY(y);
|
||||
this._renderer.updatePosition();
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the angle of the object.
|
||||
* @param angle The new angle of the object.
|
||||
*/
|
||||
setAngle(angle: float): void {
|
||||
super.setAngle(angle);
|
||||
this._renderer.updateAngle();
|
||||
@@ -241,20 +253,14 @@ namespace gdjs {
|
||||
/**
|
||||
* Get object opacity.
|
||||
*/
|
||||
getOpacity() {
|
||||
getOpacity(): float {
|
||||
return this._opacity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the width of the object.
|
||||
*/
|
||||
getWidth(): float {
|
||||
return this._renderer.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the height of the object.
|
||||
*/
|
||||
getHeight(): float {
|
||||
return this._renderer.getHeight();
|
||||
}
|
||||
|
@@ -553,6 +553,7 @@ void ExporterHelper::AddLibsInclude(bool pixiRenderers,
|
||||
InsertUnique(includesFiles, "oncetriggers.js");
|
||||
InsertUnique(includesFiles, "runtimebehavior.js");
|
||||
InsertUnique(includesFiles, "spriteruntimeobject.js");
|
||||
InsertUnique(includesFiles, "affinetransformation.js");
|
||||
|
||||
// Common includes for events only.
|
||||
InsertUnique(includesFiles, "events-tools/commontools.js");
|
||||
|
493
GDJS/Runtime/affinetransformation.ts
Normal file
493
GDJS/Runtime/affinetransformation.ts
Normal file
@@ -0,0 +1,493 @@
|
||||
namespace gdjs {
|
||||
/**
|
||||
* An affine transformation that can transform points.
|
||||
*/
|
||||
export class AffineTransformation {
|
||||
private matrix: Float32Array;
|
||||
|
||||
/**
|
||||
* Initialize to the identity.
|
||||
*/
|
||||
constructor() {
|
||||
// | 1 0 0 |
|
||||
// | 0 1 0 |
|
||||
// | 0 0 1 |
|
||||
this.matrix = new Float32Array([1, 0, 0, 1, 0, 0]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to the identity.
|
||||
*/
|
||||
setToIdentity() {
|
||||
const matrix = this.matrix;
|
||||
// | 1 0 0 |
|
||||
// | 0 1 0 |
|
||||
// | 0 0 1 |
|
||||
matrix[0] = 1;
|
||||
matrix[1] = 0;
|
||||
matrix[2] = 0;
|
||||
matrix[3] = 1;
|
||||
matrix[4] = 0;
|
||||
matrix[5] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this transformation is the identity.
|
||||
*/
|
||||
isIdentity(): boolean {
|
||||
const matrix = this.matrix;
|
||||
return (
|
||||
matrix[0] === 1 &&
|
||||
matrix[1] === 0 &&
|
||||
matrix[2] === 0 &&
|
||||
matrix[3] === 1 &&
|
||||
matrix[4] === 0 &&
|
||||
matrix[5] === 0
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is equals to another transformation.
|
||||
* @param other The transformation to check.
|
||||
*/
|
||||
equals(other: AffineTransformation): boolean {
|
||||
const matrix = this.matrix;
|
||||
const otherMatrix = other.matrix;
|
||||
return (
|
||||
this === other ||
|
||||
(matrix[0] === otherMatrix[0] &&
|
||||
matrix[1] === otherMatrix[1] &&
|
||||
matrix[2] === otherMatrix[2] &&
|
||||
matrix[3] === otherMatrix[3] &&
|
||||
matrix[4] === otherMatrix[4] &&
|
||||
matrix[5] === otherMatrix[5])
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if this is almost equals to another transformation.
|
||||
* @param other The transformation to check.
|
||||
* @param epsilon The relative margin error.
|
||||
*/
|
||||
nearlyEquals(other: AffineTransformation, epsilon: float): boolean {
|
||||
const matrix = this.matrix;
|
||||
const otherMatrix = other.matrix;
|
||||
return (
|
||||
this === other ||
|
||||
(gdjs.nearlyEqual(matrix[0], otherMatrix[0], epsilon) &&
|
||||
gdjs.nearlyEqual(matrix[1], otherMatrix[1], epsilon) &&
|
||||
gdjs.nearlyEqual(matrix[2], otherMatrix[2], epsilon) &&
|
||||
gdjs.nearlyEqual(matrix[3], otherMatrix[3], epsilon) &&
|
||||
gdjs.nearlyEqual(matrix[4], otherMatrix[4], epsilon) &&
|
||||
gdjs.nearlyEqual(matrix[5], otherMatrix[5], epsilon))
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Copy a transformation.
|
||||
* @param other The transformation to copy.
|
||||
*/
|
||||
copyFrom(other: AffineTransformation) {
|
||||
const matrix = this.matrix;
|
||||
const otherMatrix = other.matrix;
|
||||
|
||||
matrix[0] = otherMatrix[0];
|
||||
matrix[1] = otherMatrix[1];
|
||||
matrix[2] = otherMatrix[2];
|
||||
matrix[3] = otherMatrix[3];
|
||||
matrix[4] = otherMatrix[4];
|
||||
matrix[5] = otherMatrix[5];
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to a translation.
|
||||
*
|
||||
* @param x The horizontal translation value.
|
||||
* @param y The vertical translation value.
|
||||
*/
|
||||
setToTranslation(tx: float, ty: float) {
|
||||
const matrix = this.matrix;
|
||||
// | m0 m2 m4 | | 1 0 tx |
|
||||
// | m1 m3 m5 | = | 0 1 ty |
|
||||
// | 0 0 1 | | 0 0 1 |
|
||||
matrix[0] = 1;
|
||||
matrix[1] = 0;
|
||||
matrix[2] = 0;
|
||||
matrix[3] = 1;
|
||||
matrix[4] = tx;
|
||||
matrix[5] = ty;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate a translation.
|
||||
*
|
||||
* @param tx The horizontal translation value.
|
||||
* @param ty The vertical translation value.
|
||||
*/
|
||||
translate(tx: float, ty: float) {
|
||||
var matrix = this.matrix;
|
||||
// 1 0 tx
|
||||
// 0 1 ty
|
||||
// 0 0 1
|
||||
// m0 m2 m4
|
||||
// m1 m3 m5
|
||||
// 0 0 1
|
||||
matrix[4] = matrix[0] * tx + matrix[2] * ty + matrix[4];
|
||||
matrix[5] = matrix[1] * tx + matrix[3] * ty + matrix[5];
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to a scale.
|
||||
*
|
||||
* @param sx The horizontal scale value.
|
||||
* @param sy The vertical scale value.
|
||||
*/
|
||||
setToScale(sx: float, sy: float) {
|
||||
const matrix = this.matrix;
|
||||
// | m0 m2 m4 | | sx 0 0 |
|
||||
// | m1 m3 m5 | = | 0 sy 0 |
|
||||
// | 0 0 1 | | 0 0 1 |
|
||||
matrix[0] = sx;
|
||||
matrix[1] = 0;
|
||||
matrix[2] = 0;
|
||||
matrix[3] = sy;
|
||||
matrix[4] = 0;
|
||||
matrix[5] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate a scale.
|
||||
*
|
||||
* @param sx The horizontal scale value.
|
||||
* @param sy The vertical scale value.
|
||||
*/
|
||||
scale(sx: float, sy: float) {
|
||||
const matrix = this.matrix;
|
||||
// sx 0 0
|
||||
// 0 sy 0
|
||||
// 0 0 1
|
||||
// m0 m2 m4
|
||||
// m1 m3 m5
|
||||
// 0 0 1
|
||||
matrix[0] *= sx;
|
||||
matrix[1] *= sx;
|
||||
matrix[2] *= sy;
|
||||
matrix[3] *= sy;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to a rotation.
|
||||
*
|
||||
* @param angle The angle of rotation in radians.
|
||||
*/
|
||||
setToRotation(theta: float) {
|
||||
const matrix = this.matrix;
|
||||
let cost = Math.cos(theta);
|
||||
let sint = Math.sin(theta);
|
||||
|
||||
// Avoid rounding errors around 0.
|
||||
if (cost === -1 || cost === 1) {
|
||||
sint = 0;
|
||||
}
|
||||
if (sint === -1 || sint === 1) {
|
||||
cost = 0;
|
||||
}
|
||||
|
||||
// | m0 m2 m4 | | cost -sint 0 |
|
||||
// | m1 m3 m5 | = | sint cost 0 |
|
||||
// | 0 0 1 | | 0 0 1 |
|
||||
matrix[0] = cost;
|
||||
matrix[1] = sint;
|
||||
matrix[2] = -sint;
|
||||
matrix[3] = cost;
|
||||
matrix[4] = 0;
|
||||
matrix[5] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate a rotation.
|
||||
*
|
||||
* @param angle The angle of rotation in radians.
|
||||
*/
|
||||
rotate(angle: float) {
|
||||
const matrix = this.matrix;
|
||||
let cost = Math.cos(angle);
|
||||
let sint = Math.sin(angle);
|
||||
|
||||
// Avoid rounding errors around 0.
|
||||
if (cost === -1 || cost === 1) {
|
||||
sint = 0;
|
||||
}
|
||||
if (sint === -1 || sint === 1) {
|
||||
cost = 0;
|
||||
}
|
||||
|
||||
// cost -sint 0
|
||||
// sint cost 0
|
||||
// 0 0 1
|
||||
// m0 m2 m4
|
||||
// m1 m3 m5
|
||||
// 0 0 1
|
||||
|
||||
const m0 = matrix[0];
|
||||
const m1 = matrix[1];
|
||||
const m2 = matrix[2];
|
||||
const m3 = matrix[3];
|
||||
|
||||
matrix[0] = m0 * cost + m2 * sint;
|
||||
matrix[1] = m1 * cost + m3 * sint;
|
||||
matrix[2] = m0 * -sint + m2 * cost;
|
||||
matrix[3] = m1 * -sint + m3 * cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to a rotation.
|
||||
*
|
||||
* @param angle The angle of rotation in radians.
|
||||
* @param anchorX The rotation anchor point X.
|
||||
* @param anchorY The rotation anchor point Y.
|
||||
*/
|
||||
setToRotationAround(angle: float, anchorX: float, anchorY: float) {
|
||||
const matrix = this.matrix;
|
||||
let cost = Math.cos(angle);
|
||||
let sint = Math.sin(angle);
|
||||
|
||||
// Avoid rounding errors around 0.
|
||||
if (cost === -1 || cost === 1) {
|
||||
sint = 0;
|
||||
}
|
||||
if (sint === -1 || sint === 1) {
|
||||
cost = 0;
|
||||
}
|
||||
|
||||
// | m0 m2 m4 | | cost -sint x-x*cost+y*sint |
|
||||
// | m1 m3 m5 | = | sint cost y-x*sint-y*cost |
|
||||
// | 0 0 1 | | 0 0 1 |
|
||||
matrix[0] = cost;
|
||||
matrix[1] = sint;
|
||||
matrix[2] = -sint;
|
||||
matrix[3] = cost;
|
||||
matrix[4] = anchorX - anchorX * cost + anchorY * sint;
|
||||
matrix[5] = anchorY - anchorX * sint + anchorY * cost;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate a rotation.
|
||||
*
|
||||
* @param angle The angle of rotation in radians.
|
||||
* @param anchorX The rotation anchor point X.
|
||||
* @param anchorY The rotation anchor point Y.
|
||||
*/
|
||||
rotateAround(angle: float, anchorX: float, anchorY: float) {
|
||||
this.translate(anchorX, anchorY);
|
||||
this.rotate(angle);
|
||||
// First: translate anchor to origin
|
||||
this.translate(-anchorX, -anchorY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to an horizontal flip.
|
||||
*
|
||||
* @param anchorX The flip anchor point X.
|
||||
*/
|
||||
setToFlipX(anchorX: float) {
|
||||
const matrix = this.matrix;
|
||||
// | m0 m2 m4 | | -1 0 2x |
|
||||
// | m1 m3 m5 | = | 0 1 0 |
|
||||
// | 0 0 1 | | 0 0 1 |
|
||||
matrix[0] = -1;
|
||||
matrix[1] = 0;
|
||||
matrix[2] = 0;
|
||||
matrix[3] = 1;
|
||||
matrix[4] = 2 * anchorX;
|
||||
matrix[5] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate an horizontal flip.
|
||||
*
|
||||
* @param anchorX The flip anchor point X.
|
||||
*/
|
||||
flipX(anchorX: float) {
|
||||
this.translate(anchorX, 0);
|
||||
this.scale(-1, 1);
|
||||
// First: translate anchor to origin
|
||||
this.translate(-anchorX, 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Reset to an vertical flip.
|
||||
*
|
||||
* @param anchorY The flip anchor point Y.
|
||||
*/
|
||||
setToFlipY(anchorY: float) {
|
||||
const matrix = this.matrix;
|
||||
// | m0 m2 m4 | | 1 0 0 |
|
||||
// | m1 m3 m5 | = | 0 -1 2x |
|
||||
// | 0 0 1 | | 0 0 1 |
|
||||
matrix[0] = -1;
|
||||
matrix[1] = 0;
|
||||
matrix[2] = 0;
|
||||
matrix[3] = 1;
|
||||
matrix[4] = 0;
|
||||
matrix[5] = 2 * anchorY;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate an vertical flip.
|
||||
*
|
||||
* @param anchorY The flip anchor point Y.
|
||||
*/
|
||||
flipY(anchorY: float) {
|
||||
this.translate(0, anchorY);
|
||||
this.scale(1, -1);
|
||||
// First: translate anchor to origin
|
||||
this.translate(0, -anchorY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate a flip between X and Y.
|
||||
*/
|
||||
flipDiagonally() {
|
||||
const matrix = this.matrix;
|
||||
|
||||
const m0 = matrix[0];
|
||||
const m1 = matrix[1];
|
||||
const m2 = matrix[2];
|
||||
const m3 = matrix[3];
|
||||
const m4 = matrix[4];
|
||||
const m5 = matrix[5];
|
||||
|
||||
matrix[0] = m1;
|
||||
matrix[1] = m0;
|
||||
matrix[2] = m3;
|
||||
matrix[3] = m2;
|
||||
matrix[4] = m5;
|
||||
matrix[5] = m4;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate a transformation after this one.
|
||||
* @param other The transformation to concatenate.
|
||||
*/
|
||||
concatenate(other: AffineTransformation) {
|
||||
const matrix = this.matrix;
|
||||
const otherMatrix = other.matrix;
|
||||
|
||||
const m0 = matrix[0];
|
||||
const m1 = matrix[1];
|
||||
const m2 = matrix[2];
|
||||
const m3 = matrix[3];
|
||||
const m4 = matrix[4];
|
||||
const m5 = matrix[5];
|
||||
|
||||
const o0 = otherMatrix[0];
|
||||
const o1 = otherMatrix[1];
|
||||
const o2 = otherMatrix[2];
|
||||
const o3 = otherMatrix[3];
|
||||
const o4 = otherMatrix[4];
|
||||
const o5 = otherMatrix[5];
|
||||
|
||||
// o0 o2 o4
|
||||
// o1 o3 o5
|
||||
// 0 0 1
|
||||
// m0 m2 m4
|
||||
// m1 m3 m5
|
||||
// 0 0 1
|
||||
matrix[0] = o0 * m0 + o1 * m2;
|
||||
matrix[1] = o0 * m1 + o1 * m3;
|
||||
matrix[2] = o2 * m0 + o3 * m2;
|
||||
matrix[3] = o2 * m1 + o3 * m3;
|
||||
matrix[4] = o4 * m0 + o5 * m2 + m4;
|
||||
matrix[5] = o4 * m1 + o5 * m3 + m5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Concatenate a transformation before this one.
|
||||
* @param other The transformation to concatenate.
|
||||
*/
|
||||
preConcatenate(other: AffineTransformation) {
|
||||
const matrix = this.matrix;
|
||||
const otherMatrix = other.matrix;
|
||||
|
||||
const m0 = matrix[0];
|
||||
const m1 = matrix[1];
|
||||
const m2 = matrix[2];
|
||||
const m3 = matrix[3];
|
||||
const m4 = matrix[4];
|
||||
const m5 = matrix[5];
|
||||
|
||||
const o0 = otherMatrix[0];
|
||||
const o1 = otherMatrix[1];
|
||||
const o2 = otherMatrix[2];
|
||||
const o3 = otherMatrix[3];
|
||||
const o4 = otherMatrix[4];
|
||||
const o5 = otherMatrix[5];
|
||||
|
||||
// m0 m2 m4
|
||||
// m1 m3 m5
|
||||
// 0 0 1
|
||||
// o0 o2 o4
|
||||
// o1 o3 o5
|
||||
// 0 0 1
|
||||
matrix[0] = m0 * o0 + m1 * o2;
|
||||
matrix[1] = m0 * o1 + m1 * o3;
|
||||
matrix[2] = m2 * o0 + m3 * o2;
|
||||
matrix[3] = m2 * o1 + m3 * o3;
|
||||
matrix[4] = m4 * o0 + m5 * o2 + o4;
|
||||
matrix[5] = m4 * o1 + m5 * o3 + o5;
|
||||
}
|
||||
|
||||
/**
|
||||
* Transform a point.
|
||||
*
|
||||
* @param source The point to transform.
|
||||
* @param destination The Point to store the transformed coordinates.
|
||||
*/
|
||||
transform(source: FloatPoint, destination: FloatPoint) {
|
||||
const matrix = this.matrix;
|
||||
// x
|
||||
// y
|
||||
// 1
|
||||
// m0 m2 m4
|
||||
// m1 m3 m5
|
||||
// 0 0 1
|
||||
const x = matrix[0] * source[0] + matrix[2] * source[1] + matrix[4];
|
||||
const y = matrix[1] * source[0] + matrix[3] * source[1] + matrix[5];
|
||||
destination[0] = x;
|
||||
destination[1] = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* Invert the matrix.
|
||||
*/
|
||||
invert() {
|
||||
const matrix = this.matrix;
|
||||
|
||||
const m0 = matrix[0];
|
||||
const m1 = matrix[1];
|
||||
const m2 = matrix[2];
|
||||
const m3 = matrix[3];
|
||||
const m4 = matrix[4];
|
||||
const m5 = matrix[5];
|
||||
|
||||
const n = m0 * m3 - m1 * m2;
|
||||
|
||||
matrix[0] = m3 / n;
|
||||
matrix[1] = -m1 / n;
|
||||
matrix[2] = -m2 / n;
|
||||
matrix[3] = m0 / n;
|
||||
matrix[4] = (m2 * m5 - m3 * m4) / n;
|
||||
matrix[5] = -(m0 * m5 - m1 * m4) / n;
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
toString() {
|
||||
const matrix = this.matrix;
|
||||
return `[[${matrix[0]} ${matrix[1]}] [${matrix[2]} ${matrix[3]}] [${matrix[4]} ${matrix[5]}]]`;
|
||||
}
|
||||
}
|
||||
}
|
@@ -95,6 +95,15 @@ namespace gdjs {
|
||||
return hexToRGBColor(value);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a RGB string ("rrr;ggg;bbb") or a Hex string ("#rrggbb") to a RGB color number.
|
||||
* @param rgbOrHexString The color as a RGB string or Hex string
|
||||
*/
|
||||
export const rgbOrHexStringToNumber = (rgbOrHexString: string): integer => {
|
||||
const components = gdjs.rgbOrHexToRGBColor(rgbOrHexString);
|
||||
return gdjs.rgbToHexNumber(components[0], components[1], components[2]);
|
||||
};
|
||||
|
||||
/**
|
||||
* Convert a RGB object to a Hex number.
|
||||
* @param r Red
|
||||
@@ -524,6 +533,31 @@ namespace gdjs {
|
||||
hex[r[15]]
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* See https://floating-point-gui.de/errors/comparison/
|
||||
* @param a
|
||||
* @param b
|
||||
* @param epsilon the relative margin error
|
||||
* @returns true when a and b are within a relative margin error.
|
||||
*/
|
||||
export const nearlyEqual = (a: float, b: float, epsilon: float): boolean => {
|
||||
const absA = Math.abs(a);
|
||||
const absB = Math.abs(b);
|
||||
const diff = Math.abs(a - b);
|
||||
|
||||
if (a === b) {
|
||||
// shortcut, handles infinities
|
||||
return true;
|
||||
} else if (a == 0 || b == 0 || absA + absB < Number.EPSILON) {
|
||||
// a or b is zero or both are extremely close to it
|
||||
// relative error is less meaningful here
|
||||
return diff < epsilon * Number.EPSILON;
|
||||
} else {
|
||||
// use relative error
|
||||
return diff / Math.min(absA + absB, Number.MAX_VALUE) < epsilon;
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
//Make sure console.warn and console.error are available.
|
||||
|
@@ -66,6 +66,7 @@ module.exports = function (config) {
|
||||
'../../newIDE/app/resources/GDJS/Runtime/events-tools/stringtools.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/events-tools/windowtools.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/debugger-client/hot-reloader.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/affinetransformation.js',
|
||||
|
||||
//Extensions:
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/DraggableBehavior/draggableruntimebehavior.js',
|
||||
@@ -93,7 +94,15 @@ module.exports = function (config) {
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Firebase/B_firebasetools/*.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Effects/kawase-blur-pixi-filter.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/Effects/pixi-filters/filter-kawase-blur.js',
|
||||
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TileMap/tilemapcollisionmaskruntimeobject.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TileMap/TileMapRuntimeManager.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TileMap/tilemapruntimeobject-pixi-renderer.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TileMap/tilemapruntimeobject.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TileMap/collision/TileMapCollisionMaskRenderer.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TileMap/collision/TransformedTileMap.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TileMap/helper/TileMapHelper.js',
|
||||
'../../newIDE/app/resources/GDJS/Runtime/Extensions/TileMap/pako/dist/pako.min.js',
|
||||
|
||||
// Test extensions:
|
||||
'./tests/Extensions/**.js',
|
||||
|
||||
@@ -112,6 +121,13 @@ module.exports = function (config) {
|
||||
served: true,
|
||||
nocache: false,
|
||||
},
|
||||
{
|
||||
pattern: './tests-utils/simple-tiled-map/*.json',
|
||||
watched: false,
|
||||
included: false,
|
||||
served: true,
|
||||
nocache: false,
|
||||
},
|
||||
|
||||
...testFiles,
|
||||
...(config.enableBenchmarks ? benchmarkFiles : []),
|
||||
|
234
GDJS/tests/tests-utils/simple-tiled-map/MiniTiledSet.json
Normal file
234
GDJS/tests/tests-utils/simple-tiled-map/MiniTiledSet.json
Normal file
@@ -0,0 +1,234 @@
|
||||
{ "columns":2,
|
||||
"image":"MiniTiledSet.png",
|
||||
"imageheight":24,
|
||||
"imagewidth":16,
|
||||
"margin":0,
|
||||
"name":"new tileset",
|
||||
"spacing":0,
|
||||
"tilecount":6,
|
||||
"tiledversion":"1.7.2",
|
||||
"tileheight":8,
|
||||
"tiles":[
|
||||
{
|
||||
"id":0,
|
||||
"objectgroup":
|
||||
{
|
||||
"draworder":"index",
|
||||
"name":"",
|
||||
"objects":[
|
||||
{
|
||||
"height":8,
|
||||
"id":1,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"class":"",
|
||||
"visible":true,
|
||||
"width":8,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
"class":"obstacle"
|
||||
},
|
||||
{
|
||||
"id":2,
|
||||
"objectgroup":
|
||||
{
|
||||
"draworder":"index",
|
||||
"name":"",
|
||||
"objects":[
|
||||
{
|
||||
"height":0,
|
||||
"id":1,
|
||||
"name":"",
|
||||
"polygon":[
|
||||
{
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"x":8,
|
||||
"y":-8
|
||||
},
|
||||
{
|
||||
"x":0,
|
||||
"y":-8
|
||||
}],
|
||||
"rotation":0,
|
||||
"class":"",
|
||||
"visible":true,
|
||||
"width":0,
|
||||
"x":0,
|
||||
"y":8
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
"class":"obstacle"
|
||||
},
|
||||
{
|
||||
"id":3,
|
||||
"objectgroup":
|
||||
{
|
||||
"draworder":"index",
|
||||
"id":2,
|
||||
"name":"",
|
||||
"objects":[
|
||||
{
|
||||
"height":0,
|
||||
"id":1,
|
||||
"name":"",
|
||||
"polygon":[
|
||||
{
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"x":4,
|
||||
"y":4
|
||||
},
|
||||
{
|
||||
"x":8,
|
||||
"y":0
|
||||
}],
|
||||
"rotation":0,
|
||||
"class":"",
|
||||
"visible":true,
|
||||
"width":0,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"height":0,
|
||||
"id":3,
|
||||
"name":"",
|
||||
"polygon":[
|
||||
{
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"x":4,
|
||||
"y":4
|
||||
},
|
||||
{
|
||||
"x":8,
|
||||
"y":0
|
||||
}],
|
||||
"rotation":180,
|
||||
"class":"",
|
||||
"visible":true,
|
||||
"width":0,
|
||||
"x":8,
|
||||
"y":8
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
"class":"obstacle"
|
||||
},
|
||||
{
|
||||
"id":4,
|
||||
"objectgroup":
|
||||
{
|
||||
"draworder":"index",
|
||||
"id":2,
|
||||
"name":"",
|
||||
"objects":[
|
||||
{
|
||||
"height":0,
|
||||
"id":1,
|
||||
"name":"",
|
||||
"polygon":[
|
||||
{
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"x":0,
|
||||
"y":8
|
||||
},
|
||||
{
|
||||
"x":8,
|
||||
"y":0
|
||||
}],
|
||||
"rotation":0,
|
||||
"class":"obstacle",
|
||||
"visible":true,
|
||||
"width":0,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"height":0,
|
||||
"id":2,
|
||||
"name":"",
|
||||
"polygon":[
|
||||
{
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
{
|
||||
"x":0,
|
||||
"y":8
|
||||
},
|
||||
{
|
||||
"x":8,
|
||||
"y":0
|
||||
}],
|
||||
"rotation":180,
|
||||
"class":"lava",
|
||||
"visible":true,
|
||||
"width":0,
|
||||
"x":8,
|
||||
"y":8
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
}
|
||||
},
|
||||
{
|
||||
"id":5,
|
||||
"objectgroup":
|
||||
{
|
||||
"draworder":"index",
|
||||
"id":2,
|
||||
"name":"",
|
||||
"objects":[
|
||||
{
|
||||
"height":8,
|
||||
"id":1,
|
||||
"name":"",
|
||||
"rotation":0,
|
||||
"class":"",
|
||||
"visible":true,
|
||||
"width":8,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"opacity":1,
|
||||
"type":"objectgroup",
|
||||
"visible":true,
|
||||
"x":0,
|
||||
"y":0
|
||||
},
|
||||
"class":"lava"
|
||||
}],
|
||||
"tilewidth":8,
|
||||
"type":"tileset",
|
||||
"version":"1.6"
|
||||
}
|
BIN
GDJS/tests/tests-utils/simple-tiled-map/MiniTiledSet.png
Normal file
BIN
GDJS/tests/tests-utils/simple-tiled-map/MiniTiledSet.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 246 B |
32
GDJS/tests/tests-utils/simple-tiled-map/SmallTiledMap.json
Normal file
32
GDJS/tests/tests-utils/simple-tiled-map/SmallTiledMap.json
Normal file
@@ -0,0 +1,32 @@
|
||||
{ "compressionlevel":-1,
|
||||
"height":2,
|
||||
"infinite":false,
|
||||
"layers":[
|
||||
{
|
||||
"data":[1, 3, 4, 5, 3, 2, 0, 1073741829],
|
||||
"height":2,
|
||||
"id":1,
|
||||
"name":"Tile Layer 1",
|
||||
"opacity":1,
|
||||
"type":"tilelayer",
|
||||
"visible":true,
|
||||
"width":4,
|
||||
"x":0,
|
||||
"y":0
|
||||
}],
|
||||
"nextlayerid":2,
|
||||
"nextobjectid":1,
|
||||
"orientation":"orthogonal",
|
||||
"renderorder":"right-down",
|
||||
"tiledversion":"1.7.2",
|
||||
"tileheight":8,
|
||||
"tilesets":[
|
||||
{
|
||||
"firstgid":1,
|
||||
"source":"MiniTiledSet.json"
|
||||
}],
|
||||
"tilewidth":8,
|
||||
"type":"map",
|
||||
"version":"1.6",
|
||||
"width":4
|
||||
}
|
58
GDJS/tests/tests/affinetransformation.spec.js
Normal file
58
GDJS/tests/tests/affinetransformation.spec.js
Normal file
@@ -0,0 +1,58 @@
|
||||
describe('gdjs.AffineTransformation', function () {
|
||||
const epsilon = 1 / (2 << 16);
|
||||
|
||||
it('can conserve identity through identity', function () {
|
||||
const identityA = new gdjs.AffineTransformation();
|
||||
const identityB = new gdjs.AffineTransformation();
|
||||
|
||||
expect(identityA.equals(identityB)).to.be(true);
|
||||
|
||||
identityB.setToIdentity();
|
||||
expect(identityA.equals(identityB)).to.be(true);
|
||||
|
||||
identityB.concatenate(identityA);
|
||||
expect(identityA.equals(identityB)).to.be(true);
|
||||
});
|
||||
|
||||
it('can compose translations', function () {
|
||||
const translationA = new gdjs.AffineTransformation();
|
||||
translationA.setToTranslation(12 + 45, 67 + 89);
|
||||
|
||||
const translationB = new gdjs.AffineTransformation();
|
||||
translationB.setToTranslation(12, 67);
|
||||
translationB.translate(45, 89);
|
||||
|
||||
expect(translationA.equals(translationB)).to.be(true);
|
||||
});
|
||||
|
||||
it('can compose rotations', function () {
|
||||
const rotationA = new gdjs.AffineTransformation();
|
||||
rotationA.setToRotation(Math.PI / 3 + Math.PI / 2);
|
||||
|
||||
const rotationB = new gdjs.AffineTransformation();
|
||||
rotationB.setToRotation(Math.PI / 3);
|
||||
rotationB.rotate(Math.PI / 2);
|
||||
|
||||
expect(rotationA.nearlyEquals(rotationB, epsilon)).to.be(true);
|
||||
});
|
||||
|
||||
it('can do exact 90° rotation transformations', function () {
|
||||
const rotationA = new gdjs.AffineTransformation();
|
||||
rotationA.setToRotation(90 * Math.PI / 180);
|
||||
|
||||
const result = [10, 5];
|
||||
rotationA.transform(result, result);
|
||||
expect(result).to.eql([-5, 10]);
|
||||
});
|
||||
|
||||
it('can compose scales', function () {
|
||||
const scaleA = new gdjs.AffineTransformation();
|
||||
scaleA.setToScale(2 * 4, 3 * 5);
|
||||
|
||||
const scaleB = new gdjs.AffineTransformation();
|
||||
scaleB.setToScale(2, 3);
|
||||
scaleB.scale(4, 5);
|
||||
|
||||
expect(scaleA.equals(scaleB)).to.be(true);
|
||||
});
|
||||
});
|
4
SharedLibs/TileMapHelper/.gitignore
vendored
Normal file
4
SharedLibs/TileMapHelper/.gitignore
vendored
Normal file
@@ -0,0 +1,4 @@
|
||||
/dist/
|
||||
/coverage/
|
||||
/node_modules/
|
||||
/package-lock.json
|
3
SharedLibs/TileMapHelper/ReadMe.md
Normal file
3
SharedLibs/TileMapHelper/ReadMe.md
Normal file
@@ -0,0 +1,3 @@
|
||||
This library sources is used by the [tile map extension](../../Extensions/TileMap/).
|
||||
|
||||
The `npm run build` command copy the bundled library at the right place for the extension to use it.
|
42
SharedLibs/TileMapHelper/karma.conf.js
Normal file
42
SharedLibs/TileMapHelper/karma.conf.js
Normal file
@@ -0,0 +1,42 @@
|
||||
module.exports = function (config) {
|
||||
config.set({
|
||||
frameworks: ['mocha', 'karma-typescript'],
|
||||
browserNoActivityTimeout: 400000,
|
||||
client: {
|
||||
mocha: {
|
||||
reporter: 'html',
|
||||
timeout: 10000,
|
||||
},
|
||||
},
|
||||
files: [
|
||||
{ pattern: "node_modules/expect.js/index.js" },
|
||||
{ pattern: "./src/tiled/**/*.ts" },
|
||||
{ pattern: "./src/model/**/*.ts" }
|
||||
],
|
||||
preprocessors: {
|
||||
"**/*.ts": 'karma-typescript'
|
||||
},
|
||||
reporters: ['dots', 'karma-typescript'],
|
||||
singleRun: true,
|
||||
karmaTypescriptConfig: {
|
||||
compilerOptions: {
|
||||
module: "commonjs",
|
||||
noImplicitAny: true,
|
||||
outDir: "tmp",
|
||||
target: "ES5",
|
||||
sourceMap: true,
|
||||
types : [
|
||||
"mocha",
|
||||
"expect.js",
|
||||
"offscreencanvas"
|
||||
],
|
||||
lib: ["DOM", "ES5", "ES6"],
|
||||
"esModuleInterop": false,
|
||||
"downlevelIteration": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
},
|
||||
exclude: ["node_modules"]
|
||||
}
|
||||
});
|
||||
};
|
59
SharedLibs/TileMapHelper/package.json
Normal file
59
SharedLibs/TileMapHelper/package.json
Normal file
@@ -0,0 +1,59 @@
|
||||
{
|
||||
"name": "tilemap",
|
||||
"version": "0.0.0",
|
||||
"license": "MIT",
|
||||
"description": "",
|
||||
"main": "dist/TileMapHelper.js",
|
||||
"types": "dist/index.d.ts",
|
||||
"files": [
|
||||
"src",
|
||||
"dist"
|
||||
],
|
||||
"scripts": {
|
||||
"test": "karma start --browsers ChromeHeadless --single-run",
|
||||
"test:watch": "karma start --browsers ChromeHeadless",
|
||||
"test:firefox": "karma start --browsers Firefox --single-run",
|
||||
"test:firefox:watch": "karma start --browsers Firefox",
|
||||
"tsc": "tsc",
|
||||
"build": "rollup -c",
|
||||
"dev": "rollup -c -w",
|
||||
"prepublishOnly": "npm run build",
|
||||
"format": "prettier --write \"src/**/*.ts\"",
|
||||
"check-format": "prettier --list-different \"src/**/*.ts\""
|
||||
},
|
||||
"dependencies": {
|
||||
"@types/offscreencanvas": "^2019.6.4",
|
||||
"pako": "^2.0.4",
|
||||
"pixi.js": "^6.1.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rollup/plugin-node-resolve": "^13.1.3",
|
||||
"@rollup/plugin-typescript": "^8.3.3",
|
||||
"@types/expect.js": "^0.3.29",
|
||||
"@types/mocha": "^5.2.7",
|
||||
"expect.js": "^0.3.1",
|
||||
"karma": "^6.1.0",
|
||||
"karma-chrome-launcher": "^3.1.0",
|
||||
"karma-firefox-launcher": "^2.0.0",
|
||||
"karma-mocha": "^1.3.0",
|
||||
"karma-typescript": "latest",
|
||||
"mocha": "^6.2.0",
|
||||
"prettier": "2.1.2",
|
||||
"rollup": "^2.66.1",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"ts-loader": "^9.2.3",
|
||||
"tslib": "^2.3.1",
|
||||
"typescript": "latest"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
"url": ""
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"contributors": [],
|
||||
"bugs": {
|
||||
"url": ""
|
||||
},
|
||||
"homepage": ""
|
||||
}
|
30
SharedLibs/TileMapHelper/rollup.config.js
Normal file
30
SharedLibs/TileMapHelper/rollup.config.js
Normal file
@@ -0,0 +1,30 @@
|
||||
//import pkg from "./package.json";
|
||||
import typescript from "@rollup/plugin-typescript";
|
||||
import resolve from '@rollup/plugin-node-resolve';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
|
||||
export default [
|
||||
{
|
||||
input: './src/index.ts',
|
||||
output: [
|
||||
{
|
||||
name: 'TileMapHelper',
|
||||
format: 'umd',
|
||||
file: '../../Extensions/TileMap/helper/TileMapHelper.js',
|
||||
sourcemap: true,
|
||||
plugins: [terser({
|
||||
format: {
|
||||
comments: false
|
||||
},
|
||||
})]
|
||||
},
|
||||
],
|
||||
external: ['pixi.js'],
|
||||
plugins: [
|
||||
resolve({
|
||||
extensions: ['.js'],
|
||||
}),
|
||||
typescript({ tsconfig: './tsconfig.json' }),
|
||||
],
|
||||
},
|
||||
];
|
28
SharedLibs/TileMapHelper/src/index.ts
Normal file
28
SharedLibs/TileMapHelper/src/index.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
/**
|
||||
* @packageDocumentation
|
||||
* @module TileMapHelper
|
||||
*/
|
||||
|
||||
import { TiledMap, TiledTileset } from "./tiled/TiledFormat";
|
||||
import {
|
||||
EditableTileMap,
|
||||
EditableTileMapLayer,
|
||||
TileDefinition,
|
||||
} from "./model/TileMapModel";
|
||||
|
||||
import { TileMapManager } from "./render/TileMapManager";
|
||||
import { TileTextureCache } from "./render/TileTextureCache";
|
||||
import { PixiTileMapHelper } from "./render/TileMapPixiHelper";
|
||||
|
||||
export * from "./model/CommonTypes";
|
||||
|
||||
export { EditableTileMap };
|
||||
export { EditableTileMapLayer };
|
||||
export { TileDefinition };
|
||||
|
||||
export { TiledMap };
|
||||
export { TiledTileset };
|
||||
|
||||
export { TileMapManager };
|
||||
export { TileTextureCache };
|
||||
export { PixiTileMapHelper };
|
5
SharedLibs/TileMapHelper/src/model/CommonTypes.ts
Normal file
5
SharedLibs/TileMapHelper/src/model/CommonTypes.ts
Normal file
@@ -0,0 +1,5 @@
|
||||
export declare type integer = number;
|
||||
export declare type float = number;
|
||||
export type FloatPoint = [float, float];
|
||||
|
||||
export type PolygonVertices = FloatPoint[];
|
608
SharedLibs/TileMapHelper/src/model/TileMapModel.ts
Normal file
608
SharedLibs/TileMapHelper/src/model/TileMapModel.ts
Normal file
@@ -0,0 +1,608 @@
|
||||
import { PolygonVertices, integer, float } from "./CommonTypes";
|
||||
|
||||
/**
|
||||
* A tile map model.
|
||||
*
|
||||
* Tile map files are parsed into this model by {@link TiledTileMapLoader}.
|
||||
* This model is used for rending ({@link TileMapRuntimeObjectPixiRenderer})
|
||||
* and hitboxes handling ({@link TransformedCollisionTileMap}).
|
||||
* This allows to support new file format with only a new parser.
|
||||
*/
|
||||
export class EditableTileMap {
|
||||
private _tileSet: Map<integer, TileDefinition>;
|
||||
private _layers: Array<AbstractEditableLayer>;
|
||||
/**
|
||||
* The width of a tile.
|
||||
*/
|
||||
private readonly tileWidth: integer;
|
||||
/**
|
||||
* The height of a tile.
|
||||
*/
|
||||
private readonly tileHeight: integer;
|
||||
/**
|
||||
* The number of tile columns in the map.
|
||||
*/
|
||||
private readonly dimX: integer;
|
||||
/**
|
||||
* The number of tile rows in the map.
|
||||
*/
|
||||
private readonly dimY: integer;
|
||||
|
||||
/**
|
||||
* @param tileWidth The width of a tile.
|
||||
* @param tileHeight The height of a tile.
|
||||
* @param dimX The number of tile columns in the map.
|
||||
* @param dimY The number of tile rows in the map.
|
||||
* @param tileSet The tile set.
|
||||
*/
|
||||
constructor(
|
||||
tileWidth: integer,
|
||||
tileHeight: integer,
|
||||
dimX: integer,
|
||||
dimY: integer,
|
||||
// TODO should the tile set be built internally?
|
||||
// It's not meant to change and it avoid to do a copy.
|
||||
tileSet: Map<integer, TileDefinition>
|
||||
) {
|
||||
this.tileWidth = tileWidth;
|
||||
this.tileHeight = tileHeight;
|
||||
this.dimX = dimX;
|
||||
this.dimY = dimY;
|
||||
this._tileSet = tileSet;
|
||||
this._layers = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile map width in pixels.
|
||||
*/
|
||||
getWidth(): integer {
|
||||
return this.tileWidth * this.dimX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile map height in pixels.
|
||||
*/
|
||||
getHeight(): integer {
|
||||
return this.tileHeight * this.dimY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile width in pixels.
|
||||
*/
|
||||
getTileHeight(): integer {
|
||||
return this.tileWidth;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The tile height in pixels.
|
||||
*/
|
||||
getTileWidth(): integer {
|
||||
return this.tileHeight;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The number of tile columns in the map.
|
||||
*/
|
||||
getDimensionX(): integer {
|
||||
return this.dimX;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The number of tile rows in the map.
|
||||
*/
|
||||
getDimensionY(): integer {
|
||||
return this.dimY;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param tileId The tile identifier
|
||||
* @returns The tile definition form the tile set.
|
||||
*/
|
||||
getTileDefinition(tileId: integer): TileDefinition | undefined {
|
||||
return this._tileSet.get(tileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns All the tile definitions form the tile set.
|
||||
*/
|
||||
getTileDefinitions(): Iterable<TileDefinition> {
|
||||
return this._tileSet.values();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id The identifier of the new layer.
|
||||
* @returns The new layer.
|
||||
*/
|
||||
addTileLayer(id: integer): EditableTileMapLayer {
|
||||
const layer = new EditableTileMapLayer(this, id);
|
||||
this._layers.push(layer);
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param id The identifier of the new layer.
|
||||
* @returns The new layer.
|
||||
*/
|
||||
addObjectLayer(id: integer): EditableObjectLayer {
|
||||
const layer = new EditableObjectLayer(this, id);
|
||||
this._layers.push(layer);
|
||||
return layer;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns All the layers of the tile map.
|
||||
*/
|
||||
getLayers(): Iterable<AbstractEditableLayer> {
|
||||
return this._layers;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a point is inside a tile with a given tag.
|
||||
*
|
||||
* It doesn't use the tile hitboxes.
|
||||
* It only check the point is inside the tile square.
|
||||
*
|
||||
* @param x The X coordinate of the point to check.
|
||||
* @param y The Y coordinate of the point to check.
|
||||
* @param tag The tile tag
|
||||
* @returns true when the point is inside a tile with a given tag.
|
||||
*/
|
||||
pointIsInsideTile(x: float, y: float, tag: string): boolean {
|
||||
const indexX = Math.floor(x / this.tileWidth);
|
||||
const indexY = Math.floor(y / this.tileHeight);
|
||||
for (const layer of this._layers) {
|
||||
const tileLayer = layer as EditableTileMapLayer;
|
||||
if (!tileLayer) {
|
||||
continue;
|
||||
}
|
||||
const tileId = tileLayer.get(indexX, indexY);
|
||||
if (tileId === undefined) {
|
||||
return false;
|
||||
}
|
||||
const tileDefinition = this._tileSet.get(tileId);
|
||||
if (tileDefinition!.hasTag(tag)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile map layer.
|
||||
*/
|
||||
abstract class AbstractEditableLayer {
|
||||
/**
|
||||
* The layer tile map.
|
||||
*/
|
||||
readonly tileMap: EditableTileMap;
|
||||
/**
|
||||
* The layer identifier.
|
||||
*/
|
||||
readonly id: integer;
|
||||
private visible: boolean = true;
|
||||
|
||||
/**
|
||||
* @param tileMap The layer tile map.
|
||||
* @param id The layer identifier.
|
||||
*/
|
||||
constructor(tileMap: EditableTileMap, id: integer) {
|
||||
this.tileMap = tileMap;
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
setVisible(visible: boolean): void {
|
||||
this.visible = visible;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the layer is visible.
|
||||
*/
|
||||
isVisible(): boolean {
|
||||
return this.visible;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A layer where tiles are placed with pixel coordinates.
|
||||
*/
|
||||
export class EditableObjectLayer extends AbstractEditableLayer {
|
||||
readonly objects: TileObject[];
|
||||
|
||||
/**
|
||||
* @param tileMap The layer tile map.
|
||||
* @param id The layer identifier.
|
||||
*/
|
||||
constructor(tileMap: EditableTileMap, id: integer) {
|
||||
super(tileMap, id);
|
||||
this.objects = [];
|
||||
}
|
||||
|
||||
add(object: TileObject): void {
|
||||
this.objects.push(object);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile that is placed with pixel coordinates.
|
||||
*/
|
||||
export class TileObject {
|
||||
/**
|
||||
* The tile identifier in the tile set.
|
||||
*/
|
||||
private tileId: integer;
|
||||
/**
|
||||
* The coordinate of the tile left side.
|
||||
*/
|
||||
readonly x: float;
|
||||
/**
|
||||
* The coordinate of the tile top side.
|
||||
*/
|
||||
readonly y: float;
|
||||
|
||||
/**
|
||||
* @param x The coordinate of the tile left side.
|
||||
* @param y The coordinate of the tile top side.
|
||||
* @param tileId The tile identifier in the tile set.
|
||||
*/
|
||||
constructor(x: float, y: float, tileId: integer) {
|
||||
this.tileId = tileId;
|
||||
this.x = x;
|
||||
this.y = y;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return The tile identifier in the tile set.
|
||||
*/
|
||||
getTileId(): integer {
|
||||
return FlippingHelper.getTileId(this.tileId);
|
||||
}
|
||||
|
||||
setFlippedHorizontally(flippedHorizontally: boolean): void {
|
||||
this.tileId = FlippingHelper.setFlippedHorizontally(
|
||||
this.tileId,
|
||||
flippedHorizontally
|
||||
);
|
||||
}
|
||||
|
||||
setFlippedVertically(flippedVertically: boolean): void {
|
||||
this.tileId = FlippingHelper.setFlippedVertically(
|
||||
this.tileId,
|
||||
flippedVertically
|
||||
);
|
||||
}
|
||||
|
||||
setFlippedDiagonally(flippedDiagonally: boolean): void {
|
||||
this.tileId = FlippingHelper.setFlippedDiagonally(
|
||||
this.tileId,
|
||||
flippedDiagonally
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the tile is flipped horizontally.
|
||||
*/
|
||||
isFlippedHorizontally(): boolean {
|
||||
return FlippingHelper.isFlippedHorizontally(this.tileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the tile is flipped vertically.
|
||||
*/
|
||||
isFlippedVertically(): boolean {
|
||||
return FlippingHelper.isFlippedVertically(this.tileId);
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns true if the tile is flipped diagonally.
|
||||
*/
|
||||
isFlippedDiagonally(): boolean {
|
||||
return FlippingHelper.isFlippedDiagonally(this.tileId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Tile identifiers making to access flipping flags.
|
||||
*/
|
||||
class FlippingHelper {
|
||||
static readonly flippedHorizontallyFlag = 0x80000000;
|
||||
static readonly flippedVerticallyFlag = 0x40000000;
|
||||
static readonly flippedDiagonallyFlag = 0x20000000;
|
||||
static readonly tileIdMask = ~(
|
||||
FlippingHelper.flippedHorizontallyFlag |
|
||||
FlippingHelper.flippedVerticallyFlag |
|
||||
FlippingHelper.flippedDiagonallyFlag
|
||||
);
|
||||
|
||||
static getTileId(tileId: integer): integer {
|
||||
return tileId & FlippingHelper.tileIdMask;
|
||||
}
|
||||
|
||||
static setFlippedHorizontally(
|
||||
tileId: integer,
|
||||
flippedHorizontally: boolean
|
||||
): integer {
|
||||
tileId &= ~FlippingHelper.flippedHorizontallyFlag;
|
||||
if (flippedHorizontally) {
|
||||
tileId |= FlippingHelper.flippedHorizontallyFlag;
|
||||
}
|
||||
return tileId;
|
||||
}
|
||||
|
||||
static setFlippedVertically(
|
||||
tileId: integer,
|
||||
flippedVertically: boolean
|
||||
): integer {
|
||||
tileId &= ~FlippingHelper.flippedVerticallyFlag;
|
||||
if (flippedVertically) {
|
||||
tileId |= FlippingHelper.flippedVerticallyFlag;
|
||||
}
|
||||
return tileId;
|
||||
}
|
||||
|
||||
static setFlippedDiagonally(
|
||||
tileId: integer,
|
||||
flippedDiagonally: boolean
|
||||
): integer {
|
||||
tileId &= ~FlippingHelper.flippedDiagonallyFlag;
|
||||
if (flippedDiagonally) {
|
||||
tileId |= FlippingHelper.flippedDiagonallyFlag;
|
||||
}
|
||||
return tileId;
|
||||
}
|
||||
|
||||
static isFlippedHorizontally(tileId: integer): boolean {
|
||||
return (tileId & FlippingHelper.flippedHorizontallyFlag) !== 0;
|
||||
}
|
||||
|
||||
static isFlippedVertically(tileId: integer): boolean {
|
||||
return (tileId & FlippingHelper.flippedVerticallyFlag) !== 0;
|
||||
}
|
||||
|
||||
static isFlippedDiagonally(tileId: integer): boolean {
|
||||
return (tileId & FlippingHelper.flippedDiagonallyFlag) !== 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile map layer with tile organized in grid.
|
||||
*/
|
||||
export class EditableTileMapLayer extends AbstractEditableLayer {
|
||||
private readonly _tiles: Array<Int32Array>;
|
||||
|
||||
/**
|
||||
* @param tileMap The layer tile map.
|
||||
* @param id The layer identifier.
|
||||
*/
|
||||
constructor(tileMap: EditableTileMap, id: integer) {
|
||||
super(tileMap, id);
|
||||
this._tiles = [];
|
||||
this._tiles.length = this.tileMap.getDimensionY();
|
||||
for (let index = 0; index < this._tiles.length; index++) {
|
||||
this._tiles[index] = new Int32Array(this.tileMap.getDimensionX());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param tileId The tile identifier in the tile set.
|
||||
*/
|
||||
setTile(x: integer, y: integer, tileId: integer): void {
|
||||
const definition = this.tileMap.getTileDefinition(tileId);
|
||||
if (!definition) {
|
||||
console.error(`Invalid tile definition index: ${tileId}`);
|
||||
return;
|
||||
}
|
||||
// +1 because 0 mean null
|
||||
this._tiles[y][x] = tileId + 1;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
*/
|
||||
removeTile(x: integer, y: integer): void {
|
||||
// 0 mean null
|
||||
this._tiles[y][x] = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param flippedHorizontally true if the tile is flipped horizontally.
|
||||
*/
|
||||
setFlippedHorizontally(
|
||||
x: integer,
|
||||
y: integer,
|
||||
flippedHorizontally: boolean
|
||||
): void {
|
||||
const tileId = this._tiles[y][x];
|
||||
if (tileId === 0) {
|
||||
return;
|
||||
}
|
||||
this._tiles[y][x] = FlippingHelper.setFlippedHorizontally(
|
||||
tileId,
|
||||
flippedHorizontally
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param flippedVertically true if the tile is flipped vertically.
|
||||
*/
|
||||
setFlippedVertically(
|
||||
x: integer,
|
||||
y: integer,
|
||||
flippedVertically: boolean
|
||||
): void {
|
||||
const tileId = this._tiles[y][x];
|
||||
if (tileId === 0) {
|
||||
return;
|
||||
}
|
||||
this._tiles[y][x] = FlippingHelper.setFlippedVertically(
|
||||
tileId,
|
||||
flippedVertically
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @param flippedDiagonally true if the tile is flipped diagonally.
|
||||
*/
|
||||
setFlippedDiagonally(
|
||||
x: integer,
|
||||
y: integer,
|
||||
flippedDiagonally: boolean
|
||||
): void {
|
||||
const tileId = this._tiles[y][x];
|
||||
if (tileId === 0) {
|
||||
return;
|
||||
}
|
||||
this._tiles[y][x] = FlippingHelper.setFlippedDiagonally(
|
||||
tileId,
|
||||
flippedDiagonally
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped horizontally.
|
||||
*/
|
||||
isFlippedHorizontally(x: integer, y: integer): boolean {
|
||||
return FlippingHelper.isFlippedHorizontally(this._tiles[y][x]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped vertically.
|
||||
*/
|
||||
isFlippedVertically(x: integer, y: integer): boolean {
|
||||
return FlippingHelper.isFlippedVertically(this._tiles[y][x]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns true if the tile is flipped diagonally.
|
||||
*/
|
||||
isFlippedDiagonally(x: integer, y: integer): boolean {
|
||||
return FlippingHelper.isFlippedDiagonally(this._tiles[y][x]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param x The layer column.
|
||||
* @param y The layer row.
|
||||
* @returns The tile identifier from the tile set.
|
||||
*/
|
||||
get(x: integer, y: integer): integer | undefined {
|
||||
const row = this._tiles[y];
|
||||
if (!row || row[x] === 0) {
|
||||
return undefined;
|
||||
}
|
||||
// -1 because 0 is keep for null.
|
||||
const tileId = FlippingHelper.getTileId(row[x] - 1);
|
||||
return tileId;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of tile columns in the layer.
|
||||
*/
|
||||
getDimensionX(): integer {
|
||||
return this._tiles.length === 0 ? 0 : this._tiles[0].length;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of tile rows in the layer.
|
||||
*/
|
||||
getDimensionY(): integer {
|
||||
return this._tiles.length;
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The layer width in pixels.
|
||||
*/
|
||||
getWidth(): integer {
|
||||
return this.tileMap.getWidth();
|
||||
}
|
||||
|
||||
/**
|
||||
* @returns The layer height in pixels.
|
||||
*/
|
||||
getHeight(): integer {
|
||||
return this.tileMap.getHeight();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* A tile definition from the tile set.
|
||||
*/
|
||||
export class TileDefinition {
|
||||
/**
|
||||
* There will probably be at most 4 tags on a tile.
|
||||
* An array lookup should take less time than using a Map.
|
||||
*/
|
||||
private readonly taggedHitBoxes: {
|
||||
tag: string;
|
||||
polygons: PolygonVertices[];
|
||||
}[];
|
||||
private readonly animationLength: integer;
|
||||
|
||||
/**
|
||||
* @param animationLength The number of frame in the tile animation.
|
||||
*/
|
||||
constructor(animationLength: integer) {
|
||||
this.taggedHitBoxes = [];
|
||||
this.animationLength = animationLength;
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a polygon for the collision layer
|
||||
* @param tag The tag to allow collision layer filtering.
|
||||
* @param polygon The polygon to use for collisions.
|
||||
*/
|
||||
add(tag: string, polygon: PolygonVertices): void {
|
||||
let taggedHitBox = this.taggedHitBoxes.find((hitbox) => hitbox.tag === tag);
|
||||
if (!taggedHitBox) {
|
||||
taggedHitBox = { tag, polygons: [] };
|
||||
this.taggedHitBoxes.push(taggedHitBox);
|
||||
}
|
||||
taggedHitBox.polygons.push(polygon);
|
||||
}
|
||||
|
||||
/**
|
||||
* This property is used by {@link TransformedCollisionTileMap}
|
||||
* to make collision classes.
|
||||
* @param tag The tag to allow collision layer filtering.
|
||||
* @returns true if this tile contains any polygon with the given tag.
|
||||
*/
|
||||
hasTag(tag: string): boolean {
|
||||
return this.taggedHitBoxes.some((hitbox) => hitbox.tag === tag);
|
||||
}
|
||||
|
||||
/**
|
||||
* The hitboxes positioning is done by {@link TransformedCollisionTileMap}.
|
||||
* @param tag The tag to allow collision layer filtering.
|
||||
* @returns The hit boxes for this tile.
|
||||
*/
|
||||
getHitBoxes(tag: string): PolygonVertices[] | undefined {
|
||||
const taggedHitBox = this.taggedHitBoxes.find(
|
||||
(hitbox) => hitbox.tag === tag
|
||||
);
|
||||
return taggedHitBox && taggedHitBox.polygons;
|
||||
}
|
||||
|
||||
/**
|
||||
* Animated tiles have a limitation:
|
||||
* they are only able to use frames arranged horizontally one next
|
||||
* to each other on the atlas.
|
||||
* @returns The number of frame in the tile animation.
|
||||
*/
|
||||
getAnimationLength(): integer {
|
||||
return this.animationLength;
|
||||
}
|
||||
}
|
62
SharedLibs/TileMapHelper/src/render/ResourceCache.ts
Normal file
62
SharedLibs/TileMapHelper/src/render/ResourceCache.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
/**
|
||||
* A cache of resources identified by a string.
|
||||
*
|
||||
* It ensures that a resource is never load twice.
|
||||
*/
|
||||
export class ResourceCache<T> {
|
||||
private _cachedValues: Map<string, T>;
|
||||
|
||||
/**
|
||||
* Several calls can happen before the resource is loaded.
|
||||
* This allows to stack them.
|
||||
*/
|
||||
private _callbacks: Map<string, Array<(value: T | null) => void>>;
|
||||
|
||||
constructor() {
|
||||
this._cachedValues = new Map<string, T>();
|
||||
this._callbacks = new Map<string, Array<(value: T | null) => void>>();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a resource through a call back.
|
||||
* @param key the resource identifier.
|
||||
* @param load load the resource in case of cache default.
|
||||
* Note that the load callback is used by `getOrLoad` and not by the caller.
|
||||
* @param callback called when the resource is ready.
|
||||
*/
|
||||
getOrLoad(
|
||||
key: string,
|
||||
load: (callback: (value: T | null) => void) => void,
|
||||
callback: (value: T | null) => void
|
||||
): void {
|
||||
// Check if the value is in the cache.
|
||||
{
|
||||
const value = this._cachedValues.get(key);
|
||||
if (value) {
|
||||
callback(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
// Check if the value is being loading.
|
||||
{
|
||||
const callbacks = this._callbacks.get(key);
|
||||
if (callbacks) {
|
||||
callbacks.push(callback);
|
||||
return;
|
||||
} else {
|
||||
this._callbacks.set(key, [callback]);
|
||||
}
|
||||
}
|
||||
|
||||
load((value) => {
|
||||
if (value) {
|
||||
this._cachedValues.set(key, value);
|
||||
}
|
||||
const callbacks = this._callbacks.get(key)!;
|
||||
this._callbacks.delete(key);
|
||||
for (const callback of callbacks) {
|
||||
callback(value);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
138
SharedLibs/TileMapHelper/src/render/TileMapManager.ts
Normal file
138
SharedLibs/TileMapHelper/src/render/TileMapManager.ts
Normal file
@@ -0,0 +1,138 @@
|
||||
import { ResourceCache } from "./ResourceCache";
|
||||
import { TiledMap } from "../tiled/TiledFormat";
|
||||
import { TiledTileMapLoader } from "../tiled/TiledTileMapLoader";
|
||||
import { EditableTileMap } from "../model/TileMapModel";
|
||||
import { TileTextureCache } from "./TileTextureCache";
|
||||
import { PixiTileMapHelper } from "./TileMapPixiHelper";
|
||||
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
|
||||
/**
|
||||
* A holder to share tile maps across the 2 extension objects.
|
||||
*
|
||||
* Every instance with the same files path in properties will
|
||||
* share the same {@link EditableTileMap} and {@link TileTextureCache}.
|
||||
*
|
||||
* @see {@link TileMapRuntimeManager}
|
||||
*/
|
||||
export class TileMapManager {
|
||||
private _tileMapCache: ResourceCache<EditableTileMap>;
|
||||
private _textureCacheCaches: ResourceCache<TileTextureCache>;
|
||||
|
||||
constructor() {
|
||||
this._tileMapCache = new ResourceCache<EditableTileMap>();
|
||||
this._textureCacheCaches = new ResourceCache<TileTextureCache>();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param instanceHolder Where to set the manager instance.
|
||||
* @returns The shared manager.
|
||||
*/
|
||||
static getManager(instanceHolder: Object): TileMapManager {
|
||||
// @ts-ignore
|
||||
if (!instanceHolder.tileMapCollisionMaskManager) {
|
||||
//Create the shared manager if necessary.
|
||||
// @ts-ignore
|
||||
instanceHolder.tileMapCollisionMaskManager = new TileMapManager();
|
||||
}
|
||||
// @ts-ignore
|
||||
return instanceHolder.tileMapCollisionMaskManager;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loadTiledMap The method that loads the Tiled JSON file in memory.
|
||||
* @param tileMapJsonResourceName The resource name of the tile map.
|
||||
* @param tileSetJsonResourceName The resource name of the tile set.
|
||||
* @param pako The zlib library.
|
||||
* @param callback A function called when the tile map is parsed.
|
||||
*/
|
||||
getOrLoadTileMap(
|
||||
loadTiledMap: (
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (tiledMap: TiledMap | null) => void
|
||||
) => void,
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
pako: any,
|
||||
callback: (tileMap: EditableTileMap | null) => void
|
||||
): void {
|
||||
const key = tileMapJsonResourceName + "|" + tileSetJsonResourceName;
|
||||
|
||||
this._tileMapCache.getOrLoad(
|
||||
key,
|
||||
(callback) => {
|
||||
loadTiledMap(
|
||||
tileMapJsonResourceName,
|
||||
tileSetJsonResourceName,
|
||||
(tiledMap: TiledMap | null) => {
|
||||
if (!tiledMap) {
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const collisionTileMap = TiledTileMapLoader.load(pako, tiledMap);
|
||||
callback(collisionTileMap);
|
||||
}
|
||||
);
|
||||
},
|
||||
callback
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param loadTiledMap The method that loads the Tiled JSON file in memory.
|
||||
* @param getTexture The method that loads the atlas image file in memory.
|
||||
* @param atlasImageResourceName The resource name of the atlas image.
|
||||
* @param tileMapJsonResourceName The resource name of the tile map.
|
||||
* @param tileSetJsonResourceName The resource name of the tile set.
|
||||
* @param callback A function called when the tiles textures are split.
|
||||
*/
|
||||
getOrLoadTextureCache(
|
||||
loadTiledMap: (
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (tiledMap: TiledMap | null) => void
|
||||
) => void,
|
||||
getTexture: (textureName: string) => PIXI.BaseTexture<PIXI.Resource>,
|
||||
atlasImageResourceName: string,
|
||||
tileMapJsonResourceName: string,
|
||||
tileSetJsonResourceName: string,
|
||||
callback: (textureCache: TileTextureCache | null) => void
|
||||
): void {
|
||||
const key =
|
||||
tileMapJsonResourceName +
|
||||
"|" +
|
||||
tileSetJsonResourceName +
|
||||
"|" +
|
||||
atlasImageResourceName;
|
||||
|
||||
this._textureCacheCaches.getOrLoad(
|
||||
key,
|
||||
(callback) => {
|
||||
loadTiledMap(
|
||||
tileMapJsonResourceName,
|
||||
tileSetJsonResourceName,
|
||||
(tiledMap: TiledMap | null) => {
|
||||
if (!tiledMap) {
|
||||
// loadTiledMap already log errors.
|
||||
callback(null);
|
||||
return;
|
||||
}
|
||||
|
||||
const atlasTexture = atlasImageResourceName
|
||||
? getTexture(atlasImageResourceName)
|
||||
: null;
|
||||
const textureCache = PixiTileMapHelper.parseAtlas(
|
||||
tiledMap,
|
||||
atlasTexture,
|
||||
getTexture
|
||||
);
|
||||
callback(textureCache);
|
||||
}
|
||||
);
|
||||
},
|
||||
callback
|
||||
);
|
||||
}
|
||||
}
|
273
SharedLibs/TileMapHelper/src/render/TileMapPixiHelper.ts
Normal file
273
SharedLibs/TileMapHelper/src/render/TileMapPixiHelper.ts
Normal file
@@ -0,0 +1,273 @@
|
||||
import { integer, float } from "../model/CommonTypes";
|
||||
import { TiledMap } from "../tiled/TiledFormat";
|
||||
import {
|
||||
EditableObjectLayer,
|
||||
EditableTileMap,
|
||||
EditableTileMapLayer,
|
||||
} from "../model/TileMapModel";
|
||||
import { TileTextureCache } from "./TileTextureCache";
|
||||
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
import { getTileIdFromTiledGUI } from "../tiled/TiledLoaderHelper";
|
||||
|
||||
export class PixiTileMapHelper {
|
||||
/**
|
||||
* Split an atlas image into Pixi textures.
|
||||
*
|
||||
* @param tiledMap A tile map exported from Tiled.
|
||||
* @param atlasTexture The texture containing the whole tile set.
|
||||
* @param getTexture A getter to load a texture. Used if atlasTexture is not specified.
|
||||
* @returns A textures cache.
|
||||
*/
|
||||
static parseAtlas(
|
||||
tiledMap: TiledMap,
|
||||
atlasTexture: PIXI.BaseTexture<PIXI.Resource> | null,
|
||||
getTexture: (textureName: string) => PIXI.BaseTexture<PIXI.Resource>
|
||||
): TileTextureCache | null {
|
||||
if (!tiledMap.tiledversion) {
|
||||
console.warn(
|
||||
"The loaded Tiled map does not contain a 'tiledversion' key. Are you sure this file has been exported from Tiled (mapeditor.org)?"
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
// We only handle tileset embedded in the tilemap. Warn if it's not the case.
|
||||
if (!tiledMap.tilesets.length || "source" in tiledMap.tilesets[0]) {
|
||||
console.warn(
|
||||
"The loaded Tiled map seems not to contain any tileset data (nothing in 'tilesets' key)."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
const tiledSet = tiledMap.tilesets[0];
|
||||
const {
|
||||
tilewidth,
|
||||
tileheight,
|
||||
tilecount,
|
||||
image,
|
||||
columns,
|
||||
spacing,
|
||||
margin,
|
||||
} = tiledSet;
|
||||
const firstGid = tiledSet.firstgid === undefined ? 1 : tiledSet.firstgid;
|
||||
if (!atlasTexture) atlasTexture = getTexture(image);
|
||||
|
||||
// We try to detect what size Tiled is expecting.
|
||||
const rows = tilecount / columns;
|
||||
const expectedAtlasWidth =
|
||||
tilewidth * columns + spacing * (columns - 1) + margin * 2;
|
||||
const expectedAtlasHeight =
|
||||
tileheight * rows + spacing * (rows - 1) + margin * 2;
|
||||
if (
|
||||
(atlasTexture.width !== 1 && expectedAtlasWidth !== atlasTexture.width) ||
|
||||
(atlasTexture.height !== 1 && expectedAtlasHeight !== atlasTexture.height)
|
||||
) {
|
||||
const expectedSize = expectedAtlasWidth + "x" + expectedAtlasHeight;
|
||||
const actualSize = atlasTexture.width + "x" + atlasTexture.height;
|
||||
console.warn(
|
||||
"It seems the atlas file was resized, which is not supported. It should be " +
|
||||
expectedSize +
|
||||
"px, but it's " +
|
||||
actualSize +
|
||||
" px."
|
||||
);
|
||||
return null;
|
||||
}
|
||||
|
||||
// Prepare the textures pointing to the base "Atlas" Texture for each tile.
|
||||
// Note that this cache can be augmented later with rotated/flipped
|
||||
// versions of the tile textures.
|
||||
const textureCache = new TileTextureCache();
|
||||
for (let tileSetIndex = 0; tileSetIndex < tilecount; tileSetIndex++) {
|
||||
const columnMultiplier = Math.floor(tileSetIndex % columns);
|
||||
const rowMultiplier = Math.floor(tileSetIndex / columns);
|
||||
const x = margin + columnMultiplier * (tilewidth + spacing);
|
||||
const y = margin + rowMultiplier * (tileheight + spacing);
|
||||
const tileId = getTileIdFromTiledGUI(firstGid + tileSetIndex);
|
||||
|
||||
try {
|
||||
const rect = new PIXI.Rectangle(x, y, tilewidth, tileheight);
|
||||
const texture = new PIXI.Texture(atlasTexture!, rect);
|
||||
|
||||
textureCache.setTexture(tileId, false, false, false, texture);
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"An error occurred while creating a PIXI.Texture to be used in a TileMap:",
|
||||
error
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return textureCache;
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-renders the tile map whenever its rendering settings have been changed
|
||||
*
|
||||
* @param pixiTileMap the tile map renderer
|
||||
* @param tileMap the tile map model
|
||||
* @param textureCache the tile set textures
|
||||
* @param displayMode What to display:
|
||||
* - only a single layer (`index`)
|
||||
* - only visible layers (`visible`)
|
||||
* - everything (`all`).
|
||||
* @param layerIndex If `displayMode` is set to `index`, the layer index to be
|
||||
* displayed.
|
||||
*/
|
||||
static updatePixiTileMap(
|
||||
untypedPixiTileMap: any,
|
||||
tileMap: EditableTileMap,
|
||||
textureCache: TileTextureCache,
|
||||
displayMode: "index" | "visible" | "all",
|
||||
layerIndex: number
|
||||
): void {
|
||||
// The extension doesn't handle the Pixi sub-namespace very well.
|
||||
const pixiTileMap = untypedPixiTileMap as PIXI.tilemap.CompositeRectTileLayer;
|
||||
if (!pixiTileMap) return;
|
||||
pixiTileMap.clear();
|
||||
|
||||
for (const layer of tileMap.getLayers()) {
|
||||
if (
|
||||
(displayMode === "index" && layerIndex !== layer.id) ||
|
||||
(displayMode === "visible" && !layer.isVisible())
|
||||
) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (layer instanceof EditableObjectLayer) {
|
||||
const objectLayer = layer as EditableObjectLayer;
|
||||
for (const object of objectLayer.objects) {
|
||||
const texture = textureCache.findTileTexture(
|
||||
object.getTileId(),
|
||||
object.isFlippedHorizontally(),
|
||||
object.isFlippedVertically(),
|
||||
object.isFlippedDiagonally()
|
||||
);
|
||||
if (texture) {
|
||||
pixiTileMap.addFrame(
|
||||
texture,
|
||||
object.x,
|
||||
object.y - objectLayer.tileMap.getTileHeight()
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (layer instanceof EditableTileMapLayer) {
|
||||
const tileLayer = layer as EditableTileMapLayer;
|
||||
|
||||
for (let y = 0; y < tileLayer.tileMap.getDimensionY(); y++) {
|
||||
for (let x = 0; x < tileLayer.tileMap.getDimensionX(); x++) {
|
||||
const tileWidth = tileLayer.tileMap.getTileWidth();
|
||||
const xPos = tileWidth * x;
|
||||
const yPos = tileLayer.tileMap.getTileHeight() * y;
|
||||
|
||||
const tileId = tileLayer.get(x, y);
|
||||
if (tileId === undefined) {
|
||||
continue;
|
||||
}
|
||||
const tileTexture = textureCache.findTileTexture(
|
||||
tileId,
|
||||
tileLayer.isFlippedHorizontally(x, y),
|
||||
tileLayer.isFlippedVertically(x, y),
|
||||
tileLayer.isFlippedDiagonally(x, y)
|
||||
);
|
||||
if (!tileTexture) {
|
||||
continue;
|
||||
}
|
||||
const pixiTilemapFrame = pixiTileMap.addFrame(
|
||||
tileTexture,
|
||||
xPos,
|
||||
yPos
|
||||
);
|
||||
|
||||
const tileDefinition = tileLayer.tileMap.getTileDefinition(tileId);
|
||||
|
||||
// Animated tiles have a limitation:
|
||||
// they are only able to use frames arranged horizontally one next
|
||||
// to each other on the atlas.
|
||||
if (tileDefinition && tileDefinition.getAnimationLength() > 0) {
|
||||
pixiTilemapFrame.tileAnimX(
|
||||
tileWidth,
|
||||
tileDefinition.getAnimationLength()
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Re-renders the collision mask
|
||||
*/
|
||||
static updatePixiCollisionMask(
|
||||
pixiGraphics: PIXI.Graphics,
|
||||
tileMap: EditableTileMap,
|
||||
typeFilter: string,
|
||||
outlineSize: integer,
|
||||
outlineColor: integer,
|
||||
outlineOpacity: float,
|
||||
fillColor: integer,
|
||||
fillOpacity: float
|
||||
): void {
|
||||
if (!pixiGraphics) return;
|
||||
pixiGraphics.clear();
|
||||
|
||||
for (const layer of tileMap.getLayers()) {
|
||||
const tileWidth = tileMap.getTileWidth();
|
||||
const tileHeight = tileMap.getTileHeight();
|
||||
|
||||
if (layer instanceof EditableTileMapLayer) {
|
||||
const tileLayer = layer as EditableTileMapLayer;
|
||||
|
||||
for (let y = 0; y < tileLayer.tileMap.getDimensionY(); y++) {
|
||||
for (let x = 0; x < tileLayer.tileMap.getDimensionX(); x++) {
|
||||
const xPos = tileWidth * x;
|
||||
const yPos = tileHeight * y;
|
||||
|
||||
const tileId = tileLayer.get(x, y)!;
|
||||
const isFlippedHorizontally = tileLayer.isFlippedHorizontally(x, y);
|
||||
const isFlippedVertically = tileLayer.isFlippedVertically(x, y);
|
||||
const isFlippedDiagonally = tileLayer.isFlippedDiagonally(x, y);
|
||||
const tileDefinition = tileLayer.tileMap.getTileDefinition(tileId);
|
||||
if (!tileDefinition) {
|
||||
continue;
|
||||
}
|
||||
const hitboxes = tileDefinition.getHitBoxes(typeFilter);
|
||||
if (!hitboxes) {
|
||||
continue;
|
||||
}
|
||||
pixiGraphics.lineStyle(outlineSize, outlineColor, outlineOpacity);
|
||||
for (const vertices of hitboxes) {
|
||||
if (vertices.length === 0) continue;
|
||||
|
||||
pixiGraphics.beginFill(fillColor, fillOpacity);
|
||||
for (let index = 0; index < vertices.length; index++) {
|
||||
let vertexX = vertices[index][0];
|
||||
let vertexY = vertices[index][1];
|
||||
if (isFlippedHorizontally) {
|
||||
vertexX = tileWidth - vertexX;
|
||||
}
|
||||
if (isFlippedVertically) {
|
||||
vertexY = tileHeight - vertexY;
|
||||
}
|
||||
if (isFlippedDiagonally) {
|
||||
const swap = vertexX;
|
||||
vertexX = vertexY;
|
||||
vertexY = swap;
|
||||
}
|
||||
if (index === 0) {
|
||||
pixiGraphics.moveTo(xPos + vertexX, yPos + vertexY);
|
||||
} else {
|
||||
pixiGraphics.lineTo(xPos + vertexX, yPos + vertexY);
|
||||
}
|
||||
}
|
||||
pixiGraphics.closePath();
|
||||
pixiGraphics.endFill();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
137
SharedLibs/TileMapHelper/src/render/TileTextureCache.ts
Normal file
137
SharedLibs/TileMapHelper/src/render/TileTextureCache.ts
Normal file
@@ -0,0 +1,137 @@
|
||||
import { integer } from "../model/CommonTypes";
|
||||
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
|
||||
/**
|
||||
* A cache to access the tile images.
|
||||
*
|
||||
* It's created by {@link PixiTileMapHelper.parseAtlas}
|
||||
* and used by {@link PixiTileMapHelper.updatePixiTileMap}.
|
||||
*/
|
||||
export class TileTextureCache {
|
||||
private static readonly flippedHorizontallyFlag = 0x80000000;
|
||||
private static readonly flippedVerticallyFlag = 0x40000000;
|
||||
private static readonly flippedDiagonallyFlag = 0x20000000;
|
||||
|
||||
private readonly _textures: Map<integer, PIXI.Texture>;
|
||||
|
||||
constructor() {
|
||||
this._textures = new Map<integer, PIXI.Texture>();
|
||||
}
|
||||
|
||||
setTexture(
|
||||
tileId: integer,
|
||||
flippedHorizontally: boolean,
|
||||
flippedVertically: boolean,
|
||||
flippedDiagonally: boolean,
|
||||
texture: PIXI.Texture
|
||||
): void {
|
||||
let globalTileUid = this._getGlobalId(
|
||||
tileId,
|
||||
flippedHorizontally,
|
||||
flippedVertically,
|
||||
flippedDiagonally
|
||||
);
|
||||
this._textures.set(globalTileUid, texture);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the texture to use for the tile with the specified uid, which can contains
|
||||
* information about rotation in bits 32, 31 and 30
|
||||
* (see https://doc.mapeditor.org/en/stable/reference/tmx-map-format/).
|
||||
*
|
||||
* @param tileId The tile identifier
|
||||
* @param flippedHorizontally true if the tile is flipped horizontally.
|
||||
* @param flippedVertically true if the tile is flipped vertically.
|
||||
* @param flippedDiagonally true if the tile is flipped diagonally.
|
||||
* @returns The texture for the given tile identifier and orientation.
|
||||
*/
|
||||
findTileTexture(
|
||||
tileId: integer,
|
||||
flippedHorizontally: boolean,
|
||||
flippedVertically: boolean,
|
||||
flippedDiagonally: boolean
|
||||
): PIXI.Texture | undefined {
|
||||
let globalTileUid = this._getGlobalId(
|
||||
tileId,
|
||||
flippedHorizontally,
|
||||
flippedVertically,
|
||||
flippedDiagonally
|
||||
);
|
||||
|
||||
if (this._textures.has(globalTileUid)) {
|
||||
return this._textures.get(globalTileUid);
|
||||
}
|
||||
// If the texture is not in the cache, it's potentially because its ID
|
||||
// is a flipped/rotated version of another ID.
|
||||
const unflippedTexture = this._textures.get(tileId);
|
||||
// If the tile still can't be found in the cache, it means the ID we got
|
||||
// is invalid.
|
||||
if (!unflippedTexture) return undefined;
|
||||
|
||||
// Clone the unflipped texture and save it in the cache
|
||||
const frame = unflippedTexture.frame.clone();
|
||||
const orig = unflippedTexture.orig.clone();
|
||||
if (flippedDiagonally) {
|
||||
const width = orig.width;
|
||||
orig.width = orig.height;
|
||||
orig.height = width;
|
||||
}
|
||||
const trim = orig.clone();
|
||||
|
||||
// Get the rotation "D8" number.
|
||||
// See https://pixijs.io/examples/#/textures/texture-rotate.js
|
||||
let rotate = 0;
|
||||
if (flippedDiagonally) {
|
||||
rotate = 10;
|
||||
if (!flippedHorizontally && flippedVertically) {
|
||||
rotate = 2;
|
||||
} else if (flippedHorizontally && !flippedVertically) {
|
||||
rotate = 6;
|
||||
} else if (flippedHorizontally && flippedVertically) {
|
||||
rotate = 14;
|
||||
}
|
||||
} else {
|
||||
rotate = 0;
|
||||
if (!flippedHorizontally && flippedVertically) {
|
||||
rotate = 8;
|
||||
} else if (flippedHorizontally && !flippedVertically) {
|
||||
rotate = 12;
|
||||
} else if (flippedHorizontally && flippedVertically) {
|
||||
rotate = 4;
|
||||
}
|
||||
}
|
||||
|
||||
const flippedTexture = new PIXI.Texture(
|
||||
unflippedTexture.baseTexture,
|
||||
frame,
|
||||
orig,
|
||||
trim,
|
||||
rotate
|
||||
);
|
||||
this._textures.set(globalTileUid, flippedTexture);
|
||||
return flippedTexture;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return the Tiled tile global uniq identifier.
|
||||
*/
|
||||
private _getGlobalId(
|
||||
tileId: integer,
|
||||
flippedHorizontally: boolean,
|
||||
flippedVertically: boolean,
|
||||
flippedDiagonally: boolean
|
||||
): integer {
|
||||
let globalTileUid = tileId;
|
||||
if (flippedHorizontally) {
|
||||
globalTileUid |= TileTextureCache.flippedHorizontallyFlag;
|
||||
}
|
||||
if (flippedVertically) {
|
||||
globalTileUid |= TileTextureCache.flippedVerticallyFlag;
|
||||
}
|
||||
if (flippedDiagonally) {
|
||||
globalTileUid |= TileTextureCache.flippedDiagonallyFlag;
|
||||
}
|
||||
return globalTileUid;
|
||||
}
|
||||
}
|
487
SharedLibs/TileMapHelper/src/tiled/TiledFormat.ts
Normal file
487
SharedLibs/TileMapHelper/src/tiled/TiledFormat.ts
Normal file
@@ -0,0 +1,487 @@
|
||||
import { float, integer } from "../model/CommonTypes";
|
||||
|
||||
/**
|
||||
* Tiled JSON format (https://www.mapeditor.org/).
|
||||
*/
|
||||
export type TiledMap = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
|
||||
backgroundcolor?: string;
|
||||
|
||||
/** The compression level to use for tile layer data (defaults to -1, which means to use the algorithm default) */
|
||||
compressionlevel: integer;
|
||||
|
||||
/** Number of tile rows */
|
||||
height: integer;
|
||||
|
||||
/** Length of the side of a hex tile in pixels (hexagonal maps only) */
|
||||
hexsidelength?: integer;
|
||||
|
||||
/** Whether the map has infinite dimensions */
|
||||
infinite: boolean;
|
||||
|
||||
/** Array of {@link TiledLayer} */
|
||||
layers: Array<TiledLayer>;
|
||||
|
||||
/** Auto-increments for each layer */
|
||||
nextlayerid: integer;
|
||||
|
||||
/** Auto-increments for each placed object */
|
||||
nextobjectid: integer;
|
||||
|
||||
/** `orthogonal`, `isometric`, `staggered` or `hexagonal` */
|
||||
orientation: string;
|
||||
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
|
||||
/** `right-down` (the default), `right-up`, `left-down` or `left-up` (currently only supported for orthogonal maps) */
|
||||
renderorder: string;
|
||||
|
||||
/** `x` or `y` (staggered / hexagonal maps only) */
|
||||
staggeraxis?: string;
|
||||
|
||||
/** `odd` or `even` (staggered / hexagonal maps only) */
|
||||
staggerindex?: string;
|
||||
|
||||
/** The Tiled version used to save the file */
|
||||
tiledversion: string;
|
||||
|
||||
/** Map grid height */
|
||||
tileheight: integer;
|
||||
|
||||
/** Array of {@link TiledTileset} */
|
||||
tilesets: Array<TiledTileset>;
|
||||
|
||||
/** Map grid width */
|
||||
tilewidth: integer;
|
||||
|
||||
/** `map` (since 1.0) */
|
||||
type: string;
|
||||
|
||||
/** The JSON format version (previously a number, saved as string since 1.6) */
|
||||
version: string;
|
||||
|
||||
/** Number of tile columns */
|
||||
width: integer;
|
||||
};
|
||||
|
||||
export type TiledLayer = {
|
||||
/** Array of {@link TiledChunk} (optional). `tilelayer` only. */
|
||||
chunks?: Array<TiledChunk>;
|
||||
|
||||
/** `zlib`, `gzip`, `zstd` (since Tiled 1.3) or empty (default). `tilelayer` only. */
|
||||
compression?: string;
|
||||
|
||||
/** Array of `unsigned`, `integer` (GIDs) or base64-encoded data. `tilelayer` only.*/
|
||||
data?: Array<integer> | string;
|
||||
|
||||
/** `topdown` (default) or `index`. `objectgroup` only. */
|
||||
draworder?: string;
|
||||
|
||||
/** `csv` (default) or `base64`. `tilelayer` only. */
|
||||
encoding?: string;
|
||||
|
||||
/** Row count. Same as map height for fixed-size maps. */
|
||||
height?: integer;
|
||||
|
||||
/** Incremental ID - unique across all layers */
|
||||
id?: integer;
|
||||
|
||||
/** Image used by this layer. `imagelayer` only. */
|
||||
image?: string;
|
||||
|
||||
/** Array of {@link TiledLayer}. `group` only. */
|
||||
layers?: Array<TiledLayer>;
|
||||
|
||||
/** Name assigned to this layer */
|
||||
name: string;
|
||||
|
||||
/** Array of {@link TiledObject}. `objectgroup` only. */
|
||||
objects?: Array<TiledObject>;
|
||||
|
||||
/** Horizontal layer offset in pixels (default: 0) */
|
||||
offsetx?: float;
|
||||
|
||||
/** Vertical layer offset in pixels (default: 0) */
|
||||
offsety?: float;
|
||||
|
||||
/** Value between 0 and 1 */
|
||||
opacity: float;
|
||||
|
||||
/** Horizontal {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
|
||||
parallaxx?: float;
|
||||
|
||||
/** Vertical {@link parallax factor} for this layer (default: 1). (since Tiled 1.5) */
|
||||
parallaxy?: float;
|
||||
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
|
||||
/** X coordinate where layer content starts (for infinite maps) */
|
||||
startx?: integer;
|
||||
|
||||
/** Y coordinate where layer content starts (for infinite maps) */
|
||||
starty?: integer;
|
||||
|
||||
/** Hex-formatted {@link tint color} (#RRGGBB or #AARRGGBB) that is multiplied with any graphics drawn by this layer or any child layers (optional). */
|
||||
tintcolor?: string;
|
||||
|
||||
/** Hex-formatted color (#RRGGBB) (optional). `imagelayer` only. */
|
||||
transparentcolor?: string;
|
||||
|
||||
/** `tilelayer`, `objectgroup`, `imagelayer` or `group` */
|
||||
type: string;
|
||||
|
||||
/** Whether layer is shown or hidden in editor */
|
||||
visible: boolean;
|
||||
|
||||
/** Column count. Same as map width for fixed-size maps. */
|
||||
width?: integer;
|
||||
|
||||
/** Horizontal layer offset in tiles. Always 0. */
|
||||
x: integer;
|
||||
|
||||
/** Vertical layer offset in tiles. Always 0. */
|
||||
y: integer;
|
||||
};
|
||||
|
||||
export type TiledChunk = {
|
||||
/** Array of `unsigned` `integer` (GIDs) or base64-encoded data */
|
||||
data: Array<integer> | string;
|
||||
|
||||
/** Height in tiles */
|
||||
height: integer;
|
||||
|
||||
/** Width in tiles */
|
||||
width: integer;
|
||||
|
||||
/** X coordinate in tiles */
|
||||
x: integer;
|
||||
|
||||
/** Y coordinate in tiles */
|
||||
y: integer;
|
||||
};
|
||||
|
||||
export type TiledObject = {
|
||||
/** The class of the object (renamed from type since 1.9, optional) */
|
||||
class?: string;
|
||||
|
||||
/** Used to mark an object as an ellipse */
|
||||
ellipse?: boolean;
|
||||
|
||||
/** Global tile ID, only if object represents a tile */
|
||||
gid?: integer;
|
||||
|
||||
/** Height in pixels. */
|
||||
height: float;
|
||||
|
||||
/** Incremental ID, unique across all objects */
|
||||
id: integer;
|
||||
|
||||
/** String assigned to name field in editor */
|
||||
name: string;
|
||||
|
||||
/** Used to mark an object as a point */
|
||||
point?: boolean;
|
||||
|
||||
/** Array of {@link TiledPoint}, in case the object is a polygon */
|
||||
polygon?: Array<TiledPoint>;
|
||||
|
||||
/** Array of {@link TiledPoint}, in case the object is a polyline */
|
||||
polyline?: Array<TiledPoint>;
|
||||
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
|
||||
/** Angle in degrees clockwise */
|
||||
rotation: float;
|
||||
|
||||
/** Reference to a template file, in case object is a {@link template instance} */
|
||||
template?: string;
|
||||
|
||||
/** Only used for text objects */
|
||||
text?: Text;
|
||||
|
||||
/** Whether object is shown in editor. */
|
||||
visible: boolean;
|
||||
|
||||
/** Width in pixels. */
|
||||
width: float;
|
||||
|
||||
/** X coordinate in pixels */
|
||||
x: float;
|
||||
|
||||
/** Y coordinate in pixels */
|
||||
y: float;
|
||||
};
|
||||
|
||||
export type TiledText = {
|
||||
/** Whether to use a bold font (default: `false`) */
|
||||
bold: boolean;
|
||||
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (default: `#000000`) */
|
||||
color: string;
|
||||
|
||||
/** Font family (default: `sans-serif`) */
|
||||
fontfamily: string;
|
||||
|
||||
/** Horizontal alignment (`center`, `right`, `justify` or `left` (default)) */
|
||||
halign: string;
|
||||
|
||||
/** Whether to use an italic font (default: `false`) */
|
||||
italic: boolean;
|
||||
|
||||
/** Whether to use kerning when placing characters (default: `true`) */
|
||||
kerning: boolean;
|
||||
|
||||
/** Pixel size of font (default: 16) */
|
||||
pixelsize: integer;
|
||||
|
||||
/** Whether to strike out the text (default: `false`) */
|
||||
strikeout: boolean;
|
||||
|
||||
/** Text */
|
||||
text: string;
|
||||
|
||||
/** Whether to underline the text (default: `false`) */
|
||||
underline: boolean;
|
||||
|
||||
/** Vertical alignment (`center`, `bottom` or `top` (default)) */
|
||||
valign: string;
|
||||
|
||||
/** Whether the text is wrapped within the object bounds (default: `false`) */
|
||||
wrap: boolean;
|
||||
};
|
||||
|
||||
export type TiledTileset = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) (optional) */
|
||||
backgroundcolor?: string;
|
||||
|
||||
/** The number of tile columns in the tileset */
|
||||
columns: integer;
|
||||
|
||||
/** GID corresponding to the first tile in the set */
|
||||
firstgid: integer;
|
||||
|
||||
/** (optional) */
|
||||
grid?: TiledGrid;
|
||||
|
||||
/** Image used for tiles in this set */
|
||||
image: string;
|
||||
|
||||
/** Height of source image in pixels */
|
||||
imageheight: integer;
|
||||
|
||||
/** Width of source image in pixels */
|
||||
imagewidth: integer;
|
||||
|
||||
/** Buffer between image edge and first tile (pixels) */
|
||||
margin: integer;
|
||||
|
||||
/** Name given to this tileset */
|
||||
name: string;
|
||||
|
||||
/** Alignment to use for tile objects (`unspecified` (default), `topleft`, `top`, `topright`, `left`, `center`, `right`, `bottomleft`, `bottom` or `bottomright`) (since 1.4) */
|
||||
objectalignment?: string;
|
||||
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
|
||||
/** The external file that contains this tilesets data */
|
||||
source?: string;
|
||||
|
||||
/** Spacing between adjacent tiles in image (pixels) */
|
||||
spacing: integer;
|
||||
|
||||
/** Array of {@link TiledTerrain} (optional) */
|
||||
terrains?: Array<TiledTerrain>;
|
||||
|
||||
/** The number of tiles in this tileset */
|
||||
tilecount: integer;
|
||||
|
||||
/** The Tiled version used to save the file */
|
||||
tiledversion: string;
|
||||
|
||||
/** Maximum height of tiles in this set */
|
||||
tileheight: integer;
|
||||
|
||||
/** (optional) */
|
||||
tileoffset?: TileOffset;
|
||||
|
||||
/** Array of {@link TiledTileDefinition} (optional) */
|
||||
tiles?: Array<TiledTileDefinition>;
|
||||
|
||||
/** Maximum width of tiles in this set */
|
||||
tilewidth: integer;
|
||||
|
||||
/** Allowed transformations (optional) */
|
||||
transformations?: TiledTransformations;
|
||||
|
||||
/** Hex-formatted color (#RRGGBB) (optional) */
|
||||
transparentcolor?: string;
|
||||
|
||||
/** `tileset` (for tileset files, since 1.0) */
|
||||
type: string;
|
||||
|
||||
/** The JSON format version (previously a number, saved as string since 1.6) */
|
||||
version: string;
|
||||
|
||||
/** Array of {@link TiledWangSet} (since 1.1.5) */
|
||||
wangsets?: Array<TiledWangSet>;
|
||||
};
|
||||
|
||||
export type TiledGrid = {
|
||||
/** Cell height of tile grid */
|
||||
height: integer;
|
||||
|
||||
/** `orthogonal` (default) or `isometric` */
|
||||
orientation: string;
|
||||
|
||||
/** Cell width of tile grid */
|
||||
width: integer;
|
||||
};
|
||||
|
||||
export type TileOffset = {
|
||||
/** Horizontal offset in pixels */
|
||||
x: integer;
|
||||
|
||||
/** Vertical offset in pixels (positive is down) */
|
||||
y: integer;
|
||||
};
|
||||
|
||||
export type TiledTransformations = {
|
||||
/** Tiles can be flipped horizontally */
|
||||
hflip: boolean;
|
||||
|
||||
/** Tiles can be flipped vertically */
|
||||
vflip: boolean;
|
||||
|
||||
/** Tiles can be rotated in 90-degree increments */
|
||||
rotate: boolean;
|
||||
|
||||
/** Whether untransformed tiles remain preferred, otherwise transformed tiles are used to produce more variations */
|
||||
preferuntransformed: boolean;
|
||||
};
|
||||
|
||||
export type TiledTileDefinition = {
|
||||
/** Array of {@link TiledTiles} */
|
||||
animation?: Array<TiledTileDefinition>;
|
||||
|
||||
/** The class of the tile (renamed from type since 1.9, optional) */
|
||||
class?: string;
|
||||
|
||||
/** Local ID of the tile */
|
||||
id: integer;
|
||||
|
||||
/** Image representing this tile (optional) */
|
||||
image?: string;
|
||||
|
||||
/** Height of the tile image in pixels */
|
||||
imageheight?: integer;
|
||||
|
||||
/** Width of the tile image in pixels */
|
||||
imagewidth?: integer;
|
||||
|
||||
/** Layer with type Tiled`objectgroup`, when collision shapes are specified (optional) */
|
||||
objectgroup?: TiledLayer;
|
||||
|
||||
/** Percentage chance this tile is chosen when competing with others in the editor (optional) */
|
||||
probability?: float;
|
||||
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties?: Array<TiledProperty>;
|
||||
|
||||
/** Index of terrain for each corner of tile (optional) */
|
||||
terrain?: Array<integer>;
|
||||
};
|
||||
|
||||
export type TiledFrame = {
|
||||
/** Frame duration in milliseconds */
|
||||
duration: integer;
|
||||
|
||||
/** Local tile ID representing this frame */
|
||||
tileid: integer;
|
||||
};
|
||||
|
||||
export type TiledTerrain = {
|
||||
/** Name of terrain */
|
||||
name: string;
|
||||
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
|
||||
/** Local ID of tile representing terrain */
|
||||
tile: integer;
|
||||
};
|
||||
|
||||
export type TiledWangSet = {
|
||||
/** Array of {@link TiledWangColor} */
|
||||
colors: Array<TiledWangColor>;
|
||||
|
||||
/** Name of the Wang set */
|
||||
name: string;
|
||||
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
|
||||
/** Local ID of tile representing the Wang set */
|
||||
tile: integer;
|
||||
|
||||
/** Array of {@link TiledWangTile} */
|
||||
wangtiles: Array<TiledWangTile>;
|
||||
};
|
||||
|
||||
export type TiledWangColor = {
|
||||
/** Hex-formatted color (#RRGGBB or #AARRGGBB) */
|
||||
color: string;
|
||||
|
||||
/** Name of the Wang color */
|
||||
name: string;
|
||||
|
||||
/** Probability used when randomizing */
|
||||
probability: float;
|
||||
|
||||
/** Array of {@link TiledProperty} */
|
||||
properties: Array<TiledProperty>;
|
||||
|
||||
/** Local ID of tile representing the Wang color */
|
||||
tile: integer;
|
||||
};
|
||||
|
||||
export type TiledWangTile = {
|
||||
/** Local ID of tile */
|
||||
tileid: integer;
|
||||
|
||||
/** Array of Wang color indexes (`uchar[8]`) */
|
||||
wangid: Array<integer>;
|
||||
};
|
||||
|
||||
export type TiledObjectTemplate = {
|
||||
/** `template` */
|
||||
type: string;
|
||||
|
||||
/** External tileset used by the template (optional) */
|
||||
tileset?: TiledTileset;
|
||||
|
||||
/** The object instantiated by this template */
|
||||
object: Object;
|
||||
};
|
||||
|
||||
export type TiledProperty = {
|
||||
/** Name of the property */
|
||||
name: string;
|
||||
|
||||
/** type of the property (`string` (default), `integer`, `float`, `boolean`, `color` or `file` (since 0.16, with `color` and `file` added in 0.17)) */
|
||||
type: string;
|
||||
|
||||
/** Value of the property */
|
||||
value: string | number;
|
||||
};
|
||||
|
||||
export type TiledPoint = {
|
||||
/** X coordinate in pixels */
|
||||
x: float;
|
||||
|
||||
/** Y coordinate in pixels */
|
||||
y: float;
|
||||
};
|
108
SharedLibs/TileMapHelper/src/tiled/TiledLoaderHelper.ts
Normal file
108
SharedLibs/TileMapHelper/src/tiled/TiledLoaderHelper.ts
Normal file
@@ -0,0 +1,108 @@
|
||||
import { integer } from "../model/CommonTypes";
|
||||
import { TiledLayer } from "./TiledFormat";
|
||||
|
||||
/**
|
||||
* Decodes a layer data, which can sometimes be store as a compressed base64 string
|
||||
* by Tiled.
|
||||
* See https://doc.mapeditor.org/en/stable/reference/tmx-map-format/#data.
|
||||
* @param pako The zlib library.
|
||||
* @param layer The layer data from a Tiled JSON.
|
||||
* @returns The decoded layer data.
|
||||
*/
|
||||
export const decodeBase64LayerData = (pako: any, layer: TiledLayer) => {
|
||||
const { data, compression } = layer;
|
||||
const dataBase64 = data as string;
|
||||
if (!dataBase64) {
|
||||
// The layer data is not encoded.
|
||||
return data as number[];
|
||||
}
|
||||
let index = 4;
|
||||
const decodedData: integer[] = [];
|
||||
let step1 = atob(dataBase64)
|
||||
.split("")
|
||||
.map(function (x) {
|
||||
return x.charCodeAt(0);
|
||||
});
|
||||
try {
|
||||
const decodeArray = (arr: integer[] | Uint8Array, index: integer) =>
|
||||
(arr[index] +
|
||||
(arr[index + 1] << 8) +
|
||||
(arr[index + 2] << 16) +
|
||||
(arr[index + 3] << 24)) >>>
|
||||
0;
|
||||
|
||||
if (compression === "zlib") {
|
||||
const binData = new Uint8Array(step1);
|
||||
const decompressedData = pako.inflate(binData);
|
||||
while (index <= decompressedData.length) {
|
||||
decodedData.push(decodeArray(decompressedData, index - 4));
|
||||
index += 4;
|
||||
}
|
||||
} else if (compression === "zstd") {
|
||||
console.error(
|
||||
"Zstandard compression is not supported for layers in a Tilemap. Use instead zlib compression or no compression."
|
||||
);
|
||||
return null;
|
||||
} else {
|
||||
while (index <= step1.length) {
|
||||
decodedData.push(decodeArray(step1, index - 4));
|
||||
index += 4;
|
||||
}
|
||||
}
|
||||
return decodedData;
|
||||
} catch (error) {
|
||||
console.error(
|
||||
"Failed to decompress and unzip base64 layer.data string",
|
||||
error
|
||||
);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
export type TiledGID = {
|
||||
id: integer;
|
||||
flippedHorizontally: boolean;
|
||||
flippedVertically: boolean;
|
||||
flippedDiagonally: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* Extract information about the rotation of a tile from the tile id.
|
||||
* @param globalTileUid The Tiled tile global uniq identifier.
|
||||
* @returns The tile identifier and orientation.
|
||||
*/
|
||||
export const extractTileUidFlippedStates = (
|
||||
globalTileUid: integer
|
||||
): TiledGID => {
|
||||
const FLIPPED_HORIZONTALLY_FLAG = 0x80000000;
|
||||
const FLIPPED_VERTICALLY_FLAG = 0x40000000;
|
||||
const FLIPPED_DIAGONALLY_FLAG = 0x20000000;
|
||||
|
||||
const flippedHorizontally = globalTileUid & FLIPPED_HORIZONTALLY_FLAG;
|
||||
const flippedVertically = globalTileUid & FLIPPED_VERTICALLY_FLAG;
|
||||
const flippedDiagonally = globalTileUid & FLIPPED_DIAGONALLY_FLAG;
|
||||
const tileUid = getTileIdFromTiledGUI(
|
||||
globalTileUid &
|
||||
~(
|
||||
FLIPPED_HORIZONTALLY_FLAG |
|
||||
FLIPPED_VERTICALLY_FLAG |
|
||||
FLIPPED_DIAGONALLY_FLAG
|
||||
)
|
||||
);
|
||||
|
||||
return {
|
||||
id: tileUid,
|
||||
flippedHorizontally: !!flippedHorizontally,
|
||||
flippedVertically: !!flippedVertically,
|
||||
flippedDiagonally: !!flippedDiagonally,
|
||||
};
|
||||
};
|
||||
|
||||
/**
|
||||
* Tiled use 0 as null, we do too but it's black boxed.
|
||||
* This is why the id needs to be decremented.
|
||||
* @return the tile identifier used in {@link TilMapModel}.
|
||||
*/
|
||||
export const getTileIdFromTiledGUI = (
|
||||
tiledGUI: number | undefined
|
||||
): number | undefined => (tiledGUI === 0 ? undefined : tiledGUI - 1);
|
516
SharedLibs/TileMapHelper/src/tiled/TiledTileMapLoader.spec.ts
Normal file
516
SharedLibs/TileMapHelper/src/tiled/TiledTileMapLoader.spec.ts
Normal file
@@ -0,0 +1,516 @@
|
||||
import { EditableTileMap, EditableTileMapLayer } from "../model/TileMapModel";
|
||||
import { TiledMap } from "./TiledFormat";
|
||||
import { TiledTileMapLoader } from "./TiledTileMapLoader";
|
||||
|
||||
describe("TiledTileMapLoader", function () {
|
||||
describe("without a collision mask", function () {
|
||||
// Built from an actual json file exported by Tiled.
|
||||
const tiledMap: TiledMap = {
|
||||
compressionlevel: -1,
|
||||
height: 2,
|
||||
infinite: false,
|
||||
layers: [
|
||||
{
|
||||
data: [1, 0, 2, 0, 0, 1, 0, 2],
|
||||
height: 2,
|
||||
id: 1,
|
||||
name: "Tile Layer 1",
|
||||
opacity: 1,
|
||||
type: "tilelayer",
|
||||
visible: true,
|
||||
width: 4,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
nextlayerid: 2,
|
||||
nextobjectid: 1,
|
||||
orientation: "orthogonal",
|
||||
renderorder: "right-down",
|
||||
tiledversion: "1.7.2",
|
||||
tileheight: 8,
|
||||
tilesets: [
|
||||
{
|
||||
firstgid: 1,
|
||||
columns: 2,
|
||||
image: "MiniTiledSet.png",
|
||||
imageheight: 8,
|
||||
imagewidth: 16,
|
||||
margin: 0,
|
||||
name: "new tileset",
|
||||
spacing: 0,
|
||||
tilecount: 2,
|
||||
tiledversion: "1.7.2",
|
||||
tileheight: 8,
|
||||
tilewidth: 8,
|
||||
type: "tileset",
|
||||
version: "1.6",
|
||||
},
|
||||
],
|
||||
tilewidth: 8,
|
||||
type: "map",
|
||||
version: "1.6",
|
||||
width: 4,
|
||||
};
|
||||
|
||||
const tileMap: EditableTileMap = TiledTileMapLoader.load(null, tiledMap);
|
||||
|
||||
it("can load map dimensions", function () {
|
||||
expect(tileMap.getDimensionX()).to.be(4);
|
||||
expect(tileMap.getDimensionY()).to.be(2);
|
||||
expect(tileMap.getTileHeight()).to.be(8);
|
||||
expect(tileMap.getTileWidth()).to.be(8);
|
||||
expect(tileMap.getWidth()).to.be(32);
|
||||
expect(tileMap.getHeight()).to.be(16);
|
||||
});
|
||||
|
||||
it("can load a tile set", function () {
|
||||
expect(tileMap.getTileDefinition(0)).to.be.ok();
|
||||
expect(tileMap.getTileDefinition(1)).to.be.ok();
|
||||
|
||||
expect(tileMap.getTileDefinition(2)).not.to.be.ok();
|
||||
});
|
||||
|
||||
it("can load a tile map content", function () {
|
||||
const layers = new Array(...tileMap.getLayers());
|
||||
expect(layers.length).to.be(1);
|
||||
// TODO Change the model to avoid casts?
|
||||
const layer = layers[0] as EditableTileMapLayer;
|
||||
// TODO Add the layer name as it can be useful for events?
|
||||
expect(layer.id).to.be(1);
|
||||
expect(layer.isVisible()).to.be(true);
|
||||
|
||||
expect(layer.get(0, 0)).to.be(0);
|
||||
expect(layer.get(1, 0)).to.be(undefined);
|
||||
expect(layer.get(2, 0)).to.be(1);
|
||||
expect(layer.get(3, 0)).to.be(undefined);
|
||||
|
||||
expect(layer.get(0, 1)).to.be(undefined);
|
||||
expect(layer.get(1, 1)).to.be(0);
|
||||
expect(layer.get(2, 1)).to.be(undefined);
|
||||
expect(layer.get(3, 1)).to.be(1);
|
||||
});
|
||||
});
|
||||
|
||||
describe("with a collision mask", function () {
|
||||
// Built from an actual json file exported by Tiled.
|
||||
const tiledMap: TiledMap = {
|
||||
compressionlevel: -1,
|
||||
height: 2,
|
||||
infinite: false,
|
||||
layers: [
|
||||
{
|
||||
data: [1, 3, 4, 5, 3, 2, 0, 1073741829],
|
||||
height: 2,
|
||||
id: 1,
|
||||
name: "Tile Layer 1",
|
||||
opacity: 1,
|
||||
type: "tilelayer",
|
||||
visible: true,
|
||||
width: 4,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
nextlayerid: 2,
|
||||
nextobjectid: 1,
|
||||
orientation: "orthogonal",
|
||||
renderorder: "right-down",
|
||||
tiledversion: "1.7.2",
|
||||
tileheight: 8,
|
||||
tilesets: [
|
||||
{
|
||||
firstgid: 1,
|
||||
columns: 2,
|
||||
image: "MiniTiledSet.png",
|
||||
imageheight: 16,
|
||||
imagewidth: 16,
|
||||
margin: 0,
|
||||
name: "new tileset",
|
||||
spacing: 0,
|
||||
tilecount: 4,
|
||||
tiledversion: "1.7.2",
|
||||
tileheight: 8,
|
||||
tiles: [
|
||||
// Contains a rectangle
|
||||
{
|
||||
id: 0,
|
||||
objectgroup: {
|
||||
draworder: "index",
|
||||
name: "",
|
||||
objects: [
|
||||
{
|
||||
height: 8,
|
||||
id: 1,
|
||||
name: "",
|
||||
rotation: 0,
|
||||
class: "",
|
||||
visible: true,
|
||||
width: 8,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
opacity: 1,
|
||||
type: "objectgroup",
|
||||
visible: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
class: "obstacle",
|
||||
},
|
||||
|
||||
// The tile with id == 1 is missing
|
||||
// because it doesn't have any collision mask.
|
||||
|
||||
// Contains a polygon.
|
||||
{
|
||||
id: 2,
|
||||
objectgroup: {
|
||||
draworder: "index",
|
||||
name: "",
|
||||
objects: [
|
||||
{
|
||||
height: 0,
|
||||
id: 1,
|
||||
name: "",
|
||||
polygon: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
x: 8,
|
||||
y: -8,
|
||||
},
|
||||
{
|
||||
x: 0,
|
||||
y: -8,
|
||||
},
|
||||
],
|
||||
rotation: 0,
|
||||
class: "",
|
||||
visible: true,
|
||||
width: 0,
|
||||
x: 0,
|
||||
y: 8,
|
||||
},
|
||||
],
|
||||
opacity: 1,
|
||||
type: "objectgroup",
|
||||
visible: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
class: "obstacle",
|
||||
},
|
||||
// Contains 2 polygons, one is a rotated.
|
||||
{
|
||||
id: 3,
|
||||
objectgroup: {
|
||||
draworder: "index",
|
||||
id: 2,
|
||||
name: "",
|
||||
objects: [
|
||||
{
|
||||
height: 0,
|
||||
id: 1,
|
||||
name: "",
|
||||
polygon: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
x: 4,
|
||||
y: 4,
|
||||
},
|
||||
{
|
||||
x: 8,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
rotation: 0,
|
||||
class: "",
|
||||
visible: true,
|
||||
width: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
height: 0,
|
||||
id: 3,
|
||||
name: "",
|
||||
polygon: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
x: 4,
|
||||
y: 4,
|
||||
},
|
||||
{
|
||||
x: 8,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
rotation: 180,
|
||||
class: "",
|
||||
visible: true,
|
||||
width: 0,
|
||||
x: 8,
|
||||
y: 8,
|
||||
},
|
||||
],
|
||||
opacity: 1,
|
||||
type: "objectgroup",
|
||||
visible: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
class: "obstacle",
|
||||
},
|
||||
// Contains hitboxes for obstacle and lava.
|
||||
{
|
||||
id: 4,
|
||||
objectgroup: {
|
||||
draworder: "index",
|
||||
id: 2,
|
||||
name: "",
|
||||
objects: [
|
||||
{
|
||||
height: 0,
|
||||
id: 1,
|
||||
name: "",
|
||||
polygon: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
x: 0,
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
x: 8,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
rotation: 0,
|
||||
class: "obstacle",
|
||||
visible: true,
|
||||
width: 0,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
height: 0,
|
||||
id: 2,
|
||||
name: "",
|
||||
polygon: [
|
||||
{
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
{
|
||||
x: 0,
|
||||
y: 8,
|
||||
},
|
||||
{
|
||||
x: 8,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
rotation: 180,
|
||||
class: "lava",
|
||||
visible: true,
|
||||
width: 0,
|
||||
x: 8,
|
||||
y: 8,
|
||||
},
|
||||
],
|
||||
opacity: 1,
|
||||
type: "objectgroup",
|
||||
visible: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
},
|
||||
// Contains hitboxes for lava only
|
||||
{
|
||||
id: 5,
|
||||
objectgroup: {
|
||||
draworder: "index",
|
||||
id: 2,
|
||||
name: "",
|
||||
objects: [
|
||||
{
|
||||
height: 8,
|
||||
id: 1,
|
||||
name: "",
|
||||
rotation: 0,
|
||||
class: "lava",
|
||||
visible: true,
|
||||
width: 8,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
],
|
||||
opacity: 1,
|
||||
type: "objectgroup",
|
||||
visible: true,
|
||||
x: 0,
|
||||
y: 0,
|
||||
},
|
||||
class: "lava",
|
||||
},
|
||||
],
|
||||
tilewidth: 8,
|
||||
type: "tileset",
|
||||
version: "1.6",
|
||||
},
|
||||
],
|
||||
tilewidth: 8,
|
||||
type: "map",
|
||||
version: "1.6",
|
||||
width: 4,
|
||||
};
|
||||
|
||||
const tileMap: EditableTileMap = TiledTileMapLoader.load(null, tiledMap);
|
||||
|
||||
it("can load map dimensions", function () {
|
||||
expect(tileMap.getDimensionX()).to.be(4);
|
||||
expect(tileMap.getDimensionY()).to.be(2);
|
||||
expect(tileMap.getTileHeight()).to.be(8);
|
||||
expect(tileMap.getTileWidth()).to.be(8);
|
||||
expect(tileMap.getWidth()).to.be(32);
|
||||
expect(tileMap.getHeight()).to.be(16);
|
||||
});
|
||||
|
||||
it("can load a tile set with a rectangle collision mask", function () {
|
||||
const tileDefinition = tileMap.getTileDefinition(0);
|
||||
expect(tileDefinition).to.be.ok();
|
||||
expect(tileDefinition.hasTag("obstacle")).to.be(true);
|
||||
expect(tileDefinition.hasTag("lava")).to.be(false);
|
||||
expect(tileDefinition.getHitBoxes("obstacle")).to.be.eql([
|
||||
[
|
||||
[0, 0],
|
||||
[0, 8],
|
||||
[8, 8],
|
||||
[8, 0],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it("can load a tile set with an empty collision mask", function () {
|
||||
const tileDefinition = tileMap.getTileDefinition(1);
|
||||
expect(tileDefinition).to.be.ok();
|
||||
expect(tileDefinition.hasTag("obstacle")).to.be(false);
|
||||
expect(tileDefinition.hasTag("lava")).to.be(false);
|
||||
});
|
||||
|
||||
it("can load a tile set with a polygon collision mask", function () {
|
||||
{
|
||||
const tileDefinition = tileMap.getTileDefinition(2);
|
||||
expect(tileDefinition).to.be.ok();
|
||||
expect(tileDefinition.hasTag("obstacle")).to.be(true);
|
||||
expect(tileDefinition.hasTag("lava")).to.be(false);
|
||||
expect(tileDefinition.getHitBoxes("obstacle")).to.be.eql([
|
||||
[
|
||||
[0, 8],
|
||||
[8, 0],
|
||||
[0, 0],
|
||||
],
|
||||
]);
|
||||
}
|
||||
});
|
||||
|
||||
it("can load a tile set with a 2 polygons collision mask", function () {
|
||||
const tileDefinition = tileMap.getTileDefinition(3);
|
||||
expect(tileDefinition).to.be.ok();
|
||||
expect(tileDefinition.hasTag("obstacle")).to.be(true);
|
||||
expect(tileDefinition.hasTag("lava")).to.be(false);
|
||||
expect(tileDefinition.getHitBoxes("obstacle")).to.be.eql([
|
||||
[
|
||||
[0, 0],
|
||||
[4, 4],
|
||||
[8, 0],
|
||||
],
|
||||
[
|
||||
[8, 8],
|
||||
[4, 4],
|
||||
[0, 8],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it("can load a tile set with several collision mask filter tags", function () {
|
||||
const tileDefinition = tileMap.getTileDefinition(4);
|
||||
expect(tileDefinition).to.be.ok();
|
||||
expect(tileDefinition.hasTag("obstacle")).to.be(true);
|
||||
expect(tileDefinition.hasTag("lava")).to.be(true);
|
||||
expect(tileDefinition.getHitBoxes("obstacle")).to.be.eql([
|
||||
[
|
||||
[0, 0],
|
||||
[0, 8],
|
||||
[8, 0],
|
||||
],
|
||||
]);
|
||||
expect(tileDefinition.getHitBoxes("lava")).to.be.eql([
|
||||
[
|
||||
[8, 8],
|
||||
[8, 0],
|
||||
[0, 8],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it("can load a tile set with only the other filter tag", function () {
|
||||
const tileDefinition = tileMap.getTileDefinition(5);
|
||||
expect(tileDefinition).to.be.ok();
|
||||
expect(tileDefinition.hasTag("obstacle")).to.be(false);
|
||||
expect(tileDefinition.hasTag("lava")).to.be(true);
|
||||
expect(tileDefinition.getHitBoxes("lava")).to.be.eql([
|
||||
[
|
||||
[0, 0],
|
||||
[0, 8],
|
||||
[8, 8],
|
||||
[8, 0],
|
||||
],
|
||||
]);
|
||||
});
|
||||
|
||||
it("can load a tile set", function () {
|
||||
expect(tileMap.getTileDefinition(6)).not.to.be.ok();
|
||||
});
|
||||
|
||||
it("can load a tile map content", function () {
|
||||
const layers = new Array(...tileMap.getLayers());
|
||||
expect(layers.length).to.be(1);
|
||||
const layer = layers[0] as EditableTileMapLayer;
|
||||
expect(layer.id).to.be(1);
|
||||
expect(layer.isVisible()).to.be(true);
|
||||
|
||||
expect(layer.get(0, 0)).to.be(0);
|
||||
expect(layer.get(1, 0)).to.be(2);
|
||||
expect(layer.get(2, 0)).to.be(3);
|
||||
expect(layer.get(3, 0)).to.be(4);
|
||||
|
||||
expect(layer.get(0, 1)).to.be(2);
|
||||
expect(layer.get(1, 1)).to.be(1);
|
||||
expect(layer.get(2, 1)).to.be(undefined);
|
||||
expect(layer.get(3, 1)).to.be(4);
|
||||
expect(layer.isFlippedVertically(3, 1)).to.be(true);
|
||||
expect(layer.isFlippedHorizontally(3, 1)).to.be(false);
|
||||
expect(layer.isFlippedDiagonally(3, 1)).to.be(false);
|
||||
});
|
||||
|
||||
it("can detect that a point is in a tile that contains a mask with a given tag", function () {
|
||||
// The point is in the black square with an hitbox.
|
||||
expect(tileMap.pointIsInsideTile(4, 4, "obstacle")).to.be(true);
|
||||
// The point is in wite square without any hitbox.
|
||||
expect(tileMap.pointIsInsideTile(12, 12, "obstacle")).to.be(false);
|
||||
});
|
||||
});
|
||||
});
|
189
SharedLibs/TileMapHelper/src/tiled/TiledTileMapLoader.ts
Normal file
189
SharedLibs/TileMapHelper/src/tiled/TiledTileMapLoader.ts
Normal file
@@ -0,0 +1,189 @@
|
||||
import { integer, PolygonVertices } from "../model/CommonTypes";
|
||||
import {
|
||||
EditableTileMap,
|
||||
TileDefinition,
|
||||
TileObject,
|
||||
} from "../model/TileMapModel";
|
||||
import { TiledMap } from "./TiledFormat";
|
||||
import {
|
||||
extractTileUidFlippedStates,
|
||||
decodeBase64LayerData,
|
||||
getTileIdFromTiledGUI,
|
||||
} from "./TiledLoaderHelper";
|
||||
|
||||
/**
|
||||
* It creates a {@link EditableTileMap} from a Tiled JSON.
|
||||
*/
|
||||
export class TiledTileMapLoader {
|
||||
static load(pako: any, tiledMap: TiledMap): EditableTileMap | null {
|
||||
if (!tiledMap.tiledversion) {
|
||||
console.warn(
|
||||
"The loaded Tiled map does not contain a 'tiledversion' key. Are you sure this file has been exported from Tiled (mapeditor.org)?"
|
||||
);
|
||||
|
||||
return null;
|
||||
}
|
||||
|
||||
const definitions = new Map<integer, TileDefinition>();
|
||||
for (const tiledSet of tiledMap.tilesets) {
|
||||
const firstGid = tiledSet.firstgid === undefined ? 1 : tiledSet.firstgid;
|
||||
if (tiledSet.tiles) {
|
||||
for (const tile of tiledSet.tiles) {
|
||||
const tileDefinition = new TileDefinition(
|
||||
tile.animation ? tile.animation.length : 0
|
||||
);
|
||||
if (tile.objectgroup) {
|
||||
for (const object of tile.objectgroup.objects) {
|
||||
const tag = object.class || tile.class;
|
||||
if (!tag || tag.length === 0) {
|
||||
continue;
|
||||
}
|
||||
let polygon: PolygonVertices | null = null;
|
||||
if (object.polygon) {
|
||||
const angle = (object.rotation * Math.PI) / 180;
|
||||
let cos = Math.cos(angle);
|
||||
let sin = Math.sin(angle);
|
||||
// Avoid rounding errors around 0.
|
||||
if (cos === -1 || cos === 1) {
|
||||
sin = 0;
|
||||
}
|
||||
if (sin === -1 || sin === 1) {
|
||||
cos = 0;
|
||||
}
|
||||
polygon = object.polygon.map((point) => [
|
||||
object.x + point.x * cos - point.y * sin,
|
||||
object.y + point.x * sin + point.y * cos,
|
||||
]);
|
||||
//TODO check that polygons are convex or split them?
|
||||
}
|
||||
// TODO handle ellipses by creating a polygon?
|
||||
// Make an object property for the number of vertices or always create 8 ones?
|
||||
// Will the user need the same vertices number for every ellipse?
|
||||
else if (
|
||||
object.x !== undefined &&
|
||||
object.y !== undefined &&
|
||||
object.width !== undefined &&
|
||||
object.height !== undefined
|
||||
) {
|
||||
polygon = [
|
||||
[object.x, object.y],
|
||||
[object.x, object.y + object.height],
|
||||
[object.x + object.width, object.y + object.height],
|
||||
[object.x + object.width, object.y],
|
||||
];
|
||||
}
|
||||
if (polygon) {
|
||||
tileDefinition.add(tag, polygon);
|
||||
}
|
||||
}
|
||||
} else if (tile.class && tile.class.length > 0) {
|
||||
// When there is no shape, default to the whole tile.
|
||||
const polygon: PolygonVertices = [
|
||||
[0, 0],
|
||||
[0, tiledMap.tileheight],
|
||||
[tiledMap.tilewidth, tiledMap.tileheight],
|
||||
[tiledMap.tilewidth, 0],
|
||||
];
|
||||
tileDefinition.add(tile.class, polygon);
|
||||
}
|
||||
definitions.set(
|
||||
getTileIdFromTiledGUI(firstGid + tile.id),
|
||||
tileDefinition
|
||||
);
|
||||
}
|
||||
}
|
||||
for (let tileIndex = 0; tileIndex < tiledSet.tilecount; tileIndex++) {
|
||||
const tileId = getTileIdFromTiledGUI(
|
||||
firstGid + tileIndex
|
||||
);
|
||||
if (!definitions.has(tileId)) {
|
||||
definitions.set(tileId, new TileDefinition(0));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const collisionTileMap = new EditableTileMap(
|
||||
tiledMap.tilewidth,
|
||||
tiledMap.tileheight,
|
||||
tiledMap.width,
|
||||
tiledMap.height,
|
||||
definitions
|
||||
);
|
||||
|
||||
for (const tiledLayer of tiledMap.layers) {
|
||||
if (tiledLayer.type === "objectgroup") {
|
||||
const objectLayer = collisionTileMap.addObjectLayer(tiledLayer.id);
|
||||
objectLayer.setVisible(tiledLayer.visible);
|
||||
for (const tiledObject of tiledLayer.objects) {
|
||||
if (!tiledObject.visible || !tiledObject.gid) {
|
||||
// Objects layer are nice to put decorations but dynamic objects
|
||||
// must be done with GDevelop objects.
|
||||
// So, there is no point to load it as there won't be any action to
|
||||
// make objects visible individually.
|
||||
continue;
|
||||
}
|
||||
const tileGid = extractTileUidFlippedStates(tiledObject.gid);
|
||||
const object = new TileObject(
|
||||
tiledObject.x,
|
||||
tiledObject.y,
|
||||
tileGid.id
|
||||
);
|
||||
objectLayer.add(object);
|
||||
object.setFlippedHorizontally(tileGid.flippedHorizontally);
|
||||
object.setFlippedVertically(tileGid.flippedVertically);
|
||||
object.setFlippedDiagonally(tileGid.flippedDiagonally);
|
||||
}
|
||||
} else if (tiledLayer.type === "tilelayer") {
|
||||
let tileSlotIndex = 0;
|
||||
let layerData: integer[] | null = null;
|
||||
|
||||
if (tiledLayer.encoding === "base64") {
|
||||
layerData = decodeBase64LayerData(pako, tiledLayer);
|
||||
if (!layerData) {
|
||||
console.warn("Failed to uncompress layer.data");
|
||||
}
|
||||
} else {
|
||||
layerData = tiledLayer.data as integer[];
|
||||
}
|
||||
if (layerData) {
|
||||
const collisionTileLayer = collisionTileMap.addTileLayer(
|
||||
tiledLayer.id
|
||||
);
|
||||
collisionTileLayer.setVisible(tiledLayer.visible);
|
||||
// TODO handle layer offset
|
||||
|
||||
for (let y = 0; y < tiledLayer.height; y++) {
|
||||
for (let x = 0; x < tiledLayer.width; x++) {
|
||||
// The "globalTileUid" is the tile UID with encoded
|
||||
// bits about the flipping/rotation of the tile.
|
||||
const globalTileUid = layerData[tileSlotIndex];
|
||||
// Extract the tile UID and the texture.
|
||||
const tileUid = extractTileUidFlippedStates(globalTileUid);
|
||||
if (tileUid.id !== undefined) {
|
||||
collisionTileLayer.setTile(x, y, tileUid.id);
|
||||
collisionTileLayer.setFlippedHorizontally(
|
||||
x,
|
||||
y,
|
||||
tileUid.flippedHorizontally
|
||||
);
|
||||
collisionTileLayer.setFlippedVertically(
|
||||
x,
|
||||
y,
|
||||
tileUid.flippedVertically
|
||||
);
|
||||
collisionTileLayer.setFlippedDiagonally(
|
||||
x,
|
||||
y,
|
||||
tileUid.flippedDiagonally
|
||||
);
|
||||
}
|
||||
tileSlotIndex += 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return collisionTileMap;
|
||||
}
|
||||
}
|
26
SharedLibs/TileMapHelper/tsconfig.json
Normal file
26
SharedLibs/TileMapHelper/tsconfig.json
Normal file
@@ -0,0 +1,26 @@
|
||||
{
|
||||
"compileOnSave": false,
|
||||
"compilerOptions": {
|
||||
"module": "esnext",
|
||||
"noImplicitAny": true,
|
||||
"outDir": "./dist/tsc",
|
||||
"target": "ES5",
|
||||
"sourceMap": true,
|
||||
"declaration": true,
|
||||
"declarationMap": true,
|
||||
"declarationDir": "./dts/",
|
||||
"types" : [
|
||||
"mocha",
|
||||
"expect.js",
|
||||
"offscreencanvas"
|
||||
],
|
||||
"lib": ["DOM", "ES5", "ES6"],
|
||||
"esModuleInterop": false,
|
||||
"downlevelIteration": true,
|
||||
"moduleResolution": "node",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
},
|
||||
"exclude": [
|
||||
"node_modules"
|
||||
]
|
||||
}
|
29
SharedLibs/TileMapHelper/types/global-pixi-tilemap.d.ts
vendored
Normal file
29
SharedLibs/TileMapHelper/types/global-pixi-tilemap.d.ts
vendored
Normal file
@@ -0,0 +1,29 @@
|
||||
// https://github.com/pixijs/tilemap
|
||||
|
||||
import {
|
||||
CanvasTileRenderer,
|
||||
CompositeRectTileLayer,
|
||||
GraphicsLayer,
|
||||
IMultiTextureOptions,
|
||||
MultiTextureResource,
|
||||
RectTileGeom,
|
||||
RectTileLayer,
|
||||
RectTileShader,
|
||||
TileRenderer,
|
||||
ZLayer,
|
||||
} from './pixi-tilemap';
|
||||
|
||||
declare module 'pixi.js' {
|
||||
export namespace tilemap {
|
||||
export { CanvasTileRenderer };
|
||||
export { CompositeRectTileLayer };
|
||||
export { GraphicsLayer };
|
||||
export { IMultiTextureOptions };
|
||||
export { MultiTextureResource };
|
||||
export { RectTileGeom };
|
||||
export { RectTileLayer };
|
||||
export { RectTileShader };
|
||||
export { TileRenderer };
|
||||
export { ZLayer };
|
||||
}
|
||||
}
|
22
SharedLibs/TileMapHelper/types/global-pixi.d.ts
vendored
Normal file
22
SharedLibs/TileMapHelper/types/global-pixi.d.ts
vendored
Normal file
@@ -0,0 +1,22 @@
|
||||
import * as PixiModule from "pixi.js";
|
||||
|
||||
declare global {
|
||||
/**
|
||||
* This namespace contains the `PIXI` module, and should always be used to access to PixiJS apis.
|
||||
*
|
||||
* Rationale:
|
||||
* PixiJS typings are sadly not available anymore as an "ambient namespace" in TypeScript.
|
||||
* To expose the typings as a global object, we need to use the "export import" syntax.
|
||||
* This only works in a namespace, that we call GlobalPIXIModule.
|
||||
*
|
||||
* This at least allows to easily find in the codebase all the dependencies on PixiJS in the GDJS
|
||||
* runtime and extensions.
|
||||
*
|
||||
* Note that we also modified the bundled `pixi.js` file to create a global variable called
|
||||
* `GlobalPIXIModule`, containing the `PIXI` object.
|
||||
* Note that we could use `export as namespace`, but this crash the TypeScript compiler.
|
||||
*/
|
||||
namespace GlobalPIXIModule {
|
||||
export import PIXI = PixiModule;
|
||||
}
|
||||
}
|
193
SharedLibs/TileMapHelper/types/pixi-tilemap.d.ts
vendored
Normal file
193
SharedLibs/TileMapHelper/types/pixi-tilemap.d.ts
vendored
Normal file
@@ -0,0 +1,193 @@
|
||||
import PIXI = GlobalPIXIModule.PIXI;
|
||||
|
||||
export declare class CanvasTileRenderer {
|
||||
renderer: PIXI.Renderer;
|
||||
tileAnim: number[];
|
||||
dontUseTransform: boolean;
|
||||
constructor(renderer: PIXI.Renderer);
|
||||
}
|
||||
|
||||
export declare class CompositeRectTileLayer extends PIXI.Container {
|
||||
constructor(zIndex?: number, bitmaps?: Array<PIXI.Texture>, texPerChild?: number);
|
||||
z: number;
|
||||
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
|
||||
zIndex: number;
|
||||
modificationMarker: number;
|
||||
shadowColor: Float32Array;
|
||||
_globalMat: PIXI.Matrix;
|
||||
_lastLayer: RectTileLayer;
|
||||
texPerChild: number;
|
||||
tileAnim: number[];
|
||||
initialize(zIndex?: number, bitmaps?: Array<PIXI.Texture>, texPerChild?: number): void;
|
||||
setBitmaps(bitmaps: Array<PIXI.Texture>): void;
|
||||
clear(): void;
|
||||
addRect(textureIndex: number, u: number, v: number, x: number, y: number, tileWidth: number, tileHeight: number, animX?: number, animY?: number, rotate?: number, animWidth?: number, animHeight?: number): this;
|
||||
tileRotate(rotate: number): this;
|
||||
tileAnimX(offset: number, count: number): this;
|
||||
tileAnimY(offset: number, count: number): this;
|
||||
addFrame(texture_: PIXI.Texture | String | number, x: number, y: number, animX?: number, animY?: number, animWidth?: number, animHeight?: number): this;
|
||||
renderCanvas(renderer: any): void;
|
||||
render(renderer: PIXI.Renderer): void;
|
||||
isModified(anim: boolean): boolean;
|
||||
clearModify(): void;
|
||||
}
|
||||
|
||||
export declare const Constant: {
|
||||
maxTextures: number;
|
||||
bufferSize: number;
|
||||
boundSize: number;
|
||||
boundCountPerBuffer: number;
|
||||
use32bitIndex: boolean;
|
||||
SCALE_MODE: PIXI.SCALE_MODES;
|
||||
DO_CLEAR: boolean;
|
||||
};
|
||||
|
||||
export declare function fillSamplers(shader: TilemapShader, maxTextures: number): void;
|
||||
|
||||
export declare function generateFragmentSrc(maxTextures: number, fragmentSrc: string): string;
|
||||
|
||||
export declare function generateSampleSrc(maxTextures: number): string;
|
||||
|
||||
export declare class GraphicsLayer extends PIXI.Graphics {
|
||||
constructor(zIndex: number);
|
||||
renderCanvas(renderer: any): void;
|
||||
isModified(anim: boolean): boolean;
|
||||
clearModify(): void;
|
||||
}
|
||||
|
||||
export declare interface IMultiTextureOptions {
|
||||
boundCountPerBuffer: number;
|
||||
boundSize: number;
|
||||
bufferSize: number;
|
||||
DO_CLEAR?: boolean;
|
||||
}
|
||||
|
||||
export declare class MultiTextureResource extends PIXI.Resource {
|
||||
constructor(options: IMultiTextureOptions);
|
||||
DO_CLEAR: boolean;
|
||||
boundSize: number;
|
||||
_clearBuffer: Uint8Array;
|
||||
bind(baseTexture: PIXI.BaseTexture): void;
|
||||
baseTex: PIXI.BaseTexture;
|
||||
boundSprites: Array<PIXI.Sprite>;
|
||||
dirties: Array<number>;
|
||||
setTexture(ind: number, texture: PIXI.Texture): void;
|
||||
upload(renderer: PIXI.Renderer, texture: PIXI.BaseTexture, glTexture: PIXI.GLTexture): boolean;
|
||||
}
|
||||
|
||||
export declare const pixi_tilemap: {
|
||||
CanvasTileRenderer: typeof CanvasTileRenderer;
|
||||
CompositeRectTileLayer: typeof CompositeRectTileLayer;
|
||||
Constant: {
|
||||
maxTextures: number;
|
||||
bufferSize: number;
|
||||
boundSize: number;
|
||||
boundCountPerBuffer: number;
|
||||
use32bitIndex: boolean;
|
||||
SCALE_MODE: PIXI.SCALE_MODES;
|
||||
DO_CLEAR: boolean;
|
||||
};
|
||||
GraphicsLayer: typeof GraphicsLayer;
|
||||
MultiTextureResource: typeof MultiTextureResource;
|
||||
RectTileLayer: typeof RectTileLayer;
|
||||
TilemapShader: typeof TilemapShader;
|
||||
RectTileShader: typeof RectTileShader;
|
||||
RectTileGeom: typeof RectTileGeom;
|
||||
TileRenderer: typeof TileRenderer;
|
||||
ZLayer: typeof ZLayer;
|
||||
};
|
||||
|
||||
export declare const POINT_STRUCT_SIZE = 12;
|
||||
|
||||
export declare class RectTileGeom extends PIXI.Geometry {
|
||||
vertSize: number;
|
||||
vertPerQuad: number;
|
||||
stride: number;
|
||||
lastTimeAccess: number;
|
||||
constructor();
|
||||
buf: PIXI.Buffer;
|
||||
}
|
||||
|
||||
export declare class RectTileLayer extends PIXI.Container {
|
||||
constructor(zIndex: number, texture: PIXI.Texture | Array<PIXI.Texture>);
|
||||
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
|
||||
zIndex: number;
|
||||
modificationMarker: number;
|
||||
_$_localBounds: PIXI.Bounds;
|
||||
shadowColor: Float32Array;
|
||||
_globalMat: PIXI.Matrix;
|
||||
pointsBuf: Array<number>;
|
||||
hasAnim: boolean;
|
||||
textures: Array<PIXI.Texture>;
|
||||
offsetX: number;
|
||||
offsetY: number;
|
||||
compositeParent: boolean;
|
||||
initialize(zIndex: number, textures: PIXI.Texture | Array<PIXI.Texture>): void;
|
||||
clear(): void;
|
||||
addFrame(texture_: PIXI.Texture | String | number, x: number, y: number, animX: number, animY: number): boolean;
|
||||
addRect(textureIndex: number, u: number, v: number, x: number, y: number, tileWidth: number, tileHeight: number, animX?: number, animY?: number, rotate?: number, animCountX?: number, animCountY?: number): this;
|
||||
tileRotate(rotate: number): void;
|
||||
tileAnimX(offset: number, count: number): void;
|
||||
tileAnimY(offset: number, count: number): void;
|
||||
renderCanvas(renderer: any): void;
|
||||
renderCanvasCore(renderer: any): void;
|
||||
vbId: number;
|
||||
vb: RectTileGeom;
|
||||
vbBuffer: ArrayBuffer;
|
||||
vbArray: Float32Array;
|
||||
vbInts: Uint32Array;
|
||||
destroyVb(): void;
|
||||
render(renderer: PIXI.Renderer): void;
|
||||
renderWebGLCore(renderer: PIXI.Renderer, plugin: TileRenderer): void;
|
||||
isModified(anim: boolean): boolean;
|
||||
clearModify(): void;
|
||||
protected _calculateBounds(): void;
|
||||
getLocalBounds(rect?: PIXI.Rectangle): PIXI.Rectangle;
|
||||
destroy(options?: any): void;
|
||||
}
|
||||
|
||||
export declare class RectTileShader extends TilemapShader {
|
||||
constructor(maxTextures: number);
|
||||
}
|
||||
|
||||
export declare abstract class TilemapShader extends PIXI.Shader {
|
||||
maxTextures: number;
|
||||
constructor(maxTextures: number, shaderVert: string, shaderFrag: string);
|
||||
}
|
||||
|
||||
export declare class TileRenderer extends PIXI.ObjectRenderer {
|
||||
renderer: PIXI.Renderer;
|
||||
gl: WebGLRenderingContext;
|
||||
sn: number;
|
||||
indexBuffer: PIXI.Buffer;
|
||||
ibLen: number;
|
||||
tileAnim: number[];
|
||||
texLoc: Array<number>;
|
||||
rectShader: RectTileShader;
|
||||
texResources: Array<MultiTextureResource>;
|
||||
constructor(renderer: PIXI.Renderer);
|
||||
initBounds(): void;
|
||||
bindTexturesWithoutRT(renderer: PIXI.Renderer, shader: TilemapShader, textures: Array<PIXI.Texture>): void;
|
||||
bindTextures(renderer: PIXI.Renderer, shader: TilemapShader, textures: Array<PIXI.Texture>): void;
|
||||
start(): void;
|
||||
createVb(): RectTileGeom;
|
||||
checkIndexBuffer(size: number, vb?: RectTileGeom): void;
|
||||
getShader(): TilemapShader;
|
||||
destroy(): void;
|
||||
}
|
||||
|
||||
export declare class ZLayer extends PIXI.Container {
|
||||
constructor(tilemap: PIXI.Container, zIndex: number);
|
||||
tilemap: any;
|
||||
z: number;
|
||||
// @ts-ignore Maybe it's a compatibility issue with the PIXI version we are using
|
||||
zIndex: number;
|
||||
_previousLayers: number;
|
||||
canvasBuffer: HTMLCanvasElement;
|
||||
_tempRender: any;
|
||||
_lastAnimationFrame: number;
|
||||
layerTransform: PIXI.Matrix;
|
||||
clear(): void;
|
||||
cacheIfDirty(): void;
|
||||
renderCanvas(renderer: any): void;
|
||||
}
|
21
newIDE/app/public/JsPlatform/Extensions/tile_map.svg
Normal file
21
newIDE/app/public/JsPlatform/Extensions/tile_map.svg
Normal file
@@ -0,0 +1,21 @@
|
||||
<svg width="32" height="32" version="1.1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<g fill="#243286">
|
||||
<rect x="8" y="16" width="8" height="8"/>
|
||||
<rect x="-1.91e-6" y="24" width="8" height="8"/>
|
||||
<rect x="16" y="24" width="8" height="8"/>
|
||||
<rect x="24" y="16" width="8" height="8"/>
|
||||
<rect x="16" y="8" width="8" height="8"/>
|
||||
<rect x="24" y="4.6e-7" width="8" height="8"/>
|
||||
</g>
|
||||
<g fill="#2c7ea1">
|
||||
<rect x="24" y="24" width="8" height="8"/>
|
||||
<rect x="16" y="16" width="8" height="8"/>
|
||||
<rect x="24" y="8" width="8" height="8"/>
|
||||
<rect x="8" y="24" width="8" height="8"/>
|
||||
<rect y="16" width="8" height="8"/>
|
||||
<rect x="8" y="8" width="8" height="8"/>
|
||||
<rect x="16" width="8" height="8"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 760 B |
@@ -0,0 +1,38 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="24" height="24" version="1.1" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<g transform="scale(.75)">
|
||||
<g fill="#243286">
|
||||
<rect x="8" y="16" width="8" height="8"/>
|
||||
<rect y="24" width="8" height="8"/>
|
||||
<rect x="16" y="24" width="8" height="8"/>
|
||||
<rect x="24" y="16" width="8" height="8"/>
|
||||
<rect x="16" y="8" width="8" height="8"/>
|
||||
<rect x="24" y="5.47e-7" width="8" height="8"/>
|
||||
</g>
|
||||
<g fill="#2c7ea1">
|
||||
<rect x="24" y="24" width="8" height="8"/>
|
||||
<rect x="16" y="16" width="8" height="8"/>
|
||||
<rect x="24" y="8" width="8" height="8"/>
|
||||
<rect x="8" y="24" width="8" height="8"/>
|
||||
<rect y="16" width="8" height="8"/>
|
||||
<rect x="8" y="8" width="8" height="8"/>
|
||||
<rect x="16" width="8" height="8"/>
|
||||
</g>
|
||||
</g>
|
||||
<g fill="#2c7ea1">
|
||||
<path d="m19 1v4h4v-4zm1 1h2v2h-2z"/>
|
||||
<path d="m13 7v4h4v-4zm1 1h2v2h-2z"/>
|
||||
<path d="m7 13v4h4v-4zm1 1h2v2h-2z"/>
|
||||
<path d="m1 19v4h4v-4zm1 1h2v2h-2z"/>
|
||||
</g>
|
||||
<g fill="#243286" fill-rule="evenodd">
|
||||
<path d="m17 1-4 4h4zm-1 2.4v0.6h-0.6z"/>
|
||||
<path d="m23 7-4 4v-4zm-2.4 1h-0.6v0.6z"/>
|
||||
<path d="m11 7-4 4h4zm-1 2.4v0.6h-0.6z"/>
|
||||
<path d="m5 13-4 4h4zm-1 2.4v0.6h-0.6z"/>
|
||||
<path d="m11 19-4 4v-4zm-2.4 1h-0.6v0.6z"/>
|
||||
<path d="m17 13-4 4v-4zm-2.4 1h-0.6v0.6z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@@ -0,0 +1,36 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<svg width="32" height="32" version="1.1" viewBox="0 0 32 32" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<g fill="#243286">
|
||||
<rect x="8" y="16" width="8" height="8"/>
|
||||
<rect y="24" width="8" height="8"/>
|
||||
<rect x="16" y="24" width="8" height="8"/>
|
||||
<rect x="24" y="16" width="8" height="8"/>
|
||||
<rect x="16" y="8" width="8" height="8"/>
|
||||
<rect x="24" y="5.47e-7" width="8" height="8"/>
|
||||
</g>
|
||||
<g fill="#2c7ea1">
|
||||
<rect x="24" y="24" width="8" height="8"/>
|
||||
<rect x="16" y="16" width="8" height="8"/>
|
||||
<rect x="24" y="8" width="8" height="8"/>
|
||||
<rect x="8" y="24" width="8" height="8"/>
|
||||
<rect y="16" width="8" height="8"/>
|
||||
<rect x="8" y="8" width="8" height="8"/>
|
||||
<rect x="16" width="8" height="8"/>
|
||||
</g>
|
||||
<g fill="#2c7ea1">
|
||||
<path d="m9 17v6h6v-6zm1 1h4v4h-4z"/>
|
||||
<path d="m17 9v6h6v-6zm1 1h4v4h-4z"/>
|
||||
<path d="m1 25v6h6v-6zm1 1h4v4h-4z"/>
|
||||
<path d="m25 1v6h6v-6zm1 1h4v4h-4z"/>
|
||||
</g>
|
||||
<g fill="#243286" fill-rule="evenodd">
|
||||
<path d="m15 9-6 6h6zm-1 2.4v2.6h-2.6z"/>
|
||||
<path d="m22.6 1-6 6h6zm-1 2.4v2.6h-2.6z"/>
|
||||
<path d="m7 17-6 6h6zm-1 2.4v2.6h-2.6z"/>
|
||||
<path d="m31 9-6 6 1e-6 -6zm-2.4 1h-2.6v2.6z"/>
|
||||
<path d="m23 17-6 6 1e-6 -6zm-2.4 1h-2.6v2.6z"/>
|
||||
<path d="m15 25-6 6 1e-6 -6zm-2.4 1h-2.6v2.6z"/>
|
||||
</g>
|
||||
</g>
|
||||
</svg>
|
After Width: | Height: | Size: 1.3 KiB |
@@ -112,7 +112,7 @@ const jsExtensions = [
|
||||
// $FlowExpectedError - this path is ignored for Flow.
|
||||
'pixi-tilemap/dist/pixi-tilemap.umd': require('GDJS-for-web-app-only/Runtime/Extensions/TileMap/pixi-tilemap/dist/pixi-tilemap.umd'),
|
||||
// $FlowExpectedError - this path is ignored for Flow.
|
||||
'pixi-tilemap-helper': require('GDJS-for-web-app-only/Runtime/Extensions/TileMap/pixi-tilemap-helper'),
|
||||
TileMapHelper: require('GDJS-for-web-app-only/Runtime/Extensions/TileMap/helper/TileMapHelper.js'),
|
||||
// $FlowExpectedError - this path is ignored for Flow.
|
||||
'pako/dist/pako.min': require('GDJS-for-web-app-only/Runtime/Extensions/TileMap/pako/dist/pako.min'),
|
||||
},
|
||||
|
Reference in New Issue
Block a user