2020-12-16 16:54:41 +00:00
|
|
|
<!--
|
|
|
|
- Copyright 2020 James Lyne
|
|
|
|
-
|
|
|
|
- Licensed under the Apache License, Version 2.0 (the "License");
|
|
|
|
- you may not use this file except in compliance with the License.
|
|
|
|
- You may obtain a copy of the License at
|
|
|
|
-
|
|
|
|
- http://www.apache.org/licenses/LICENSE-2.0
|
|
|
|
-
|
|
|
|
- Unless required by applicable law or agreed to in writing, software
|
|
|
|
- distributed under the License is distributed on an "AS IS" BASIS,
|
|
|
|
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
|
|
- See the License for the specific language governing permissions and
|
|
|
|
- limitations under the License.
|
|
|
|
-->
|
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
<template>
|
2021-05-26 20:13:54 +00:00
|
|
|
<div class="map" :style="{backgroundColor: mapBackground }" v-bind="$attrs" :aria-label="mapTitle">
|
2021-05-28 18:50:08 +00:00
|
|
|
<template v-if="leaflet">
|
|
|
|
<MapLayer v-for="[name, map] in maps" :key="name" :map="map" :name="name" :leaflet="leaflet"></MapLayer>
|
|
|
|
<PlayersLayer v-if="playerMarkersEnabled" :leaflet="leaflet"></PlayersLayer>
|
|
|
|
<MarkerSetLayer v-for="[name, markerSet] in markerSets" :key="name" :markerSet="markerSet" :leaflet="leaflet"></MarkerSetLayer>
|
|
|
|
|
|
|
|
<LogoControl v-for="logo in logoControls" :key="JSON.stringify(logo)" :options="logo" :leaflet="leaflet"></LogoControl>
|
|
|
|
<CoordinatesControl v-if="coordinatesControlEnabled" :leaflet="leaflet"></CoordinatesControl>
|
|
|
|
<LinkControl v-if="linkControlEnabled" :leaflet="leaflet"></LinkControl>
|
|
|
|
<ClockControl v-if="clockControlEnabled" :leaflet="leaflet"></ClockControl>
|
|
|
|
<ChatControl v-if="chatBoxEnabled" :leaflet="leaflet"></ChatControl>
|
|
|
|
</template>
|
2020-12-01 23:20:38 +00:00
|
|
|
</div>
|
2021-05-24 19:51:19 +00:00
|
|
|
<MapContextMenu :leaflet="leaflet" v-if="leaflet"></MapContextMenu>
|
2020-12-01 23:20:38 +00:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script lang="ts">
|
2020-12-18 15:46:34 +00:00
|
|
|
import {computed, ref, defineComponent} from "@vue/runtime-core";
|
2020-12-13 02:50:17 +00:00
|
|
|
import {CRS, LatLng} from 'leaflet';
|
2020-12-01 23:20:38 +00:00
|
|
|
import {useStore} from '@/store';
|
|
|
|
import MapLayer from "@/components/map/layer/MapLayer.vue";
|
|
|
|
import PlayersLayer from "@/components/map/layer/PlayersLayer.vue";
|
|
|
|
import MarkerSetLayer from "@/components/map/layer/MarkerSetLayer.vue";
|
|
|
|
import CoordinatesControl from "@/components/map/control/CoordinatesControl.vue";
|
|
|
|
import ClockControl from "@/components/map/control/ClockControl.vue";
|
|
|
|
import LinkControl from "@/components/map/control/LinkControl.vue";
|
2020-12-17 14:50:12 +00:00
|
|
|
import ChatControl from "@/components/map/control/ChatControl.vue";
|
2020-12-01 23:20:38 +00:00
|
|
|
import LogoControl from "@/components/map/control/LogoControl.vue";
|
|
|
|
import {MutationTypes} from "@/store/mutation-types";
|
2020-12-13 02:50:17 +00:00
|
|
|
import {Coordinate, DynmapPlayer} from "@/dynmap";
|
2020-12-01 23:20:38 +00:00
|
|
|
import {ActionTypes} from "@/store/action-types";
|
2020-12-11 21:38:50 +00:00
|
|
|
import DynmapMap from "@/leaflet/DynmapMap";
|
2020-12-15 01:51:19 +00:00
|
|
|
import {LoadingControl} from "@/leaflet/control/LoadingControl";
|
2021-05-24 15:42:32 +00:00
|
|
|
import MapContextMenu from "@/components/map/MapContextMenu.vue";
|
2020-12-01 23:20:38 +00:00
|
|
|
|
|
|
|
export default defineComponent({
|
|
|
|
components: {
|
2021-05-24 15:42:32 +00:00
|
|
|
MapContextMenu,
|
2020-12-01 23:20:38 +00:00
|
|
|
MapLayer,
|
|
|
|
PlayersLayer,
|
|
|
|
MarkerSetLayer,
|
|
|
|
CoordinatesControl,
|
|
|
|
ClockControl,
|
|
|
|
LinkControl,
|
2020-12-17 14:50:12 +00:00
|
|
|
ChatControl,
|
2020-12-01 23:20:38 +00:00
|
|
|
LogoControl
|
|
|
|
},
|
|
|
|
|
|
|
|
setup() {
|
|
|
|
const store = useStore(),
|
2021-05-15 19:25:03 +00:00
|
|
|
leaflet = undefined as any,
|
2020-12-01 23:20:38 +00:00
|
|
|
|
|
|
|
maps = computed(() => store.state.maps),
|
|
|
|
markerSets = computed(() => store.state.markerSets),
|
|
|
|
configuration = computed(() => store.state.configuration),
|
|
|
|
|
|
|
|
playerMarkersEnabled = computed(() => store.getters.playerMarkersEnabled),
|
|
|
|
coordinatesControlEnabled = computed(() => store.getters.coordinatesControlEnabled),
|
|
|
|
clockControlEnabled = computed(() => store.getters.clockControlEnabled),
|
|
|
|
linkControlEnabled = computed(() => store.state.components.linkControl),
|
2021-01-06 16:02:36 +00:00
|
|
|
chatBoxEnabled = computed(() => store.state.components.chatBox),
|
2020-12-01 23:20:38 +00:00
|
|
|
logoControls = computed(() => store.state.components.logoControls),
|
|
|
|
|
|
|
|
currentWorld = computed(() => store.state.currentWorld),
|
|
|
|
currentMap = computed(() => store.state.currentMap),
|
|
|
|
currentProjection = computed(() => store.state.currentProjection),
|
2020-12-17 00:13:28 +00:00
|
|
|
mapBackground = computed(() => store.getters.mapBackground),
|
|
|
|
|
|
|
|
followTarget = computed(() => store.state.followTarget),
|
2020-12-18 15:46:34 +00:00
|
|
|
panTarget = computed(() => store.state.panTarget),
|
|
|
|
|
2021-01-27 00:11:08 +00:00
|
|
|
//Location and zoom to pan to upon next projection change
|
|
|
|
scheduledPan = ref<Coordinate|null>(null),
|
2021-05-26 20:13:54 +00:00
|
|
|
scheduledZoom = ref<number|null>(null),
|
|
|
|
|
|
|
|
mapTitle = computed(() => store.state.messages.mapTitle);
|
2020-12-01 23:20:38 +00:00
|
|
|
|
|
|
|
return {
|
|
|
|
leaflet,
|
|
|
|
maps,
|
|
|
|
markerSets,
|
|
|
|
configuration,
|
2021-05-24 15:42:32 +00:00
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
playerMarkersEnabled,
|
|
|
|
coordinatesControlEnabled,
|
|
|
|
clockControlEnabled,
|
|
|
|
linkControlEnabled,
|
2021-01-06 16:02:36 +00:00
|
|
|
chatBoxEnabled,
|
2021-05-24 15:42:32 +00:00
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
logoControls,
|
2020-12-17 00:13:28 +00:00
|
|
|
followTarget,
|
|
|
|
panTarget,
|
2020-12-01 23:20:38 +00:00
|
|
|
mapBackground,
|
2021-05-24 15:42:32 +00:00
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
currentWorld,
|
|
|
|
currentMap,
|
2021-01-27 00:11:08 +00:00
|
|
|
currentProjection,
|
2021-05-24 15:42:32 +00:00
|
|
|
|
2021-01-27 00:11:08 +00:00
|
|
|
scheduledPan,
|
2021-05-24 15:42:32 +00:00
|
|
|
scheduledZoom,
|
2021-05-26 20:13:54 +00:00
|
|
|
|
|
|
|
mapTitle
|
2020-12-01 23:20:38 +00:00
|
|
|
}
|
|
|
|
},
|
|
|
|
|
|
|
|
watch: {
|
2020-12-17 00:13:28 +00:00
|
|
|
followTarget: {
|
2020-12-01 23:20:38 +00:00
|
|
|
handler(newValue, oldValue) {
|
|
|
|
if (newValue) {
|
|
|
|
this.updateFollow(newValue, !oldValue || newValue.account !== oldValue.account);
|
|
|
|
}
|
|
|
|
},
|
|
|
|
deep: true
|
|
|
|
},
|
2020-12-17 00:13:28 +00:00
|
|
|
panTarget(newValue) {
|
|
|
|
if(newValue) {
|
2020-12-18 14:53:06 +00:00
|
|
|
//Immediately clear if on the correct world, to allow repeated panning
|
|
|
|
if(this.currentWorld && newValue.location.world === this.currentWorld.name) {
|
|
|
|
useStore().commit(MutationTypes.CLEAR_PAN_TARGET, undefined);
|
|
|
|
}
|
|
|
|
|
2020-12-17 00:13:28 +00:00
|
|
|
this.updateFollow(newValue, false);
|
|
|
|
}
|
|
|
|
},
|
2020-12-21 12:45:03 +00:00
|
|
|
currentProjection(newValue, oldValue) {
|
|
|
|
if(this.leaflet && newValue && oldValue) {
|
2021-01-27 00:11:08 +00:00
|
|
|
const panTarget = this.scheduledPan || oldValue.latLngToLocation(this.leaflet.getCenter(), 64);
|
|
|
|
|
|
|
|
if(this.scheduledZoom) {
|
|
|
|
this.leaflet!.setZoom(this.scheduledZoom, {
|
|
|
|
animate: false,
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
this.leaflet.panTo(newValue.locationToLatLng(panTarget), {
|
2020-12-21 12:45:03 +00:00
|
|
|
animate: false,
|
|
|
|
noMoveStart: true,
|
|
|
|
});
|
2021-01-27 00:11:08 +00:00
|
|
|
|
|
|
|
this.scheduledZoom = null;
|
|
|
|
this.scheduledPan = null;
|
2020-12-21 12:45:03 +00:00
|
|
|
}
|
|
|
|
},
|
2020-12-13 02:50:17 +00:00
|
|
|
currentWorld(newValue, oldValue) {
|
|
|
|
const store = useStore();
|
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
if(newValue) {
|
2021-01-27 00:11:08 +00:00
|
|
|
let location: Coordinate | null = this.scheduledPan;
|
2020-12-13 02:50:17 +00:00
|
|
|
|
|
|
|
store.dispatch(ActionTypes.GET_MARKER_SETS, undefined);
|
|
|
|
|
2020-12-18 14:53:06 +00:00
|
|
|
//Abort if follow target is present, to avoid panning twice
|
|
|
|
if(store.state.followTarget && store.state.followTarget.location.world === newValue.name) {
|
|
|
|
return;
|
|
|
|
//Abort if pan target is present, to avoid panning to the wrong place.
|
|
|
|
// Also clear it to allow repeated panning.
|
|
|
|
} else if(store.state.panTarget && store.state.panTarget.location.world === newValue.name) {
|
|
|
|
store.commit(MutationTypes.CLEAR_PAN_TARGET, undefined);
|
|
|
|
return;
|
|
|
|
//Otherwise pan to url location, if present and if we have just loaded
|
|
|
|
} else if(!oldValue && store.state.parsedUrl.location) {
|
2020-12-13 02:50:17 +00:00
|
|
|
location = store.state.parsedUrl.location;
|
2020-12-18 14:53:06 +00:00
|
|
|
//Otherwise pan to world center
|
|
|
|
} else {
|
|
|
|
location = newValue.center;
|
2020-12-13 02:50:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if(!oldValue) {
|
2021-01-27 00:11:08 +00:00
|
|
|
this.scheduledZoom = typeof store.state.parsedUrl.zoom !== 'undefined' ?
|
2020-12-21 18:38:35 +00:00
|
|
|
store.state.parsedUrl.zoom : store.state.configuration.defaultZoom;
|
2020-12-13 02:50:17 +00:00
|
|
|
}
|
|
|
|
|
2021-01-27 00:11:08 +00:00
|
|
|
//Set pan location for when the projection changes
|
|
|
|
this.scheduledPan = location;
|
2020-12-01 23:20:38 +00:00
|
|
|
}
|
2021-01-27 00:11:08 +00:00
|
|
|
}
|
2020-12-01 23:20:38 +00:00
|
|
|
},
|
|
|
|
|
|
|
|
mounted() {
|
2021-05-24 19:51:19 +00:00
|
|
|
this.leaflet = new DynmapMap(this.$el.nextElementSibling, Object.freeze({
|
2020-12-01 23:20:38 +00:00
|
|
|
zoom: this.configuration.defaultZoom,
|
2020-12-12 22:04:56 +00:00
|
|
|
center: new LatLng(0, 0),
|
2020-12-01 23:20:38 +00:00
|
|
|
fadeAnimation: false,
|
|
|
|
zoomAnimation: true,
|
|
|
|
zoomControl: true,
|
|
|
|
preferCanvas: true,
|
|
|
|
attributionControl: false,
|
2020-12-12 22:04:56 +00:00
|
|
|
crs: CRS.Simple,
|
2020-12-01 23:20:38 +00:00
|
|
|
worldCopyJump: false,
|
2020-12-10 02:22:29 +00:00
|
|
|
// markerZoomAnimation: false,
|
2021-05-15 19:25:03 +00:00
|
|
|
})) as DynmapMap;
|
2020-12-01 23:20:38 +00:00
|
|
|
|
2021-05-26 22:55:50 +00:00
|
|
|
window.addEventListener('keydown', this.handleKeydown);
|
|
|
|
|
2020-12-19 12:16:07 +00:00
|
|
|
this.leaflet.createPane('vectors');
|
|
|
|
|
2020-12-15 01:51:19 +00:00
|
|
|
this.leaflet.addControl(new LoadingControl({
|
|
|
|
position: 'topleft',
|
|
|
|
delayIndicator: 500,
|
|
|
|
}));
|
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
this.leaflet.on('moveend', () => {
|
2020-12-13 02:50:17 +00:00
|
|
|
useStore().commit(MutationTypes.SET_CURRENT_LOCATION, this.currentProjection.latLngToLocation(this.leaflet!.getCenter(), 64));
|
|
|
|
});
|
|
|
|
|
|
|
|
this.leaflet.on('zoomend', () => {
|
|
|
|
useStore().commit(MutationTypes.SET_CURRENT_ZOOM, this.leaflet!.getZoom());
|
|
|
|
});
|
2020-12-01 23:20:38 +00:00
|
|
|
},
|
|
|
|
|
2021-05-26 22:55:50 +00:00
|
|
|
unmounted() {
|
|
|
|
window.removeEventListener('keydown', this.handleKeydown);
|
2021-05-28 18:50:08 +00:00
|
|
|
this.leaflet.remove();
|
2021-05-26 22:55:50 +00:00
|
|
|
},
|
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
methods: {
|
2021-05-26 22:55:50 +00:00
|
|
|
handleKeydown(e: KeyboardEvent) {
|
|
|
|
if(e.key === 'Escape') {
|
|
|
|
e.preventDefault();
|
|
|
|
this.leaflet.getContainer().focus();
|
|
|
|
}
|
|
|
|
},
|
2020-12-01 23:20:38 +00:00
|
|
|
updateFollow(player: DynmapPlayer, newFollow: boolean) {
|
|
|
|
const store = useStore(),
|
2021-01-27 00:11:08 +00:00
|
|
|
followMapName = store.state.configuration.followMap,
|
2020-12-01 23:20:38 +00:00
|
|
|
currentWorld = store.state.currentWorld;
|
|
|
|
|
2021-01-27 00:11:08 +00:00
|
|
|
let targetWorld = null;
|
2020-12-18 15:46:34 +00:00
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
if(!this.leaflet) {
|
2020-12-31 13:55:05 +00:00
|
|
|
console.warn(`Cannot follow ${player.account}. Map not yet initialized.`);
|
2020-12-18 15:46:34 +00:00
|
|
|
return;
|
2020-12-01 23:20:38 +00:00
|
|
|
}
|
|
|
|
|
2020-12-18 16:04:06 +00:00
|
|
|
if(player.hidden) {
|
2020-12-31 13:55:05 +00:00
|
|
|
console.warn(`Cannot follow ${player.account}. Player is hidden from the map.`);
|
2020-12-18 16:04:06 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
if(!player.location.world) {
|
2020-12-31 13:55:05 +00:00
|
|
|
console.warn(`Cannot follow ${player.account}. Player isn't in a known world.`);
|
2020-12-01 23:20:38 +00:00
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
if(!currentWorld || currentWorld.name !== player.location.world) {
|
2021-01-27 00:11:08 +00:00
|
|
|
targetWorld = store.state.worlds.get(player.location.world);
|
|
|
|
} else {
|
|
|
|
targetWorld = currentWorld;
|
|
|
|
}
|
2020-12-01 23:20:38 +00:00
|
|
|
|
2021-01-27 00:11:08 +00:00
|
|
|
if (!targetWorld) {
|
|
|
|
console.warn(`Cannot follow ${player.account}. Player isn't in a known world.`);
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
let map = followMapName && targetWorld.maps.has(followMapName)
|
|
|
|
? targetWorld.maps.get(followMapName)
|
|
|
|
: targetWorld.maps.entries().next().value[1]
|
2020-12-01 23:20:38 +00:00
|
|
|
|
2021-01-27 00:33:21 +00:00
|
|
|
if(map !== store.state.currentMap && (targetWorld !== currentWorld || newFollow)) {
|
2021-01-27 00:11:08 +00:00
|
|
|
this.scheduledPan = player.location;
|
2020-12-01 23:20:38 +00:00
|
|
|
|
2021-01-27 00:11:08 +00:00
|
|
|
if(newFollow) {
|
|
|
|
console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
|
|
|
|
this.scheduledZoom = store.state.configuration.followZoom;
|
2020-12-01 23:20:38 +00:00
|
|
|
}
|
|
|
|
|
2021-01-27 00:11:08 +00:00
|
|
|
console.log(`Switching map to match player ${targetWorld.name} ${map.name}`);
|
|
|
|
store.commit(MutationTypes.SET_CURRENT_MAP, {worldName: targetWorld.name, mapName: map.name});
|
|
|
|
} else {
|
2020-12-18 15:46:34 +00:00
|
|
|
this.leaflet!.panTo(store.state.currentProjection.locationToLatLng(player.location));
|
2020-12-01 23:20:38 +00:00
|
|
|
|
2020-12-18 15:46:34 +00:00
|
|
|
if(newFollow) {
|
|
|
|
console.log(`Setting zoom for new follow ${store.state.configuration.followZoom}`);
|
|
|
|
this.leaflet!.setZoom(store.state.configuration.followZoom);
|
|
|
|
}
|
2021-01-27 00:11:08 +00:00
|
|
|
}
|
2020-12-01 23:20:38 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
})
|
|
|
|
</script>
|
|
|
|
|
2021-05-25 12:13:02 +00:00
|
|
|
<style lang="scss" scoped>
|
|
|
|
@import '../scss/_mixins.scss';
|
|
|
|
|
2020-12-01 23:20:38 +00:00
|
|
|
.map {
|
|
|
|
width: 100%;
|
|
|
|
height: 100%;
|
2021-05-15 22:24:29 +00:00
|
|
|
background: transparent;
|
2020-12-01 23:20:38 +00:00
|
|
|
z-index: 0;
|
2021-05-24 15:42:32 +00:00
|
|
|
cursor: default;
|
2021-05-25 12:13:02 +00:00
|
|
|
box-sizing: border-box;
|
|
|
|
position: relative;
|
|
|
|
|
|
|
|
@include focus {
|
|
|
|
&:before {
|
|
|
|
content: '';
|
|
|
|
position: absolute;
|
|
|
|
top: 0;
|
|
|
|
right: 0;
|
|
|
|
bottom: 0;
|
|
|
|
left: 0;
|
|
|
|
border: 0.2rem solid #cccccc;
|
|
|
|
display: block;
|
|
|
|
z-index: 2000;
|
2021-05-25 22:46:26 +00:00
|
|
|
pointer-events: none;
|
2021-05-25 12:13:02 +00:00
|
|
|
}
|
|
|
|
}
|
2020-12-01 23:20:38 +00:00
|
|
|
}
|
|
|
|
</style>
|