ets2-dashboard-skin
Version:
Euro Truck Simulator 2 dashboard
357 lines (313 loc) • 11.5 kB
JavaScript
/**
* @author: Emmanuel SMITH <hey@emmanuel-smith.me>
* project: ets2-dashboard-skin
* file: _maps.js
* Date: 18/12/2020
* Time: 21:38
*/
import _history from '@/utils/_history';
import axios from 'axios';
import ol from 'ol';
import Vue from 'vue';
import store from '../store/index';
let d = {
map: null,
playerIcon: null,
playerFeature: null,
gBehaviorCenterOnPlayer: true,
gBehaviorRotateWithPlayer: true,
gIgnoreViewChangeEvents: false,
ready: false,
arrowRotate: '',
config: null,
paths: {
base: '',
tiles: 'tiles/{z}/{x}/{y}.png',
config: 'config.json'
}
};
const ZOOM_MIN = 0;
const ZOOM_MAX = 9;
const ZOOM_DEFAULT = 9;
const TILES_REMOTE_HOST = 'https://ets2.jagfx.fr';
// ----
/**
* TODO: Add verification for the min map version and the min map version allowed by the dash
*/
const initConfig = ( game ) => {
const type = store.getters[ 'config/get' ]( 'maps_map_activeMap' );
const tilesLocation = store.getters[ 'config/get' ]( 'maps_map_tilesLocations' );
const basePath = (tilesLocation === 'remote')
? `${ TILES_REMOTE_HOST }/maps/${ type }/${ game }/`
: `http://${ window.location.hostname }:3000/maps/${ type }/${ game }/`;
Vue.prototype.$pushALog( `Base path: ${ basePath } | Type: ${ type } | Tile location: ${ tilesLocation }`,
_history.HTY_ZONE.MAPS_INIT );
d.paths.base = basePath;
return axios
.get( d.paths.base + d.paths.config )
.then( response => {
//console.log( 'config', response.data );
d.config = response.data;
Vue.prototype.$pushALog( `Map config found`, _history.HTY_ZONE.MAPS_INIT );
const tilesPath = d.paths.tiles.replace( /{[xyz]}/g, 0 );
return axios
.get( d.paths.base + tilesPath )
.then( response => {
Vue.prototype.$pushALog( `Tiles OK: ${ d.paths.base + tilesPath }`, _history.HTY_ZONE.MAPS_INIT );
//console.log( 'tiles', response );
//d.config = response.data;
d.ready = true;
}, err => {
console.error( 'Cant get tiles', err );
Vue.prototype.$pushALog( `Tiles NOT FOUND`, _history.HTY_ZONE.MAPS_INIT, _history.HTY_LEVEL.ERROR );
throw 'Tiles NOT FOUND';
} );
}, err => {
console.error( 'Cant get config', err );
Vue.prototype.$pushALog( `Map config NOT FOUND`, _history.HTY_ZONE.MAPS_INIT, _history.HTY_LEVEL.ERROR );
throw 'Map config NOT FOUND';
} );
//console.log( game, type, tilesLocation, basePath );
//console.log( d.paths );
};
const initMap = () => {
let projection = new ol.proj.Projection( {
// Any name here. I chose "Funbit" because we are using funbit's image coordinates.
code: 'Funbit',
units: 'pixels',
extent: [ 0, 0, d.config.map.maxX, d.config.map.maxY ],
worldExtent: [ 0, 0, d.config.map.maxX, d.config.map.maxY ]
} );
ol.proj.addProjection( projection );
// Adding a marker for the player position/rotation.
d.playerIcon = new ol.style.Icon( {
anchor: [ 0.5, 39 ],
scale: .7,
anchorXUnits: 'fraction',
anchorYUnits: 'pixels',
rotateWithView: true,
src: 'https://github.com/meatlayer/ets2-mobile-route-advisor/raw/master/img/player_proportions.png'
} );
let playerIconStyle = new ol.style.Style( {
image: d.playerIcon
} );
d.playerFeature = new ol.Feature( {
geometry: new ol.geom.Point( [ d.config.map.maxX / 2, d.config.map.maxY / 2 ] )
} );
// For some reason, we cannot pass the style in the constructor.
d.playerFeature.setStyle( playerIconStyle );
// Adding a layer for features overlaid on the map.
let featureSource = new ol.source.Vector( {
features: [ d.playerFeature ],
wrapX: false
} );
let vectorLayer = new ol.layer.Vector( {
source: featureSource
} );
// Configuring the custom map tiles.
let custom_tilegrid = new ol.tilegrid.TileGrid( {
extent: [ 0, 0, d.config.map.maxX, d.config.map.maxY ],
minZoom: ZOOM_MIN,
origin: [ 0, d.config.map.maxY ],
//tileSize: [ 512, 512 ],
tileSize: d.config.map.tileSize,//[ 512, 512 ],
resolutions: (function () {
let r = [];
for ( let z = 0; z <= 8; ++z ) {
r[ z ] = Math.pow( 2, 8 - z );
}
return r;
})()
} );
// Creating custom controls.
//let rotate_control = new ol.control.Control( {
// //target: 'rotate-button-div'
// element: document.getElementById( 'rotate-wrapper' )
//} );
/*let speed_limit_control = new ol.control.Control({
element: document.getElementById('speed-limit')
});
let text_control = new ol.control.Control({
element: document.getElementById('map-text')
});*/
// Creating the map.
d.map = new ol.Map( {
target: 'map',
controls: [
//new ol.control.ZoomSlider(),
//new ol.control.OverviewMap(),
//new ol.control.Rotate(),
// new ol.control.MousePosition(), // DEBUG
// FIXME: Add way to custom the icon
new ol.control.Zoom( {
className: 'ol-zoom',
//zoomInLabel: '<i class="fas fa-search-plus"></i>',
//zoomOutLabel: '<i class="fas fa-search-minus"></i>',
target: 'ol-zoom-wrapper'
} )
//rotate_control
//speed_limit_control,
//text_control
// TODO: Set 'tipLabel' on both zoom and rotate controls to language-specific translations.
],
/*interactions: ol.interaction.defaults().extend( [
// Rotating by using two fingers is implemented in PinchRotate(), which is enabled by default.
// With DragRotateAndZoom(), it is possible to use Shift+mouse-drag to rotate the map.
// Without it, Shift+mouse-drag creates a rectangle to zoom to an area.
//new ol.interaction.DragRotateAndZoom()
] ),*/
layers: [
getMapTilesLayer( projection, custom_tilegrid ),
//getTextLayer(),
// Debug layer below.
//new ol.layer.Tile({
// extent: [0, 0, MAX_X, MAX_Y],
// source: new ol.source.TileDebug({
// projection: projection,
// tileGrid: custom_tilegrid,
// // tileGrid: ol.tilegrid.createXYZ({
// // extent: [0, 0, MAX_X, MAX_Y],
// // minZoom: 0,
// // maxZoom: 7,
// // tileSize: [256, 256]
// // }),
// wrapX: false
// })
//}),
vectorLayer
],
view: new ol.View( {
projection: projection,
extent: [ 0, 0, d.config.map.maxX, d.config.map.maxY ],
//center: ol.proj.transform([37.41, 8.82], 'EPSG:4326', 'EPSG:3857'),
center: [ d.config.map.maxX / 2, d.config.map.maxY / 2 ],
minZoom: ZOOM_MIN,
maxZoom: ZOOM_MAX,
zoom: ZOOM_DEFAULT
} )
} );
// Adding behavior to the custom button.
//let rotate_button = document.getElementById( 'rotate-button' );
//let rotate_arrow = rotate_button.firstElementChild;
//d.map.getView().on( 'change:rotation', function ( ev ) {
// //console.log( 'Plop' );
// d.arrowRotate = {
// transform: `rotate(${ ev.target.getRotation() }rad)`
// };
//} );
// Detecting when the user interacts with the map.
// https://stackoverflow.com/q/32868671/
d.map.getView().on( [ 'change:center', 'change:rotation' ], function ( ev ) {
//console.log( 'Hola', d.gIgnoreViewChangeEvents );
if ( d.gIgnoreViewChangeEvents ) {
return;
}
// The user has moved or rotated the map.
d.gBehaviorCenterOnPlayer = false;
// Not needed:
// g_behavior_rotate_with_player = false;
} );
// Debugging.
//d.map.getView().on('singleclick', function(evt) {
// let coordinate = evt.coordinate;
// console.log(coordinate);
//});
// map.getView().on('change:center', function(ev) {
// console.log(ev);
// });
// map.getView().on('change:rotation', function(ev) {
// console.log(ev);
// });
};
const init = ( game ) => {
return initConfig( game )
.then( () => initMap() );
};
// ----
const getMapTilesLayer = ( projection, tileGrid ) => {
return new ol.layer.Tile( {
extent: [ 0, 0, d.config.map.maxX, d.config.map.maxY ],
source: new ol.source.XYZ( {
projection: projection,
//url:
// 'https://github.com/meatlayer/ets2-mobile-route-advisor/raw/master/maps/ets2/tiles/{z}/{x}/{y}.png',
url: d.paths.base + d.paths.tiles,
//tileSize: [ 512, 512 ],
tileSize: d.config.map.tileSize,
// Using createXYZ() makes the vector layer (with the features) unaligned.
// It also tries loading non-existent tiles.
//
// Using custom_tilegrid causes rescaling of all image tiles before drawing
// (i.e. no image will be rendered at 1:1 pixels), But fixes all other issues.
tileGrid: tileGrid,
// tileGrid: ol.tilegrid.createXYZ({
// extent: [0, 0, MAX_X, MAX_Y],
// minZoom: 0,
// maxZoom: 7,
// tileSize: [256, 256]
// }),
wrapX: false,
minZoom: ZOOM_MIN,
maxZoom: ZOOM_MAX
} )
} );
};
const updatePlayerPositionAndRotation = ( lon, lat, rot, speed ) => {
if ( d.ready === null )
return;
let map_coords = gameCoordToPixels( lon, lat );
let rad = rot * Math.PI * 2;
d.playerFeature.getGeometry().setCoordinates( map_coords );
d.playerIcon.setRotation( -rad );
d.gIgnoreViewChangeEvents = true;
if ( d.gBehaviorCenterOnPlayer ) {
if ( d.gBehaviorRotateWithPlayer ) {
let height = d.map.getSize()[ 1 ];
let max_ahead_amount = height / 3.0 * d.map.getView().getResolution();
//console.log(parseFloat((speed).toFixed(0)));
//auto-zoom map by speed
if ( parseFloat( (speed).toFixed( 0 ) ) >= 15 && parseFloat( (speed).toFixed( 0 ) ) <= 35 ) {
d.map.getView().getZoom( d.map.getView().setZoom( 9 ) );
} else if ( parseFloat( (speed).toFixed( 0 ) ) >= 51 && parseFloat( (speed).toFixed( 0 ) ) <= 55 ) {
d.map.getView().getZoom( d.map.getView().setZoom( 8 ) );
} else if ( parseFloat( (speed).toFixed( 0 ) ) >= 61 && parseFloat( (speed).toFixed( 0 ) ) <= 65 ) {
d.map.getView().getZoom( d.map.getView().setZoom( 7 ) );
} else if ( parseFloat( (speed).toFixed( 0 ) ) >= 81 && parseFloat( (speed).toFixed( 0 ) ) <= 88 ) {
d.map.getView().getZoom( d.map.getView().setZoom( 6 ) );
}
let amount_ahead = speed * 0.25;
amount_ahead = Math.max( -max_ahead_amount, Math.min( amount_ahead, max_ahead_amount ) );
let ahead_coords = [
map_coords[ 0 ] + Math.sin( -rad ) * amount_ahead,
map_coords[ 1 ] + Math.cos( -rad ) * amount_ahead
];
d.map.getView().setCenter( ahead_coords );
d.map.getView().setRotation( rad );
} else {
d.map.getView().setCenter( map_coords );
d.map.getView().setRotation( 0 );
}
}
d.gIgnoreViewChangeEvents = false;
};
const gameCoordToPixels = ( x, y ) => {
if ( d.ready === null )
return;
//let r = [ x / 1.087326 + 57157, y / 1.087326 + 59287 ];
let r = [ x / d.config.transposition.x.factor + d.config.transposition.x.offset, y
/ d.config.transposition.y.factor
+ d.config.transposition.y.offset ];
// The United Kingdom of Great Britain and Northern Ireland
//if ( x < -31056.8 && y < -5832.867 ) {
// let r = [ x / 1.087326 + 57157, y / 1.087326 + 59287 ];
//}
r[ 1 ] = d.config.map.maxY - r[ 1 ];
return r;
};
export default {
d,
init,
getMapTilesLayer,
updatePlayerPositionAndRotation,
gameCoordToPixels
};