import * as I from 'immutable'; import {compose, EventEmitter} from '@avocado/core'; import {Rectangle, Vector} from '@avocado/math'; import {Synchronized} from '@avocado/state'; import {TileUpdatePacket} from './packets/tile-update.packet'; const decorate = compose( EventEmitter, Vector.Mixin('size', 'width', 'height', { default: [0, 0], }), Synchronized, ); export class Tiles extends decorate(class {}) { constructor(json) { super(); this.data = []; this.updatePackets = []; if ('undefined' !== typeof json) { this.fromJSON(json); } } acceptPacket(packet) { if (packet instanceof TileUpdatePacket) { const unpackedPosition = [ packet.data.position & 0xFFFF, packet.data.position >> 16, ]; this.setTileAt(unpackedPosition, packet.data.tile); } } forEachTile(fn) { let [x, y] = [0, 0]; let [width, height] = this.size; let i = 0; for (let k = 0; k < height; ++k) { for (let j = 0; j < width; ++j) { fn(this.data[i], x, y, i); ++i; ++x; } x = 0; ++y; } } fromJSON(json) { if (json.size) { this.size = json.size; } if (json.data) { this.data = json.data.slice(0); } return this; } packetsForUpdate() { const packetsForUpdate = this.updatePackets; this.updatePackets = []; return packetsForUpdate; } get rectangle() { return Rectangle.compose([0, 0], this.size); } setTileAt(position, tile) { const oldTile = this.tileAt(position); if (oldTile === tile) { return; } const index = position[1] * this.width + position[0]; if (index < 0 || index >= this.data.length) { return; } this.data[index] = tile; const packedPosition = position[1] << 16 | position[0]; this.updatePackets.push(new TileUpdatePacket({ position: packedPosition, tile, })); this.emit('dataChanged'); } slice(rectangle) { const tilesRectangle = this.rectangle; // Get intersection. if (!Rectangle.intersects(rectangle, tilesRectangle)) { return []; } let [x, y, sliceWidth, sliceHeight] = Rectangle.intersection( rectangle, tilesRectangle, ); // No muls in the loop. let sliceRow = y * sliceWidth; const dataWidth = this.width; let dataRow = y * dataWidth; // Copy slice. const slice = new Array(sliceWidth * sliceHeight); for (let j = 0; j < sliceHeight; ++j) { for (let i = 0; i < sliceWidth; ++i) { slice[sliceRow + x] = this.data[dataRow + x]; x++; } sliceRow += sliceWidth; dataRow += dataWidth; x -= sliceWidth; } return slice; } tileAt(position) { return this.data[position[1] * this.width + position[0]]; } toJSON() { return { size: Vector.copy(this.size), data: this.data.slice(0), }; } }