Skip to content

Commit

Permalink
Adds basic TileMap.transform(fn)
Browse files Browse the repository at this point in the history
`TileMap.getTileAt()` now returns `Tile` object instead of number
Moves `TileMap.query` to separate file
  • Loading branch information
Marak committed Feb 21, 2024
1 parent ddd627f commit 8f96497
Show file tree
Hide file tree
Showing 6 changed files with 155 additions and 52 deletions.
16 changes: 16 additions & 0 deletions examples/transform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
import labyrinthos from '../lib/labyrinthos.js';

let map = new labyrinthos.TileMap({
width: 10,
height: 10
});

map.fill(1); // fill entire map with 1

// Example transformation: Increment each tile's value by 2
map.transform(function(tile){
tile.value += 2;
return tile;
});

console.log(map.data);
61 changes: 9 additions & 52 deletions lib/TileMap.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@ import generateTiledJSON from './util/generateTiledJSON.js'; // Exports to Tiled
import Mersenne from './util/mersenne.js'; // Randomness
import noise from './util/noise.js'; // Perlin Noise

import getTileAt from './TileMap/getTileAt.js';
import transform from './TileMap/transform.js';
import query from './TileMap/query.js';

export default class TileMap {
constructor({ x = 0, y = 0, width = 16, height = 16, depth = 1, tileWidth = 16, tileHeight = 16, is3D = false } = {}) {
this.x = x;
Expand All @@ -16,7 +20,12 @@ export default class TileMap {
this.Noise = new noise();
this.noise = this.Noise.noise;
this.seedNoise = this.Noise.noiseSeed;
this.transform = transform.bind(this);
this.query = query.bind(this);
this.getTileAt = getTileAt.bind(this);
this.data = this.initializeDataArray();
this.tileSet = [];

// ASCII representations for tiles 0-10
// TODO: Is there a better default set of ASCII characters we can use?
this.defaultRogueLike = ['-', '#', '+', '0', '<', '>', '$', '⌂', '@', '&', '?'];
Expand Down Expand Up @@ -155,14 +164,6 @@ export default class TileMap {
}
}

getTileAt(x, y, z) {
if (this.is3D) {
return this.data[z][y * this.width + x];
} else {
return this.data[y * this.width + x];
}
}

toJSON() {
return JSON.stringify({
width: this.width,
Expand All @@ -177,50 +178,6 @@ export default class TileMap {
return generateTiledJSON(this);
}

query({ x, y, width, height, z, tileName } = {}) {
let results = [];

if (x !== undefined && y !== undefined && width !== undefined && height !== undefined) {
for (let offsetY = 0; offsetY < height; offsetY++) {
for (let offsetX = 0; offsetX < width; offsetX++) {
const queryX = x + offsetX;
const queryY = y + offsetY;

if (queryX >= this.width || queryY >= this.height) {
results.push(undefined); // Add undefined for out-of-bounds indices
} else {
const index = queryY * this.width + queryX; // Calculate the correct index in the 1D array
if (this.is3D) {
if (z !== undefined && this.data[z] && this.data[z][index] !== undefined) {
results.push(this.data[z][index]);
} else {
results.push(undefined);
}
} else {
if (this.data[index] !== undefined) {
results.push(this.data[index]);
} else {
results.push(undefined);
}
}
}
}
}
}

// create a new TileMap instance from the results
let subsection = new TileMap({
width,
height,
is3D: this.is3D
});

subsection.data = results;

return subsection;
};


// query3D is WIP - not fully implemented yet, see: ./test/tilemap-query-test.js
query3D({ x, y, z, width, height, depth, tileName } = {}) {
let results = [];
Expand Down
30 changes: 30 additions & 0 deletions lib/TileMap/getTileAt.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default function getTileAt(x, y, z) {
let tileId; // This will hold the actual value from the data array

// Retrieve the tile value based on whether the map is 3D or 2D
if (this.is3D) {
if (z !== undefined && this.data[z] !== undefined) {
tileId = this.data[z][y * this.width + x];
} else {
return undefined; // Return undefined if the z layer does not exist
}
} else {
tileId = this.data[y * this.width + x];
}

// Construct the tile object with the id, value, and coordinates
let tileObject = {
// id: tileId, // Tile ID from the tileSet array
id: tileId, // Actual tile ID from the data array
value: tileId, // Actual value from the data array
x: x,
y: y,
...(this.is3D ? { z: z } : {}), // Include z coordinate if the map is 3D
};

// If the tileSet is defined and contains an entry for this tile ID, merge its properties into the tile object
if (Array.isArray(this.tileSet) && this.tileSet[tileId]) {
tileObject = { ...tileObject, ...this.tileSet[tileId] };
}
return tileObject;
}
44 changes: 44 additions & 0 deletions lib/TileMap/query.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import TileMap from '../TileMap.js'; // self reference required for new TileMap instance

export default function query({ x, y, width, height, z, tile } = {}) {
let results = [];

if (x !== undefined && y !== undefined && width !== undefined && height !== undefined) {
for (let offsetY = 0; offsetY < height; offsetY++) {
for (let offsetX = 0; offsetX < width; offsetX++) {
const queryX = x + offsetX;
const queryY = y + offsetY;

if (queryX >= this.width || queryY >= this.height) {
results.push(undefined); // Add undefined for out-of-bounds indices
} else {
const index = queryY * this.width + queryX; // Calculate the correct index in the 1D array
if (this.is3D) {
if (z !== undefined && this.data[z] && this.data[z][index] !== undefined) {
results.push(this.data[z][index]);
} else {
results.push(undefined);
}
} else {
if (this.data[index] !== undefined) {
results.push(this.data[index]);
} else {
results.push(undefined);
}
}
}
}
}
}

// create a new TileMap instance from the results
let subsection = new TileMap({
width,
height,
is3D: this.is3D
});

subsection.data = results;

return subsection;
};
30 changes: 30 additions & 0 deletions lib/TileMap/transform.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
export default function transform(transformFunction) {
// Check if the map is 3D
if (this.depth > 1) {
for (let z = 0; z < this.depth; z++) {
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
// Use getTileAt to get the tile object
const tileObject = this.getTileAt(x, y, z);
// Apply the transformation function to the tile object
const transformedTile = transformFunction(tileObject, x, y, z);
// Assuming the transformation function returns a tile object with an updated 'value'
// Update the data array with the new value
this.data[z][y * this.width + x] = transformedTile;
}
}
}
} else {
// Handle 2D map transformation
for (let y = 0; y < this.height; y++) {
for (let x = 0; x < this.width; x++) {
// Use getTileAt to get the tile object for 2D maps (z is undefined)
const tileObject = this.getTileAt(x, y);
// Apply the transformation function to the tile object
const transformedTile = transformFunction(tileObject, x, y);
// Update the data array with the new value
this.data[y * this.width + x] = transformedTile.id;
}
}
}
}
26 changes: 26 additions & 0 deletions test/tilemap-transform-test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import tap from 'tape';
import TileMap from '../lib/TileMap.js';

tap.test('applies a transformation function to all tiles', (t) => {
const map = new TileMap({ width: 2, height: 2, tileSet: [{}, { value: 'tile1' }, { value: 'tile2' }] });
map.fill(1); // Assuming '1' corresponds to a tile in the tileSet

// Transformation function that increments the id of the tile object
const increment = tile => ({ ...tile, id: tile.id + 1 });

map.transform(increment);

let passed = true;
for (let y = 0; y < map.height; y++) {
for (let x = 0; x < map.width; x++) {
const tile = map.getTileAt(x, y);
// Check if the transformation has been applied correctly
if (typeof tile !== 'object' || tile.id !== 2) {
passed = false;
}
}
}

t.ok(passed, 'All tiles should have their id incremented by 1');
t.end();
});

0 comments on commit 8f96497

Please sign in to comment.