Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
30 changes: 19 additions & 11 deletions src/resource/filter-util.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

export const byNameCaseInsensitive = (name?: string) => {
return <TObject extends {name?: string}>(object: TObject) => {
return <TObject extends { name?: string }>(object: TObject) => {
if (object?.name && name) {
return object.name.toLocaleLowerCase().localeCompare(name.toLocaleLowerCase()) === 0;
}
Expand All @@ -9,7 +9,7 @@ export const byNameCaseInsensitive = (name?: string) => {
}

export const byClassCaseInsensitive = (className?: string) => {
return <TObject extends {class?: string}>(object: TObject) => {
return <TObject extends { class?: string }>(object: TObject) => {
if (object?.class && className) {
return object.class.toLocaleLowerCase().localeCompare(className.toLocaleLowerCase()) === 0;
}
Expand All @@ -20,28 +20,36 @@ export const byClassCaseInsensitive = (className?: string) => {
const copyPropsLowerCase = (properties: Map<string, string | number | boolean>) => {
const lowercase = new Map<string, string | number | boolean>();
for (let [key, value] of properties) {
let normalizedValue = value;
if (typeof value === 'string') {
normalizedValue = value.toLocaleLowerCase();
}
lowercase.set(key.toLocaleLowerCase(), normalizedValue);
lowercase.set(key.toLocaleLowerCase(), value);
}
return lowercase;
}

export const byPropertyCaseInsensitive = (propertyName: string, value?: any) => {
return <TObject extends {properties: Map<string, string | number | boolean>}>(object: TObject) => {
export const byProperty = (propertyName: string, value?: any, valueMatchInsensitve = true) => {
return <TObject extends { properties: Map<string, string | number | boolean> }>(object: TObject) => {
const lowercase = copyPropsLowerCase(object.properties);

if (value !== undefined) {
let normalizedValue = value;
if (typeof value === 'string') {
normalizedValue = value.toLocaleLowerCase();
normalizedValue = valueMatchInsensitve ? value.toLocaleLowerCase() : value;
}

const maybeValue = lowercase.get(propertyName.toLocaleLowerCase());
if (typeof maybeValue === 'string') {
return (valueMatchInsensitve ? maybeValue.toLocaleLowerCase() : maybeValue) === normalizedValue;
}

return lowercase.get(propertyName.toLocaleLowerCase()) === normalizedValue;
} else {
return lowercase.has(propertyName.toLocaleLowerCase());
}
}
}
}

export const byPropertyValueMatcher = (propertyName: string, matchValue: (val: any) => boolean) => {
return <TObject extends { properties: Map<string, string | number | boolean> }>(object: TObject) => {
const lowercase = copyPropsLowerCase(object.properties);
return matchValue(lowercase.get(propertyName.toLocaleLowerCase()));
}
}
48 changes: 35 additions & 13 deletions src/resource/iso-tile-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ import { ExcaliburTiledProperties } from "./excalibur-properties";
import { TiledLayerDataComponent } from "./tiled-layer-component";
import { Layer } from "./layer";
import { Tile } from "./tileset";
import { byClassCaseInsensitive, byPropertyCaseInsensitive } from "./filter-util";
import { byClassCaseInsensitive, byProperty, byPropertyValueMatcher } from "./filter-util";

export interface IsometricTileInfo {
/**
Expand Down Expand Up @@ -94,21 +94,36 @@ export class IsoTileLayer implements Layer {
}

/**
* Returns the excalibur tiles that match a tiled gid
* Returns the excalibur tiles that match a tiled property and optional value
* @param name
* @param value
* @param [valueMatchInsensitive=true]
*/
getTilesByGid(gid: number): IsometricTileInfo[] {
return this._gidToTileInfo.get(gid) ?? [];
getTilesByProperty(name: string, value?: any, valueMatchInsensitive = true): IsometricTileInfo[] {
const tiles = this.isometricMap.tiles.filter(t => {
const maybeTiled = t.data.get(ExcaliburTiledProperties.TileData.Tiled) as Tile | undefined;
if (maybeTiled) {
return byProperty(name, value, valueMatchInsensitive)(maybeTiled);
}
return false;
});

return tiles.map(t => ({
exTile: t,
tiledTile: t.data.get(ExcaliburTiledProperties.TileData.Tiled)
}))
}

/**
* Returns the excalibur tiles that match a tiled class name
* @param className
* Get the tiles that match the property name and the value matcher returns true
* @param name
* @param valueMatcher
*/
getTilesByClassName(className: string): IsometricTileInfo[] {
getTilesByPropertyValueMatcher(name: string, valueMatcher: (val: any) => boolean): IsometricTileInfo[] {
const tiles = this.isometricMap.tiles.filter(t => {
const maybeTiled = t.data.get(ExcaliburTiledProperties.TileData.Tiled) as Tile | undefined;
if (maybeTiled) {
return byClassCaseInsensitive(className)(maybeTiled);
return byPropertyValueMatcher(name, valueMatcher)(maybeTiled);
}
return false;
});
Expand All @@ -119,16 +134,23 @@ export class IsoTileLayer implements Layer {
}))
}


/**
* Returns the excalibur tiles that match a tiled property and optional value
* @param name
* @param value
* Returns the excalibur tiles that match a tiled gid
*/
getTilesByGid(gid: number): IsometricTileInfo[] {
return this._gidToTileInfo.get(gid) ?? [];
}

/**
* Returns the excalibur tiles that match a tiled class name
* @param className
*/
getTilesByProperty(name: string, value?: any): IsometricTileInfo[] {
getTilesByClassName(className: string): IsometricTileInfo[] {
const tiles = this.isometricMap.tiles.filter(t => {
const maybeTiled = t.data.get(ExcaliburTiledProperties.TileData.Tiled) as Tile | undefined;
if (maybeTiled) {
return byPropertyCaseInsensitive(name, value)(maybeTiled);
return byClassCaseInsensitive(className)(maybeTiled);
}
return false;
});
Expand Down
29 changes: 24 additions & 5 deletions src/resource/object-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { InsertedTile, PluginObject, TemplateObject, Text, Polygon, Rectangle, E
import { TiledObjectLayer } from "../parser/tiled-parser";
import { FactoryProps, TiledResource } from "./tiled-resource";
import { mapProps } from "./properties";
import { byClassCaseInsensitive, byNameCaseInsensitive, byPropertyCaseInsensitive } from "./filter-util";
import { byClassCaseInsensitive, byNameCaseInsensitive, byProperty, byPropertyValueMatcher } from "./filter-util";
import { Tileset } from "./tileset";
import { ExcaliburTiledProperties } from "./excalibur-properties";
import { TiledDataComponent } from "./tiled-data-component";
Expand Down Expand Up @@ -55,21 +55,40 @@ export class ObjectLayer implements Layer {
* Search for a tiled object that has a property name, and optionally specify a value
* @param propertyName
* @param value
* @param [valueMatchInsensitive=true]
* @returns
*/
getObjectsByProperty(propertyName: string, value?: any): PluginObject[] {
getObjectsByProperty(propertyName: string, value?: any, valueMatchInsensitive = true): PluginObject[] {
if (!this._loaded) this._logLoadedWarning('getObjectsByProperty');
return this.objects.filter(byPropertyCaseInsensitive(propertyName, value));
return this.objects.filter(byProperty(propertyName, value, valueMatchInsensitive));
}

/**
* Get the objects that match the property name and the value matcher returns true
* @param name
* @param valueMatcher
*/
getObjectsByPropertyValueMatcher(propertyName: string, valueMatcher: (val: any) => boolean): PluginObject[] {
if (!this._loaded) this._logLoadedWarning('getObjectsByPropertyValueMatcher');
return this.objects.filter(byPropertyValueMatcher(propertyName, valueMatcher));
}


/**
* Search for actors that were created from tiled objects
* @returns
*/
getEntitiesByProperty(propertyName: string, value?: any): Entity[] {
getEntitiesByProperty(propertyName: string, value?: any, valueMatchInsensitve = true): Entity[] {
if (!this._loaded) this._logLoadedWarning('getEntitiesByProperty');
return this.getObjectsByProperty(propertyName, value).map(o => this._objectToEntity.get(o)).filter(a => !!a) as Entity[];
return this.getObjectsByProperty(propertyName, value, valueMatchInsensitve).map(o => this._objectToEntity.get(o)).filter(a => !!a) as Entity[];
}

getEntitiesByPropertyValueMatcher(propertyName: string, valueMatcher: (val: any) => boolean): Entity[] {
if (!this._loaded) this._logLoadedWarning('getEntitiesByPropertyValueMatcher');
return this.getObjectsByProperty(propertyName, valueMatcher).map(o => this._objectToEntity.get(o)).filter(a => !!a) as Entity[];
}


/**
* Search for an Tiled object by it's Tiled class name
* @returns
Expand Down
46 changes: 33 additions & 13 deletions src/resource/tile-layer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { ExcaliburTiledProperties } from "./excalibur-properties";
import { TiledLayerDataComponent } from "./tiled-layer-component";
import { Layer } from "./layer";
import { Tile } from "./tileset";
import { byClassCaseInsensitive, byPropertyCaseInsensitive } from "./filter-util";
import { byClassCaseInsensitive, byProperty, byPropertyValueMatcher } from "./filter-util";

/**
* Tile information for both excalibur and tiled tile representations
Expand Down Expand Up @@ -79,21 +79,36 @@ export class TileLayer implements Layer {
}

/**
* Returns the excalibur tiles that match a tiled gid
* Returns the excalibur tiles that match a tiled property and optional value
* @param name
* @param value
* @param [valueMatchInsensitive=true]
*/
getTilesByGid(gid: number): TileInfo[] {
return this._gidToTileInfo.get(gid) ?? [];
getTilesByProperty(name: string, value?: any, valueMatchInsensitive = true): TileInfo[] {
const tiles = this.tilemap.tiles.filter(t => {
const maybeTiled = t.data.get(ExcaliburTiledProperties.TileData.Tiled) as Tile | undefined;
if (maybeTiled) {
return byProperty(name, value, valueMatchInsensitive)(maybeTiled);
}
return false;
});

return tiles.map(t => ({
exTile: t,
tiledTile: t.data.get(ExcaliburTiledProperties.TileData.Tiled)
}))
}

/**
* Returns the excalibur tiles that match a tiled class name
* @param className
* Get the tiles that match the property name and the value matcher returns true
* @param name
* @param valueMatcher
*/
getTilesByClassName(className: string): TileInfo[] {
getTilesByPropertyValueMatcher(name: string, valueMatcher: (val: any) => any): TileInfo[] {
const tiles = this.tilemap.tiles.filter(t => {
const maybeTiled = t.data.get(ExcaliburTiledProperties.TileData.Tiled) as Tile | undefined;
if (maybeTiled) {
return byClassCaseInsensitive(className)(maybeTiled);
return byPropertyValueMatcher(name, valueMatcher)(maybeTiled);
}
return false;
});
Expand All @@ -103,17 +118,22 @@ export class TileLayer implements Layer {
tiledTile: t.data.get(ExcaliburTiledProperties.TileData.Tiled)
}))
}
/**
* Returns the excalibur tiles that match a tiled gid
*/
getTilesByGid(gid: number): TileInfo[] {
return this._gidToTileInfo.get(gid) ?? [];
}

/**
* Returns the excalibur tiles that match a tiled property and optional value
* @param name
* @param value
* Returns the excalibur tiles that match a tiled class name
* @param className
*/
getTilesByProperty(name: string, value?: any): TileInfo[] {
getTilesByClassName(className: string): TileInfo[] {
const tiles = this.tilemap.tiles.filter(t => {
const maybeTiled = t.data.get(ExcaliburTiledProperties.TileData.Tiled) as Tile | undefined;
if (maybeTiled) {
return byPropertyCaseInsensitive(name, value)(maybeTiled);
return byClassCaseInsensitive(className)(maybeTiled);
}
return false;
});
Expand Down
8 changes: 4 additions & 4 deletions src/resource/tiled-resource.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import { compare } from "compare-versions";
import { getCanonicalGid } from "./gid-util";
import { PathMap, pathRelativeToBase } from "./path-util";
import { PluginObject } from "./objects";
import { byClassCaseInsensitive, byNameCaseInsensitive, byPropertyCaseInsensitive } from "./filter-util";
import { byClassCaseInsensitive, byNameCaseInsensitive, byProperty } from "./filter-util";
import { ExcaliburTiledProperties } from "./excalibur-properties";
import { FetchLoader, FileLoader } from './file-loader';
import { TilesetResource, TilesetResourceOptions } from "./tileset-resource";
Expand Down Expand Up @@ -326,7 +326,7 @@ export class TiledResource implements Loadable<any> {
* @returns
*/
getTilesetByProperty(propertyName: string, value?: any): Tileset[] {
return this.tilesets.filter(byPropertyCaseInsensitive(propertyName, value));
return this.tilesets.filter(byProperty(propertyName, value));
}

/**
Expand All @@ -351,7 +351,7 @@ export class TiledResource implements Loadable<any> {
getTileMetadataByProperty(name: string, value?: any): Tile[] {
let results: Tile[] = [];
for (let tileset of this.tilesets) {
results = results.concat(tileset.tiles.filter(byPropertyCaseInsensitive(name, value)));
results = results.concat(tileset.tiles.filter(byProperty(name, value)));
}
return results;
}
Expand Down Expand Up @@ -653,7 +653,7 @@ export class TiledResource implements Loadable<any> {
}

getLayersByProperty(propertyName: string, value?: any): Layer[] {
return this.layers.filter(byPropertyCaseInsensitive(propertyName, value));
return this.layers.filter(byProperty(propertyName, value));
}

private _parseMap(data: any) {
Expand Down
21 changes: 18 additions & 3 deletions src/resource/tileset.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { TiledTile, TiledTileset, isTiledTilesetCollectionOfImages, isTiledTiles
import { Ellipse, Polygon, Rectangle, parseObjects } from "./objects";
import { Properties, mapProps } from "./properties";
import { PluginObject } from "./objects";
import { byClassCaseInsensitive, byPropertyCaseInsensitive } from "./filter-util";
import { byClassCaseInsensitive, byProperty, byPropertyValueMatcher } from "./filter-util";


export interface TileOptions {
Expand Down Expand Up @@ -219,8 +219,23 @@ export class Tileset implements Properties {
return this.tiles.filter(byClassCaseInsensitive(className));
}

getTilesByProperty(name: string, value?: any): Tile[] {
return this.tiles.filter(byPropertyCaseInsensitive(name, value));
/**
* Get the tiles that match the property name and the value
* @param name
* @param value
* @param [valueMatchInsensitive=true]
*/
getTilesByProperty(name: string, value?: any, valueMatchInsensitive = true): Tile[] {
return this.tiles.filter(byProperty(name, value, valueMatchInsensitive));
}

/**
* Get the tiles that match the property name and the value matcher returns true
* @param name
* @param valueMatcher
*/
getTilesByPropertyValueMatcher(name: string, valueMatcher: (val: any) => boolean): Tile[] {
return this.tiles.filter(byPropertyValueMatcher(name, valueMatcher));
}

getSpriteForGid(gid: number): Sprite {
Expand Down