diff --git a/src/leaflet/control/LinkControl.js b/src/leaflet/control/LinkControl.ts similarity index 68% rename from src/leaflet/control/LinkControl.js rename to src/leaflet/control/LinkControl.ts index c1335fe..72da8ec 100644 --- a/src/leaflet/control/LinkControl.js +++ b/src/leaflet/control/LinkControl.ts @@ -1,9 +1,11 @@ -import L from 'leaflet'; +import L, {ControlOptions} from 'leaflet'; -const LinkControl = L.Control.extend({ - options: {position: 'bottomleft'}, +export interface LinkControlOptions extends ControlOptions {} - onAdd: function (map) { +export class LinkControl extends L.Control { + options: LinkControlOptions + + onAdd(map) { this._map = map; this._container = L.DomUtil.create('div', 'dynmap-link'); @@ -12,18 +14,18 @@ const LinkControl = L.Control.extend({ this._container.appendChild(this._linkButton); return this._container; - }, + } - getContainer: function () { + getContainer() { return this._container; - }, + } - getPosition: function () { + getPosition() { return this.options.position; - }, + } - _createButton: function (title, className, fn, context) { - var link = document.createElement('a'); + _createButton(title, className, fn, context) { + const link = document.createElement('a'); link.href = '#'; link.title = title; link.className = className; @@ -36,16 +38,14 @@ const LinkControl = L.Control.extend({ L.DomEvent.addListener(link, 'click', fn, context); return link; - }, + } - _follow: function () { + _follow() { // var url = dynmap.getLink(); // window.location = url; } -}); +} // var link = new dynmapLink(); // dynmap.map.addControl(link); // } - -export default LinkControl; \ No newline at end of file diff --git a/src/leaflet/icon/PlayerIcon.ts b/src/leaflet/icon/PlayerIcon.ts new file mode 100644 index 0000000..66a81fb --- /dev/null +++ b/src/leaflet/icon/PlayerIcon.ts @@ -0,0 +1,116 @@ +import L, {DivIconOptions} from 'leaflet'; +import {DynmapPlayer} from "@/dynmap"; + +export interface PlayerIconOptions extends DivIconOptions { + smallFace: boolean, + showSkinFace: boolean, + showBody: boolean, + showHealth: boolean, +} + +export class PlayerIcon extends L.DivIcon { + private readonly _player: DynmapPlayer; + private _container?: HTMLDivElement; + private _playerImage?: HTMLImageElement; + private _playerName?: HTMLSpanElement; + + private _playerHealth?: HTMLDivElement; + private _playerHealthBg?: HTMLDivElement; + private _playerHealthBar?: HTMLDivElement; + private _playerArmourBg?: HTMLDivElement; + private _playerArmourBar?: HTMLDivElement; + + // @ts-ignore + options: PlayerIconOptions; + + constructor(player: DynmapPlayer, options: PlayerIconOptions) { + super(options); + this._player = player; + } + + createIcon(oldIcon: HTMLElement) { + if (oldIcon) { + L.DomUtil.remove(oldIcon); + } + + const player = this._player; + + this._container = document.createElement('div'); + + // var markerPosition = dynmap.getProjection().fromLocationToLatLng(player.location); + // player.marker.setLatLng(markerPosition); + + this._container.classList.add('Marker', 'playerMarker', 'leaflet-marker-icon'); + + this._playerImage = document.createElement('img'); + this._playerImage.classList.add(this.options.smallFace ? 'playerIconSm' : 'playerIcon'); + this._playerImage.src = 'images/player.png'; + + this._playerName = document.createElement('span'); + this._playerName.classList.add(this.options.smallFace ? 'playerNameSm' : 'playerName'); + this._playerName.innerText = player.name; + + this._container.insertAdjacentElement('beforeend', this._playerImage); + this._container.insertAdjacentElement('beforeend', this._playerName); + + if (this.options.showSkinFace) { + + // if (this.options.smallFace) { + // getMinecraftHead(player.account, 16, head => { + // this._playerImage!.src = head.src; + // }); + // } else if (this.options.showBody) { + // getMinecraftHead(player.account, 'body', head => { + // this._playerImage!.src = head.src; + // }); + // } else { + // getMinecraftHead(player.account, 32, head => { + // this._playerImage!.src = head.src; + // }); + // } + } + + if (this.options.showHealth) { + this._playerHealth = document.createElement('div'); + + this._playerHealth.classList.add(this.options.smallFace ? 'healthContainerSm' : 'healthContainer'); + this._container.insertAdjacentElement('beforeend', this._playerHealth) + + this._playerHealthBar = document.createElement('div'); + this._playerHealthBar.classList.add('playerHealth'); + + this._playerArmourBar = document.createElement('div'); + this._playerArmourBar.classList.add('playerHealth'); + + this._playerHealthBg = document.createElement('div'); + this._playerArmourBg = document.createElement('div'); + + this._playerHealthBg.classList.add('playerHealthBackground'); + this._playerArmourBar.classList.add('playerArmorBackground'); + + this._playerHealthBg.insertAdjacentElement('beforeend', this._playerHealthBar); + this._playerArmourBg.insertAdjacentElement('beforeend', this._playerArmourBar); + + this._playerHealth.insertAdjacentElement('beforeend', this._playerHealthBg); + this._playerHealth.insertAdjacentElement('beforeend', this._playerArmourBg); + + this._playerHealth.hidden = true; + } else { + this._playerName.classList.add('playerNameNoHealth'); + } + + return this._container; + } + + update() { + this._playerName!.innerText = this._player!.name; + + if (this._player.health !== undefined && this._player.armor !== undefined) { + this._playerHealth!.hidden = false; + this._playerHealthBar!.style.width = Math.ceil(this._player.health * 2.5) + 'px'; + this._playerArmourBar!.style.width = Math.ceil(this._player.armor * 2.5) + 'px'; + } else { + this._playerHealth!.hidden = true; + } + } +} diff --git a/src/leaflet/mapType/HDMapType.js b/src/leaflet/mapType/HDMapType.js deleted file mode 100644 index 71057ae..0000000 --- a/src/leaflet/mapType/HDMapType.js +++ /dev/null @@ -1,47 +0,0 @@ -import DynmapTileLayer from "@/leaflet/tileLayer/DynmapTileLayer"; -import HDProjection from "@/leaflet/projection/HDProjection"; -import L from 'leaflet'; - -const HDMapType = DynmapTileLayer.extend({ - projection: undefined, - options: { - maxZoom: 1, - maxNativeZoom: 1, - worldName: '', - prefix: '', - errorTileUrl: 'images/blank.png', - }, - initialize(options) { - DynmapTileLayer.prototype.initialize.call(this, options); - options.zoomReverse = true; - options.tileSize = 128; - options.minZoom = 0; - L.Util.setOptions(this, options); - this.projection = new HDProjection(Object.assign(options, {map: this})); - }, - - getTileName: function(coords) { - let info = this.getTileInfo(coords); - // Y is inverted for HD-map. - info.y = -info.y; - info.scaledy = info.y >> 5; - return `${info.prefix}${info.nightday}/${info.scaledx}_${info.scaledy}/${info.zoom}${info.x}_${info.y}.${info.fmt}`; - }, - zoomprefix: function(amount) { - // amount == 0 -> '' - // amount == 1 -> 'z_' - // amount == 2 -> 'zz_' - return 'zzzzzzzzzzzzzzzzzzzzzz'.substr(0, amount) + (amount === 0 ? '' : '_'); - } -}); - -// maptypes.HDMapType = function(options) { return new HDMapType(options); }; - -const hdMapType = (options) => { - return new HDMapType(options); -} - -export { - HDMapType, - hdMapType -}; \ No newline at end of file diff --git a/src/leaflet/mapType/HDMapType.ts b/src/leaflet/mapType/HDMapType.ts new file mode 100644 index 0000000..c877c29 --- /dev/null +++ b/src/leaflet/mapType/HDMapType.ts @@ -0,0 +1,43 @@ +import L from 'leaflet'; +import HDProjection from "@/leaflet/projection/HDProjection"; +import {Coordinate} from "@/dynmap"; +import {DynmapTileLayer, DynmapTileLayerOptions} from "@/leaflet/tileLayer/DynmapTileLayer"; + +export interface HDMapTypeOptions extends DynmapTileLayerOptions {} + +export interface HDMapType extends DynmapTileLayer { +} + +export class HDMapType extends DynmapTileLayer { + constructor(options: DynmapTileLayerOptions) { + super(options); + + options.maxZoom = this._mapSettings.nativeZoomLevels + this._mapSettings.extraZoomLevels; + options.maxNativeZoom = this._mapSettings.nativeZoomLevels; + options.zoomReverse = true; + options.tileSize = 128; + options.minZoom = 0; + + L.Util.setOptions(this, options); + this._projection = new HDProjection({ + mapToWorld: this._mapSettings.mapToWorld, + worldToMap: this._mapSettings.worldToMap, + nativeZoomLevels: this._mapSettings.nativeZoomLevels, + }); + } + + getTileName(coords: Coordinate) { + const info = super.getTileInfo(coords); + // Y is inverted for HD-map. + info.y = -info.y; + info.scaledy = info.y >> 5; + return `${info.prefix}${info.nightday}/${info.scaledx}_${info.scaledy}/${info.zoom}${info.x}_${info.y}.${info.fmt}`; + } + + zoomprefix(amount: number) { + // amount == 0 -> '' + // amount == 1 -> 'z_' + // amount == 2 -> 'zz_' + return 'z'.repeat(amount) + (amount === 0 ? '' : '_'); + } +} diff --git a/src/leaflet/marker/PlayerMarker.js b/src/leaflet/marker/PlayerMarker.js deleted file mode 100644 index dc3cd28..0000000 --- a/src/leaflet/marker/PlayerMarker.js +++ /dev/null @@ -1,156 +0,0 @@ -import L from 'leaflet'; - -const PlayerMarker = L.Marker.extend({ - options: { - smallFace: true, - showSkinFace: false, - showBody: false, - showHealth: false, - }, - - initialize: function (player, options) { - this._player = player; - options.draggable = false; - options.icon = new PlayerIcon(player, { - smallFace: options.smallFace, - showSkinFace: options.showSkinFace, - showBody: options.showBody, - showHealth: options.showHealth, - }); - - L.Util.setOptions(this, options); - //this._latlng = toLatLng(latlng); //TODO - }, -}); - -var PlayerIcon = L.DivIcon.extend({ - _player: null, - _container: null, - _playerImage: null, - _playerName: null, - - _playerHealth: null, - _playerHealthBg: null, - _playerHealthBar: null, - _playerArmourBg: null, - _playerArmourBar: null, - - options: { - smallFace: true, - showSkinFace: false, - showBody: false, - showHealth: false, - }, - - initialize: function (player, options) { - this._player = player; - L.Util.setOptions(this, options); - }, - - createIcon: function (oldIcon) { - if (oldIcon) { - L.DomUtil.remove(oldIcon); - } - - var player = this._player; - - this._container = document.createElement('div'); - - // var markerPosition = dynmap.getProjection().fromLocationToLatLng(player.location); - // player.marker.setLatLng(markerPosition); - - this._container.classList.add('Marker', 'playerMarker', 'leaflet-marker-icon'); - - this._playerImage = document.createElement('img'); - this._playerImage.classList.add(this.options.smallFace ? 'playerIconSm' : 'playerIcon'); - this._playerImage.src = 'images/player.png'; - - this._playerName = document.createElement('span'); - this._playerName.classList.add(this.options.smallFace ? 'playerNameSm' : 'playerName'); - this._playerName.innerText = player.name; - - this._container.insertAdjacentElement('beforeend', this._playerImage); - this._container.insertAdjacentElement('beforeend', this._playerName); - - if (this.options.showSkinFace) { - var that = this; - - if (this.options.smallFace) { - getMinecraftHead(player.account, 16, function (head) { - that._playerImage.src = head.src; - }); - } else if (this.options.showBody) { - getMinecraftHead(player.account, 'body', function (head) { - that._playerImage.src = head.src; - }); - } else { - getMinecraftHead(player.account, 32, function (head) { - that._playerImage.src = head.src; - }); - } - } - - if (this.options.showHealth) { - this._playerHealth = document.createElement('div'); - - this._playerHealth.classList.add(this.options.smallFace ? 'healthContainerSm' : 'healthContainer'); - this._container.insertAdjacentElement('beforeend', this._playerHealth) - - this._playerHealthBar = document.createElement('div'); - this._playerHealthBar.classList.add('playerHealth'); - - this._playerArmourBar = document.createElement('div'); - this._playerArmourBar.classList.add('playerHealth'); - - this._playerHealthBg = document.createElement('div'); - this._playerArmourBg = document.createElement('div'); - - this._playerHealthBg.classList.add('playerHealthBackground'); - this._playerArmourBar.classList.add('playerArmorBackground'); - - this._playerHealthBg.insertAdjacentElement('beforeend', this._playerHealthBar); - this._playerArmourBg.insertAdjacentElement('beforeend', this._playerArmourBar); - - this._playerHealth.insertAdjacentElement('beforeend', this._playerHealthBg); - this._playerHealth.insertAdjacentElement('beforeend', this._playerArmourBg); - - this._playerHealth.hidden = true; - } else { - this._playerName.classList.add('playerNameNoHealth'); - } - - return this._container; - - // var div = (oldIcon && oldIcon.tagName === 'DIV') ? oldIcon : document.createElement('div'), - // options = this.options; - // - // if (options.html instanceof Element) { - // empty(div); - // div.appendChild(options.html); - // } else { - // div.innerHTML = options.html !== false ? options.html : ''; - // } - // - // if (options.bgPos) { - // var bgPos = toPoint(options.bgPos); - // div.style.backgroundPosition = (-bgPos.x) + 'px ' + (-bgPos.y) + 'px'; - // } - // this._setIconStyles(div, 'icon'); - // - // return div; - }, - - update() { - this._playerName.innerText = this.player.name; - - if (this.options.player.health !== undefined && this.options.player.armor !== undefined) { - this.options.player.healthContainer.hidden = false; - this._this.options.playerHealthBar.style.width = Math.ceil(this.options.player.health * 2.5) + 'px'; - this._this.options.playerArmourBar.style.width = Math.ceil(this.options.player.armor * 2.5) + 'px'; - } else { - this.options.player.healthContainer.hidden = true; - } - } -}); - -export default PlayerMarker; \ No newline at end of file diff --git a/src/leaflet/marker/PlayerMarker.ts b/src/leaflet/marker/PlayerMarker.ts new file mode 100644 index 0000000..d8c0756 --- /dev/null +++ b/src/leaflet/marker/PlayerMarker.ts @@ -0,0 +1,29 @@ +import L, {LatLng, MarkerOptions} from 'leaflet'; +import {DynmapPlayer} from "@/dynmap"; +import {PlayerIcon} from "@/leaflet/icon/PlayerIcon"; + +export interface PlayerMarkerOptions extends MarkerOptions { + smallFace: boolean, + showSkinFace: boolean, + showBody: boolean, + showHealth: boolean, +} + +export class PlayerMarker extends L.Marker { + private _player: DynmapPlayer; + + constructor(player: DynmapPlayer, options: PlayerMarkerOptions) { + super(new LatLng(0,0), options); + this._player = player; + options.draggable = false; + options.icon = new PlayerIcon(player, { + smallFace: options.smallFace, + showSkinFace: options.showSkinFace, + showBody: options.showBody, + showHealth: options.showHealth, + }); + + L.Util.setOptions(this, options); + //this._latlng = toLatLng(latlng); //TODO + } +} diff --git a/src/leaflet/projection/DynmapProjection.js b/src/leaflet/projection/DynmapProjection.js deleted file mode 100644 index d637a38..0000000 --- a/src/leaflet/projection/DynmapProjection.js +++ /dev/null @@ -1,15 +0,0 @@ -import L from 'leaflet'; - -const DynmapProjection = L.Class.extend({ - initialize: function (options) { - L.Util.setOptions(this, options); - }, - fromLocationToLatLng: function () { - throw "fromLocationToLatLng not implemented"; - }, - fromLatLngToLocation: function () { - return null; - } -}); - -export default DynmapProjection; \ No newline at end of file diff --git a/src/leaflet/projection/DynmapProjection.ts b/src/leaflet/projection/DynmapProjection.ts new file mode 100644 index 0000000..e5d2038 --- /dev/null +++ b/src/leaflet/projection/DynmapProjection.ts @@ -0,0 +1,25 @@ +import L from 'leaflet'; +import {Coordinate} from "@/dynmap"; + +export interface DynmapProjectionOptions {} + +export interface DynmapProjection { + locationToLatLng(location: Coordinate): L.LatLng; + latLngToLocation(latLng: L.LatLng, y: number): Coordinate; +} + +export class DynmapProjection extends L.Class { + + constructor(options: DynmapProjectionOptions) { + super(); + L.Util.setOptions(this, options); + } + + locationToLatLng(location: Coordinate): L.LatLng { + throw new Error("fromLocationToLatLng not implemented"); + } + + latLngToLocation(latLng: L.LatLng, y: number): Coordinate { + throw new Error("fromLatLngToLocation not implemented"); + } +} diff --git a/src/leaflet/projection/HDProjection.js b/src/leaflet/projection/HDProjection.js deleted file mode 100644 index c3bf9cc..0000000 --- a/src/leaflet/projection/HDProjection.js +++ /dev/null @@ -1,25 +0,0 @@ -import DynmapProjection from "@/leaflet/projection/DynmapProjection"; -import L from 'leaflet'; - -const HDProjection = DynmapProjection.extend({ - fromLocationToLatLng: function (location) { - var wtp = this.options.worldtomap; - var lat = wtp[3] * location.x + wtp[4] * location.y + wtp[5] * location.z; - var lng = wtp[0] * location.x + wtp[1] * location.y + wtp[2] * location.z; - - return new L.LatLng( - -((128 - lat) / (1 << this.options.mapzoomout)), - lng / (1 << this.options.mapzoomout), - true); - }, - fromLatLngToLocation: function (latlon, y) { - var ptw = this.options.maptoworld; - var lat = latlon.lng * (1 << this.options.mapzoomout); - var lon = 128 + latlon.lat * (1 << this.options.mapzoomout); - var x = ptw[0] * lat + ptw[1] * lon + ptw[2] * y; - var z = ptw[6] * lat + ptw[7] * lon + ptw[8] * y; - return {x: x, y: y, z: z}; - } -}); - -export default HDProjection; diff --git a/src/leaflet/projection/HDProjection.ts b/src/leaflet/projection/HDProjection.ts new file mode 100644 index 0000000..1cfd6fd --- /dev/null +++ b/src/leaflet/projection/HDProjection.ts @@ -0,0 +1,42 @@ +import {DynmapProjection} from "@/leaflet/projection/DynmapProjection"; +import L from 'leaflet'; +import {Coordinate} from "@/dynmap"; + +export interface HDProjectionOptions { + mapToWorld: [number, number, number, number, number, number, number, number, number], + worldToMap: [number, number, number, number, number, number, number, number, number], + nativeZoomLevels: number +} + +export interface HDProjection extends DynmapProjection { + options: HDProjectionOptions +} + +export class HDProjection extends DynmapProjection { + constructor(options: HDProjectionOptions) { + super(options); + L.Util.setOptions(this, options); + } + + locationToLatLng(location: Coordinate): L.LatLng { + const wtp = this.options.worldToMap, + lat = wtp[3] * location.x + wtp[4] * location.y + wtp[5] * location.z, + lng = wtp[0] * location.x + wtp[1] * location.y + wtp[2] * location.z; + + return new L.LatLng( + -((128 - lat) / (1 << this.options.nativeZoomLevels)), + lng / (1 << this.options.nativeZoomLevels)); + } + + latLngToLocation(latLng: L.LatLng, y: number): Coordinate { + const ptw = this.options.mapToWorld, + lat = latLng.lng * (1 << this.options.nativeZoomLevels), + lon = 128 + latLng.lat * (1 << this.options.nativeZoomLevels), + x = ptw[0] * lat + ptw[1] * lon + ptw[2] * y, + z = ptw[6] * lat + ptw[7] * lon + ptw[8] * y; + + return {x: x, y: y, z: z}; + } +} + +export default HDProjection; diff --git a/src/leaflet/tileLayer/DynmapTileLayer.js b/src/leaflet/tileLayer/DynmapTileLayer.js deleted file mode 100644 index 0987c68..0000000 --- a/src/leaflet/tileLayer/DynmapTileLayer.js +++ /dev/null @@ -1,128 +0,0 @@ -import L from 'leaflet'; - -const DynmapTileLayer = L.TileLayer.extend({ - options: { - worldName: '', - prefix: '', - }, - - _cachedTileUrls: null, - _namedTiles: null, - - initialize(options) { - L.TileLayer.prototype.initialize.call(this, options); - this._cachedTileUrls = {}; - this._namedTiles = {}; - L.Util.setOptions(this, options); - }, - - getProjection: function () { - return this.projection; - }, - - getTileName: function() { - throw "getTileName not implemented"; - }, - - getTileUrl: function (coords) { - var tileName = this.getTileName(coords, coords.z), - url = this._cachedTileUrls[tileName]; - - if (!url) { - let path = escape(`${this.options.worldName}/${tileName}`); - url = `${window.config.url.tiles}${path}`; - this._cachedTileUrls[tileName] = url; - } - - return url; - }, - - updateNamedTile: function (name) { - var tile = this._namedTiles[name]; - delete this._cachedTileUrls[name]; - - if (tile) { - tile.src = this._cachedTileUrls[name] = this.options.dynmap.getTileUrl(name); - } - }, - - createTile(coords, done) { - var tile = L.TileLayer.prototype.createTile.call(this, coords, done), - name = this.getTileName(coords); - - tile.tileName = name; - - // console.log("Adding " + tile.tileName); - this._namedTiles[name] = tile; - - return tile; - }, - - // stops loading all tiles in the background layer - _abortLoading: function () { - var i, tile; - for (i in this._tiles) { - if (!Object.prototype.hasOwnProperty.call(this._tiles, i)) { - continue; - } - - tile = this._tiles[i] - - if (tile.coords.z !== this._tileZoom) { - if (!tile.complete && tile.el && tile.el.tileName) { - // console.log("Aborting " + tile.el.tileName); - delete this._namedTiles[tile.el.tileName]; - } - } - } - - L.TileLayer.prototype._abortLoading.call(this); - }, - - _removeTile: function (key) { - var tile = this._tiles[key]; - - if (!tile) { - return; - } - - var tileName = tile.el.tileName; - - if (tileName) { - // console.log("Removing " + tileName); - delete this._namedTiles[tileName]; - delete this._cachedTileUrls[tileName]; - } - - L.TileLayer.prototype._removeTile.call(this, key); - }, - - // Some helper functions. - zoomprefix: function (amount) { - return 'zzzzzzzzzzzzzzzzzzzzzz'.substr(0, amount); - }, - - getTileInfo: function (coords) { - // zoom: max zoomed in = this.options.maxZoom, max zoomed out = 0 - // izoom: max zoomed in = 0, max zoomed out = this.options.maxZoom - // zoomoutlevel: izoom < mapzoomin -> 0, else -> izoom - mapzoomin (which ranges from 0 till mapzoomout) - var izoom = this._getZoomForUrl(); - var zoomoutlevel = Math.max(0, izoom - (this.options.maxZoom - this.options.maxNativeZoom)); - var scale = 1 << zoomoutlevel; - var x = scale * coords.x; - var y = scale * coords.y; - return { - prefix: this.options.prefix, - nightday: (this.options.nightandday && this.options.dynmap.serverday) ? '_day' : '', - scaledx: x >> 5, - scaledy: y >> 5, - zoom: this.zoomprefix(zoomoutlevel), - zoomprefix: (zoomoutlevel == 0) ? "" : (this.zoomprefix(zoomoutlevel) + "_"), - x: x, - y: y, - fmt: this.options['image-format'] || 'png' - }; - } -}); - -export default DynmapTileLayer; \ No newline at end of file diff --git a/src/leaflet/tileLayer/DynmapTileLayer.ts b/src/leaflet/tileLayer/DynmapTileLayer.ts new file mode 100644 index 0000000..8a496ab --- /dev/null +++ b/src/leaflet/tileLayer/DynmapTileLayer.ts @@ -0,0 +1,162 @@ +import L, {Coords, DoneCallback, TileLayerOptions} from 'leaflet'; +import {DynmapProjection} from "@/leaflet/projection/DynmapProjection"; +import {Coordinate, DynmapMap} from "@/dynmap"; + +export interface DynmapTileLayerOptions extends TileLayerOptions { + mapSettings: DynmapMap; + errorTileUrl: string; +} + +export interface DynmapTileLayer extends L.TileLayer { + options: DynmapTileLayerOptions; + _projection: any; + _mapSettings: any; + _cachedTileUrls: any; + _namedTiles: any; + + locationToLatLng(location: Coordinate): L.LatLng; + + latLngToLocation(latLng: L.LatLng): Coordinate; +} + +export interface DynmapTile extends HTMLImageElement { + tileName: string; +} + +export interface TileInfo { + prefix: string; + nightday: string; + scaledx: number; + scaledy: number; + zoom: string; + zoomprefix: string; + x: number; + y: number; + fmt: string; +} + +export class DynmapTileLayer extends L.TileLayer { + constructor(options: DynmapTileLayerOptions) { + super('', options); + + if (options.mapSettings === null) { + throw new TypeError("mapSettings missing"); + } + + this._projection = new DynmapProjection({}); + this._mapSettings = options.mapSettings; + this._cachedTileUrls = {}; + this._namedTiles = {}; + L.Util.setOptions(this, options); + } + + getTileName(coords: Coordinate): string { + throw "getTileName not implemented"; + } + + getTileUrl(coords: Coordinate) { + const tileName = this.getTileName(coords); + let url = this._cachedTileUrls[tileName]; + + if (!url) { + const path = escape(`${this._mapSettings.world.name}/${tileName}`); + url = `${window.config.url.tiles}${path}`; + this._cachedTileUrls[tileName] = url; + } + + return url; + } + + updateNamedTile(name: string) { + const tile = this._namedTiles[name]; + delete this._cachedTileUrls[name]; + + if (tile) { + //tile.src = this._cachedTileUrls[name] = this.getTileUrl(name); + } + } + + createTile(coords: Coords, done: DoneCallback) { + const tile = super.createTile.call(this, coords, done) as DynmapTile, + name = this.getTileName(coords); + + tile.tileName = name; + + // console.log("Adding " + tile.tileName); + this._namedTiles[name] = tile; + + return tile; + } + + // stops loading all tiles in the background layer + _abortLoading() { + let tile; + for (const i in this._tiles) { + if (!Object.prototype.hasOwnProperty.call(this._tiles, i)) { + continue; + } + + tile = this._tiles[i]; + + if (tile.coords.z !== this._tileZoom) { + if (tile.loaded && tile.el && (tile.el as DynmapTile).tileName) { + // console.log("Aborting " + (tile.el as DynmapTile).tileName); + delete this._namedTiles[(tile.el as DynmapTile).tileName]; + } + } + } + + super._abortLoading.call(this); + } + + _removeTile(key: string) { + const tile = this._tiles[key]; + + if (!tile) { + return; + } + + const tileName = (tile.el as DynmapTile).tileName; + + if (tileName) { + // console.log("Removing " + tileName); + delete this._namedTiles[tileName]; + delete this._cachedTileUrls[tileName]; + } + + // @ts-ignore + super._removeTile.call(this, key); + } + + // Some helper functions. + zoomprefix(amount: number) { + return 'z'.repeat(amount); + } + + getTileInfo(coords: Coordinate): TileInfo { + // zoom: max zoomed in = this.options.maxZoom, max zoomed out = 0 + // izoom: max zoomed in = 0, max zoomed out = this.options.maxZoom + // zoomoutlevel: izoom < mapzoomin -> 0, else -> izoom - mapzoomin (which ranges from 0 till mapzoomout) + const izoom = this._getZoomForUrl(), + zoomoutlevel = Math.max(0, izoom - this._mapSettings.extraZoomLevels), + scale = 1 << zoomoutlevel, + x = scale * coords.x, + y = scale * coords.y; + + return { + prefix: this._mapSettings.prefix, + nightday: /*(this._mapSettings.nightAndDay && this.options.dynmap.serverday) ? '_day' :*/ '', + scaledx: x >> 5, + scaledy: y >> 5, + zoom: this.zoomprefix(zoomoutlevel), + zoomprefix: (zoomoutlevel == 0) ? "" : (this.zoomprefix(zoomoutlevel) + "_"), + x: x, + y: y, + fmt: this._mapSettings.imageFormat || 'png' + }; + } + + getProjection(): DynmapProjection { + return this._projection; + } +}