195 lines
5.5 KiB
Plaintext
195 lines
5.5 KiB
Plaintext
// tilemap
|
|
|
|
function tilemap()
|
|
{
|
|
this.tiles = [];
|
|
this.offset_x = 0;
|
|
this.offset_y = 0;
|
|
this.size_x = 32;
|
|
this.size_y = 32;
|
|
this.layer = 0; // Default layer for scene tree sorting
|
|
this._geometry_cache = {}; // Cache actual geometry data by texture
|
|
this._dirty = true;
|
|
return this;
|
|
}
|
|
|
|
tilemap.for = function tilemap_for(map, fn) {
|
|
for (var x = 0; x < map.tiles.length; x++) {
|
|
if (!map.tiles[x]) continue;
|
|
for (var y = 0; y < map.tiles[x].length; y++) {
|
|
if (map.tiles[x][y] != null) {
|
|
var result = fn(map.tiles[x][y], {
|
|
x: x + map.offset_x,
|
|
y: y + map.offset_y
|
|
});
|
|
if (result != null) {
|
|
map.tiles[x][y] = result;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
tilemap.prototype =
|
|
{
|
|
at(pos) {
|
|
var x = pos.x - this.offset_x;
|
|
var y = pos.y - this.offset_y;
|
|
if (!this.tiles[x]) return null;
|
|
return this.tiles[x][y];
|
|
},
|
|
|
|
set(pos, image) {
|
|
// Shift arrays if negative indices
|
|
if (pos.x < this.offset_x) {
|
|
var shift = this.offset_x - pos.x;
|
|
var new_tiles = [];
|
|
for (var i = 0; i < shift; i++) new_tiles[i] = [];
|
|
this.tiles = new_tiles.concat(this.tiles);
|
|
this.offset_x = pos.x;
|
|
}
|
|
|
|
if (pos.y < this.offset_y) {
|
|
var shift = this.offset_y - pos.y;
|
|
for (var i = 0; i < this.tiles.length; i++) {
|
|
if (!this.tiles[i]) this.tiles[i] = [];
|
|
var new_col = [];
|
|
for (var j = 0; j < shift; j++) new_col[j] = null;
|
|
this.tiles[i] = new_col.concat(this.tiles[i]);
|
|
}
|
|
this.offset_y = pos.y;
|
|
}
|
|
|
|
var x = pos.x - this.offset_x;
|
|
var y = pos.y - this.offset_y;
|
|
|
|
// Ensure array exists up to x
|
|
while (this.tiles.length <= x) this.tiles.push([]);
|
|
|
|
// Convert string to image object if needed, or handle null to remove tile
|
|
if (image && typeof image == 'string') {
|
|
var graphics = use('graphics');
|
|
image = graphics.texture(image);
|
|
}
|
|
// Note: if image is null, it will remove the tile
|
|
|
|
// Set the value (null removes the tile)
|
|
this.tiles[x][y] = image;
|
|
|
|
// Mark cache as dirty when tiles change
|
|
this._dirty = true;
|
|
},
|
|
|
|
clear() {
|
|
this.tiles = [];
|
|
this.offset_x = 0;
|
|
this.offset_y = 0;
|
|
this._geometry_cache = {};
|
|
this._dirty = true;
|
|
},
|
|
|
|
// Build cached geometry grouped by texture
|
|
_build_geometry_cache(pos = {x: 0, y: 0}) {
|
|
var geometry = use('geometry');
|
|
|
|
// Group tiles by texture (using a unique key per image object)
|
|
var textureGroups = {};
|
|
var imageToKey = new Map(); // Map image objects to unique keys
|
|
var keyCounter = 0;
|
|
|
|
// Collect all tiles and their positions
|
|
for (var x = 0; x < this.tiles.length; x++) {
|
|
if (!this.tiles[x]) continue;
|
|
for (var y = 0; y < this.tiles[x].length; y++) {
|
|
var tile = this.tiles[x][y];
|
|
if (tile) {
|
|
// tile should already be an image object
|
|
// Create a unique key for each distinct image object
|
|
if (!imageToKey.has(tile)) {
|
|
var key = `texture_${keyCounter++}`;
|
|
imageToKey.set(tile, key);
|
|
log.console(`New texture key: ${key} for tile ${tile}`)
|
|
}
|
|
var textureKey = imageToKey.get(tile);
|
|
|
|
if (!textureGroups[textureKey]) {
|
|
textureGroups[textureKey] = {
|
|
tiles: [],
|
|
image: tile, // Store the image object
|
|
offset_x: this.offset_x,
|
|
offset_y: this.offset_y,
|
|
size_x: this.size_x,
|
|
size_y: this.size_y
|
|
};
|
|
}
|
|
textureGroups[textureKey].tiles.push({
|
|
x: x + this.offset_x,
|
|
y: y + this.offset_y,
|
|
image: tile
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
// Generate and cache geometry for each texture group
|
|
this._geometry_cache = {};
|
|
for (var textureKey in textureGroups) {
|
|
var group = textureGroups[textureKey];
|
|
if (group.tiles.length == 0) continue;
|
|
|
|
// Create a temporary tilemap for this texture group
|
|
var tempMap = {
|
|
tiles: [],
|
|
offset_x: group.offset_x,
|
|
offset_y: group.offset_y,
|
|
size_x: group.size_x,
|
|
size_y: group.size_y,
|
|
pos_x: pos.x,
|
|
pos_y: pos.y
|
|
};
|
|
|
|
// Build sparse array for this texture's tiles
|
|
group.tiles.forEach(({x, y, image}) => {
|
|
var arrayX = x - group.offset_x;
|
|
var arrayY = y - group.offset_y;
|
|
if (!tempMap.tiles[arrayX]) tempMap.tiles[arrayX] = [];
|
|
tempMap.tiles[arrayX][arrayY] = image;
|
|
});
|
|
|
|
// Generate and cache geometry for this group
|
|
var geom = geometry.tilemap_to_data(tempMap);
|
|
this._geometry_cache[textureKey] = {
|
|
geometry: geom,
|
|
image: group.image
|
|
};
|
|
}
|
|
|
|
this._dirty = false;
|
|
},
|
|
|
|
draw() {
|
|
var pos = this.pos || {x:0,y:0}
|
|
// Rebuild cache if dirty or position changed
|
|
if (this._dirty || Object.keys(this._geometry_cache).length == 0 || this._last_pos?.x != pos.x || this._last_pos?.y != pos.y) {
|
|
this._build_geometry_cache(pos);
|
|
this._last_pos = {x: pos.x, y: pos.y};
|
|
}
|
|
|
|
// Generate commands from cached geometry, pulling texture_id dynamically
|
|
var commands = [];
|
|
var i = 0
|
|
for (var textureKey in this._geometry_cache) {
|
|
var cached = this._geometry_cache[textureKey];
|
|
commands.push({
|
|
cmd: "geometry",
|
|
geometry: cached.geometry,
|
|
image: cached.image,
|
|
texture_id: cached.image.gpu // Pull GPU ID dynamically on each draw
|
|
});
|
|
}
|
|
|
|
return commands;
|
|
},
|
|
}
|
|
|
|
return tilemap |