From 5d0f26917d43de4316e0ba8e623ec21cad6e4a8c Mon Sep 17 00:00:00 2001 From: James Lyne Date: Tue, 1 Dec 2020 23:20:38 +0000 Subject: [PATCH] More progress --- package-lock.json | 473 ++++++++ package.json | 6 +- public/images/armor.png | Bin 0 -> 213 bytes public/images/armor_depleted.png | Bin 0 -> 192 bytes public/images/blank.png | Bin 0 -> 167 bytes public/images/block_nether_flat.png | Bin 0 -> 2620 bytes public/images/block_nether_surface.png | Bin 0 -> 340 bytes public/images/block_other.png | Bin 0 -> 330 bytes public/images/block_skylands.png | Bin 0 -> 993 bytes public/images/block_the_end_flat.png | Bin 0 -> 2089 bytes public/images/block_the_end_surface.png | Bin 0 -> 1062 bytes public/images/block_world_biome.png | Bin 0 -> 1092 bytes public/images/block_world_cave.png | Bin 0 -> 339 bytes public/images/block_world_flat.png | Bin 0 -> 387 bytes public/images/block_world_surface.png | Bin 0 -> 342 bytes public/images/book.png | Bin 0 -> 829 bytes public/images/cave_off.png | Bin 0 -> 1134 bytes public/images/cave_on.png | Bin 0 -> 868 bytes public/images/chat_bubble.png | Bin 0 -> 395 bytes public/images/chat_cursor.png | Bin 0 -> 261 bytes public/images/clock_day.png | Bin 0 -> 357 bytes public/images/clock_night.png | Bin 0 -> 360 bytes public/images/compass.png | Bin 0 -> 5203 bytes public/images/compass_E.png | Bin 0 -> 4323 bytes public/images/compass_N.png | Bin 0 -> 4187 bytes public/images/compass_NE.png | Bin 0 -> 5165 bytes public/images/compass_NW.png | Bin 0 -> 5133 bytes public/images/compass_SW.png | Bin 0 -> 5183 bytes public/images/compass_W.png | Bin 0 -> 4205 bytes public/images/compass_alt.png | Bin 0 -> 802 bytes public/images/compass_flat.png | Bin 0 -> 1844 bytes public/images/dynmap.ico | Bin 0 -> 1150 bytes public/images/follow_off.png | Bin 0 -> 377 bytes public/images/follow_on.png | Bin 0 -> 376 bytes public/images/heart.png | Bin 0 -> 198 bytes public/images/heart_depleted.png | Bin 0 -> 183 bytes public/images/home.png | Bin 0 -> 428 bytes {src/assets/css => public}/images/link.png | Bin public/images/list_off.png | Bin 0 -> 351 bytes public/images/list_on.png | Bin 0 -> 360 bytes public/images/moon.png | Bin 0 -> 3248 bytes public/images/player.png | Bin 0 -> 328 bytes public/images/player_death.png | Bin 0 -> 404 bytes public/images/player_face.png | Bin 0 -> 459 bytes public/images/player_follow_off.png | Bin 0 -> 361 bytes public/images/player_follow_on.gif | Bin 0 -> 332 bytes public/images/player_follow_on.png | Bin 0 -> 355 bytes public/images/player_travel.png | Bin 0 -> 454 bytes public/images/scrolldown.png | Bin 0 -> 1019 bytes public/images/scrollup.png | Bin 0 -> 1005 bytes public/images/server.png | Bin 0 -> 368 bytes public/images/sidebar_hint.png | Bin 0 -> 271 bytes public/images/sign.png | Bin 0 -> 681 bytes public/images/sign_home.png | Bin 0 -> 407 bytes public/images/sign_sign.png | Bin 0 -> 431 bytes public/images/sign_sign_alt.png | Bin 0 -> 431 bytes public/images/sign_warp.png | Bin 0 -> 435 bytes public/images/spawn.png | Bin 0 -> 943 bytes public/images/sun.png | Bin 0 -> 5319 bytes public/images/warp.png | Bin 0 -> 1189 bytes public/images/weather_stormy.png | Bin 0 -> 1285 bytes public/images/weather_stormy_day.png | Bin 0 -> 2665 bytes public/images/weather_stormy_night.png | Bin 0 -> 2968 bytes public/images/weather_sunny.png | Bin 0 -> 905 bytes public/images/weather_sunny_day.png | Bin 0 -> 2385 bytes public/images/weather_sunny_night.png | Bin 0 -> 2273 bytes public/images/weather_thunder.png | Bin 0 -> 627 bytes public/images/weather_thunder_day.png | Bin 0 -> 2818 bytes public/images/weather_thunder_night.png | Bin 0 -> 2898 bytes public/images/window_close.png | Bin 0 -> 374 bytes public/images/window_close_hover.png | Bin 0 -> 355 bytes public/images/window_open.png | Bin 0 -> 326 bytes public/images/window_pinned.png | Bin 0 -> 299 bytes public/images/window_pinned_hover.png | Bin 0 -> 290 bytes public/images/window_unpinned.png | Bin 0 -> 326 bytes public/images/zoom_in.png | Bin 0 -> 366 bytes public/images/zoom_out.png | Bin 0 -> 336 bytes public/index.html | 74 ++ src/App.vue | 7 +- src/api.ts | 261 ++++- src/assets/css/dynmap_style.css | 1028 ----------------- src/assets/css/images/layers.png | Bin 973 -> 0 bytes src/assets/css/images/marker-icon.png | Bin 1747 -> 0 bytes src/assets/css/images/marker-icon@2x.png | Bin 4033 -> 0 bytes src/assets/css/images/marker-shadow.png | Bin 797 -> 0 bytes src/assets/css/images/marker.png | Bin 2519 -> 0 bytes src/assets/css/images/popup-close.png | Bin 1125 -> 0 bytes src/assets/css/images/zoom-in.png | Bin 963 -> 0 bytes src/assets/css/images/zoom-out.png | Bin 959 -> 0 bytes src/assets/css/rtgame.css | 371 ------ src/assets/images/link.png | Bin 0 -> 4159 bytes src/assets/images/moon.png | Bin 3248 -> 3035 bytes src/assets/images/people.png | Bin 0 -> 3781 bytes src/assets/images/rain.png | Bin 0 -> 5098 bytes src/assets/images/storm.png | Bin 0 -> 4980 bytes src/assets/images/sun.png | Bin 5319 -> 3366 bytes src/assets/images/world 1.png | Bin 0 -> 6058 bytes src/assets/images/world 2.png | Bin 0 -> 5013 bytes src/components/Leaflet.vue | 50 - src/components/Map.vue | 195 ++++ src/components/PlayerListItem.vue | 24 - src/components/Sidebar.vue | 120 +- src/components/map/control/ClockControl.vue | 56 + .../map/control/CoordinatesControl.vue | 43 + src/components/map/control/LinkControl.vue | 40 + src/components/map/control/LogoControl.vue | 42 + src/components/map/layer/MapLayer.vue | 46 +- src/components/map/layer/MarkerSetLayer.vue | 65 ++ src/components/map/layer/PlayersLayer.vue | 34 +- src/components/map/marker/GenericMarker.vue | 74 ++ src/components/map/marker/PlayerMarker.vue | 89 +- src/components/map/vector/Areas.vue | 237 ++++ src/components/map/vector/Circle.vue | 81 ++ src/components/sidebar/FollowTarget.vue | 65 ++ src/components/{ => sidebar}/PlayerList.vue | 10 +- src/components/sidebar/PlayerListItem.vue | 68 ++ src/components/{ => sidebar}/WorldList.vue | 10 +- .../{ => sidebar}/WorldListItem.vue | 50 +- src/dynmap.d.ts | 92 +- src/leaflet/control/ClockControl.ts | 123 ++ src/leaflet/control/CoordinatesControl.js | 108 -- src/leaflet/control/CoordinatesControl.ts | 148 +++ src/leaflet/control/LayerControl.ts | 78 ++ src/leaflet/control/LinkControl.ts | 55 +- src/leaflet/control/LogoControl.js | 50 - src/leaflet/control/LogoControl.ts | 45 + src/leaflet/icon/DynmapIcon.ts | 69 ++ src/leaflet/icon/PlayerIcon.ts | 55 +- src/leaflet/map.ts | 29 + src/leaflet/mapType/HDMapType.ts | 4 +- src/leaflet/marker/DynmapMarker.ts | 25 + src/leaflet/marker/PlayerMarker.ts | 23 +- src/leaflet/projection/DynmapProjection.ts | 6 +- src/leaflet/tileLayer/DynmapTileLayer.ts | 123 +- src/main.ts | 6 +- src/scss/_placeholders.scss | 39 + src/scss/_variables.scss | 4 + src/scss/leaflet/_controls.scss | 164 +++ src/scss/leaflet/_popups.scss | 13 + src/scss/style.scss | 658 +++++++++++ src/store/action-types.ts | 2 + src/store/actions.ts | 60 +- src/store/getters.ts | 10 + src/store/index.ts | 4 +- src/store/mutation-types.ts | 10 +- src/store/mutations.ts | 153 ++- src/store/state.ts | 47 +- 147 files changed, 3917 insertions(+), 1905 deletions(-) create mode 100644 public/images/armor.png create mode 100644 public/images/armor_depleted.png create mode 100644 public/images/blank.png create mode 100644 public/images/block_nether_flat.png create mode 100644 public/images/block_nether_surface.png create mode 100644 public/images/block_other.png create mode 100644 public/images/block_skylands.png create mode 100644 public/images/block_the_end_flat.png create mode 100644 public/images/block_the_end_surface.png create mode 100644 public/images/block_world_biome.png create mode 100644 public/images/block_world_cave.png create mode 100644 public/images/block_world_flat.png create mode 100644 public/images/block_world_surface.png create mode 100644 public/images/book.png create mode 100644 public/images/cave_off.png create mode 100644 public/images/cave_on.png create mode 100644 public/images/chat_bubble.png create mode 100644 public/images/chat_cursor.png create mode 100644 public/images/clock_day.png create mode 100644 public/images/clock_night.png create mode 100644 public/images/compass.png create mode 100644 public/images/compass_E.png create mode 100644 public/images/compass_N.png create mode 100644 public/images/compass_NE.png create mode 100644 public/images/compass_NW.png create mode 100644 public/images/compass_SW.png create mode 100644 public/images/compass_W.png create mode 100644 public/images/compass_alt.png create mode 100644 public/images/compass_flat.png create mode 100644 public/images/dynmap.ico create mode 100644 public/images/follow_off.png create mode 100644 public/images/follow_on.png create mode 100644 public/images/heart.png create mode 100644 public/images/heart_depleted.png create mode 100644 public/images/home.png rename {src/assets/css => public}/images/link.png (100%) create mode 100644 public/images/list_off.png create mode 100644 public/images/list_on.png create mode 100644 public/images/moon.png create mode 100644 public/images/player.png create mode 100644 public/images/player_death.png create mode 100644 public/images/player_face.png create mode 100644 public/images/player_follow_off.png create mode 100644 public/images/player_follow_on.gif create mode 100644 public/images/player_follow_on.png create mode 100644 public/images/player_travel.png create mode 100644 public/images/scrolldown.png create mode 100644 public/images/scrollup.png create mode 100644 public/images/server.png create mode 100644 public/images/sidebar_hint.png create mode 100644 public/images/sign.png create mode 100644 public/images/sign_home.png create mode 100644 public/images/sign_sign.png create mode 100644 public/images/sign_sign_alt.png create mode 100644 public/images/sign_warp.png create mode 100644 public/images/spawn.png create mode 100644 public/images/sun.png create mode 100644 public/images/warp.png create mode 100644 public/images/weather_stormy.png create mode 100644 public/images/weather_stormy_day.png create mode 100644 public/images/weather_stormy_night.png create mode 100644 public/images/weather_sunny.png create mode 100644 public/images/weather_sunny_day.png create mode 100644 public/images/weather_sunny_night.png create mode 100644 public/images/weather_thunder.png create mode 100644 public/images/weather_thunder_day.png create mode 100644 public/images/weather_thunder_night.png create mode 100644 public/images/window_close.png create mode 100644 public/images/window_close_hover.png create mode 100644 public/images/window_open.png create mode 100644 public/images/window_pinned.png create mode 100644 public/images/window_pinned_hover.png create mode 100644 public/images/window_unpinned.png create mode 100644 public/images/zoom_in.png create mode 100644 public/images/zoom_out.png delete mode 100644 src/assets/css/dynmap_style.css delete mode 100644 src/assets/css/images/layers.png delete mode 100644 src/assets/css/images/marker-icon.png delete mode 100644 src/assets/css/images/marker-icon@2x.png delete mode 100644 src/assets/css/images/marker-shadow.png delete mode 100644 src/assets/css/images/marker.png delete mode 100644 src/assets/css/images/popup-close.png delete mode 100644 src/assets/css/images/zoom-in.png delete mode 100644 src/assets/css/images/zoom-out.png delete mode 100644 src/assets/css/rtgame.css create mode 100644 src/assets/images/link.png create mode 100644 src/assets/images/people.png create mode 100644 src/assets/images/rain.png create mode 100644 src/assets/images/storm.png create mode 100644 src/assets/images/world 1.png create mode 100644 src/assets/images/world 2.png delete mode 100644 src/components/Leaflet.vue create mode 100644 src/components/Map.vue delete mode 100644 src/components/PlayerListItem.vue create mode 100644 src/components/map/control/ClockControl.vue create mode 100644 src/components/map/control/CoordinatesControl.vue create mode 100644 src/components/map/control/LinkControl.vue create mode 100644 src/components/map/control/LogoControl.vue create mode 100644 src/components/map/layer/MarkerSetLayer.vue create mode 100644 src/components/map/marker/GenericMarker.vue create mode 100644 src/components/map/vector/Areas.vue create mode 100644 src/components/map/vector/Circle.vue create mode 100644 src/components/sidebar/FollowTarget.vue rename src/components/{ => sidebar}/PlayerList.vue (67%) create mode 100644 src/components/sidebar/PlayerListItem.vue rename src/components/{ => sidebar}/WorldList.vue (66%) rename src/components/{ => sidebar}/WorldListItem.vue (55%) create mode 100644 src/leaflet/control/ClockControl.ts delete mode 100644 src/leaflet/control/CoordinatesControl.js create mode 100644 src/leaflet/control/CoordinatesControl.ts create mode 100644 src/leaflet/control/LayerControl.ts delete mode 100644 src/leaflet/control/LogoControl.js create mode 100644 src/leaflet/control/LogoControl.ts create mode 100644 src/leaflet/icon/DynmapIcon.ts create mode 100644 src/leaflet/map.ts create mode 100644 src/leaflet/marker/DynmapMarker.ts create mode 100644 src/scss/_placeholders.scss create mode 100644 src/scss/leaflet/_controls.scss create mode 100644 src/scss/leaflet/_popups.scss create mode 100644 src/scss/style.scss diff --git a/package-lock.json b/package-lock.json index 3735184..c048f2c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1209,6 +1209,391 @@ "fastq": "^1.6.0" } }, + "@pixi/accessibility": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/accessibility/-/accessibility-5.3.3.tgz", + "integrity": "sha512-wC/enJtw5CrdWnu6l5u3VN9UIZPumNSNXlGez2BULY0osiLTywHJPdHpmXMz2YPXw75GsEBzkEvK4LTtnTp21A==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/app": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/app/-/app-5.3.3.tgz", + "integrity": "sha512-OkO7Kq3N+FPRshVmApuiHKBpobic56VYbLVCMYPy6rjV0hc5ctkchKGFyouJuPt/rHeI6FrqZ0TaON1TShnKiA==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3" + } + }, + "@pixi/constants": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/constants/-/constants-5.3.3.tgz", + "integrity": "sha512-IybgxzLlEPm7ihp70cLNKc3IPyqkFuW+idk9Zw2St+OayJTw5ctCnLAg9cducwIVHjPYTvN46BYDa+n0KRWZYw==", + "dev": true + }, + "@pixi/core": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/core/-/core-5.3.3.tgz", + "integrity": "sha512-taw50LnzV+TQVMx5HQA2ZJgF9wuhZ6DeoXHW2KkevYB0ekKYnEO2VMMiRDMcmchtyvHclJebzjeHZLGqDtKDgw==", + "dev": true, + "requires": { + "@pixi/constants": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/runner": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/ticker": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/display": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/display/-/display-5.3.3.tgz", + "integrity": "sha512-dPm7Vk2BH9byu6RHBYsI9MtjUU8x1HNm/PIi6lIlxANhTjWnhxwfvmrGE7ZcRLThTenNdDVlZ2ke2XAXP98UgA==", + "dev": true, + "requires": { + "@pixi/math": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/extract": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/extract/-/extract-5.3.3.tgz", + "integrity": "sha512-CE0GA+tEBPurpaXER2B1aq1sdumKLtCqE/Mms6fYUkIKF9D0Ogw9rqo79QCL9XkLMexa7xVeC3KPPiXW5wrOaA==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/filter-alpha": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-alpha/-/filter-alpha-5.3.3.tgz", + "integrity": "sha512-AxyHLnvO892va9raZbMMtMtEGDVqO8SvEHHNnCjTBEZ67kVKy0HEYXFOBA6nJZ6BiTgGp9js+7kevi11tfqnJQ==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3" + } + }, + "@pixi/filter-blur": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-blur/-/filter-blur-5.3.3.tgz", + "integrity": "sha512-vLN1DL6PQXo4p7j/32PZIf+lhcBVfb9hdphSmtbxlAlpbhMWI52n3YUkeInwHs7Ev08NyhI/UhNWHqjN/lAM3w==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/settings": "5.3.3" + } + }, + "@pixi/filter-color-matrix": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-color-matrix/-/filter-color-matrix-5.3.3.tgz", + "integrity": "sha512-HFr+vth5ZHHEFJYcjtWZ+O0s7Z2YWJyDyxr+nTd5Q8AT7gMDTVehpNVrm7ByaCKeEovOZzZI6A347+WmHcNpGg==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3" + } + }, + "@pixi/filter-displacement": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-displacement/-/filter-displacement-5.3.3.tgz", + "integrity": "sha512-kvrKMgqW4ELg+yT2p5vmu6h/IER/L8GD1PWyXovnzpI8RG7k8l136F9VvA3wkB6sYuNcXiDtqMtRQy5e6O4+rw==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/math": "5.3.3" + } + }, + "@pixi/filter-fxaa": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-fxaa/-/filter-fxaa-5.3.3.tgz", + "integrity": "sha512-p4vKdBwaoGRNZcoHz2ET8hBF1SoWvy9xU2B3Ci32+c0dg89ZUdGTEW0zimUHi2gMdU+2v/T0lqZ9NC9B6WVYAg==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3" + } + }, + "@pixi/filter-noise": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/filter-noise/-/filter-noise-5.3.3.tgz", + "integrity": "sha512-HCky3XPk6BYGXTS7d9/FnAHnqq7Rwm5Rlj2XtWW3JItXGCScEBII227xYwrJu5Ke84tpVlDXK4W1/BevZ1AwlQ==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3" + } + }, + "@pixi/graphics": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/graphics/-/graphics-5.3.3.tgz", + "integrity": "sha512-1bn9Jptg3JXgVOw0SrEMdmjSwkTBYDm6fPnPnh4goF3yDozh0xEqmXobVtCgy2fulMfHRzIfbgtRxrBf2mkCAg==", + "dev": true, + "requires": { + "@pixi/constants": "5.3.3", + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/sprite": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/interaction": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/interaction/-/interaction-5.3.3.tgz", + "integrity": "sha512-Tjuw4XwmrG1fhGzfn5oGspRJT2OtlH+6V7AHscH0v5Ht1Kvk6aKjNncZuSCXllhGGlIuMu3Nn9WPvDEIvW3JNw==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/ticker": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/loaders": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/loaders/-/loaders-5.3.3.tgz", + "integrity": "sha512-wj0DzniApfDoZA/buMmO/CgCB7Q7SsESForHh7wSd7UC8rrCmz5prUTEICmJGhdHpBuVB7KDPtwaaLtr9Q/kQg==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/utils": "5.3.3", + "resource-loader": "^3.0.1" + } + }, + "@pixi/math": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/math/-/math-5.3.3.tgz", + "integrity": "sha512-k5C3kQpxlGm2AdBJEUjjW2l2YlSvTKf+54vNOjD4UcEfRoDevC5p4Zg49q3UAu855lrs5qw49AbkrFKsQvPIRA==", + "dev": true + }, + "@pixi/mesh": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/mesh/-/mesh-5.3.3.tgz", + "integrity": "sha512-q8w70oAFNdArzOHVnsn7ban68NmO5S5TMg6qSez4A8te6cebMRQsNrT/0dQ/nZcG7ACFK4jiYfbXRQivO+jgVA==", + "dev": true, + "requires": { + "@pixi/constants": "5.3.3", + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/mesh-extras": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/mesh-extras/-/mesh-extras-5.3.3.tgz", + "integrity": "sha512-V2hARC7nUPaTEFxd+B8GDkSMrMZ38S8/IInqtYzGUy6FtFs7IYKty9Rz/G665eN7ThIq8tZrOVZOl6JRBtEC8A==", + "dev": true, + "requires": { + "@pixi/constants": "5.3.3", + "@pixi/core": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/mesh": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/mixin-cache-as-bitmap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/mixin-cache-as-bitmap/-/mixin-cache-as-bitmap-5.3.3.tgz", + "integrity": "sha512-P1mo3HKDWS8IZLgaP8gujiy4We4vRcxJH6EvQAevf+GsBzdjKfcGgkKzVb9HlyQvsXML5gpTOJuw5eKgRTxSQA==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/sprite": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/mixin-get-child-by-name": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/mixin-get-child-by-name/-/mixin-get-child-by-name-5.3.3.tgz", + "integrity": "sha512-CksDZ5ZG4/tHZfDOwSuznANduasJg5JR89X3D6E9DVYx4CLVE3G2K1sbeiOJNXfGIKy30UoSD7Y7IFmUzLxp/g==", + "dev": true, + "requires": { + "@pixi/display": "5.3.3" + } + }, + "@pixi/mixin-get-global-position": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/mixin-get-global-position/-/mixin-get-global-position-5.3.3.tgz", + "integrity": "sha512-M3faQYDW/ISa1+lhVkjHXRALJ33BMzLN+7x9ucx8VeCmUWvcaLlRo3CaxZsgiR+52Fii5WHl/PF/cMzdkRMF9g==", + "dev": true, + "requires": { + "@pixi/display": "5.3.3", + "@pixi/math": "5.3.3" + } + }, + "@pixi/particles": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/particles/-/particles-5.3.3.tgz", + "integrity": "sha512-t+lG8iGNYyS6ujKvC9qQjKzyxvjxqbFxvB6hkXcOKR98JWM2726ZguHouFlIbOzOxYAGoeuHIWSDlnQNvnVE2g==", + "dev": true, + "requires": { + "@pixi/constants": "5.3.3", + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/polyfill": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/polyfill/-/polyfill-5.3.3.tgz", + "integrity": "sha512-gmx67A6VmwKllxfIMQWzMUNJ8wJfWPT5FlUR0SoPastdTB/SfbgbyQBgKLZHqgmc6LOh2CrOLhN423lNiAroeA==", + "dev": true, + "requires": { + "es6-promise-polyfill": "^1.2.0", + "object-assign": "^4.1.1" + } + }, + "@pixi/prepare": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/prepare/-/prepare-5.3.3.tgz", + "integrity": "sha512-DPsKWfYJ97J67YCjPU6uvU+LBdw+64O9LG9vmzfChmYXom5VMQF9yUC6ZoYTHUPmH31iilqzGeMlPUTobnqSog==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/graphics": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/text": "5.3.3", + "@pixi/ticker": "5.3.3" + } + }, + "@pixi/runner": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/runner/-/runner-5.3.3.tgz", + "integrity": "sha512-7eLZxxT+PwxuwzcRL1egrnEdLHwD41yFb24pMSo6XM86ppP1tdBjrv5+pLDnUuDEfNjZQxx07FAlZY+sMKANmw==", + "dev": true + }, + "@pixi/settings": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/settings/-/settings-5.3.3.tgz", + "integrity": "sha512-1MYJokqpPUtvYEX0BVi0Pq2Xi6KGmWDV5hlQnTXY9NGv6tmqrPYvIb/uHFaDyVUWmrqsFL3xZ4W5zMo+c/dwVA==", + "dev": true, + "requires": { + "ismobilejs": "^1.1.0" + } + }, + "@pixi/sprite": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/sprite/-/sprite-5.3.3.tgz", + "integrity": "sha512-qo7DG0oWS1uIBqfxw2jZPn34RCR6gQ+IjZRBpFxZPKPB1cL359scZmDBqBbQ4bd4rJ/6QXQfzUdGhXfQJtc9oQ==", + "dev": true, + "requires": { + "@pixi/constants": "5.3.3", + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/sprite-animated": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/sprite-animated/-/sprite-animated-5.3.3.tgz", + "integrity": "sha512-nG5j8veJ/cFXQTgzafPLkZqaHKbuaHcIj+ZYN1I2f31Y85/pfr2PQQLHbGr+3441wOYkEHht9nHhmZHWlOOZ0Q==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/sprite": "5.3.3", + "@pixi/ticker": "5.3.3" + } + }, + "@pixi/sprite-tiling": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/sprite-tiling/-/sprite-tiling-5.3.3.tgz", + "integrity": "sha512-+Xk9AUh82rpArtrnZkw+9aJchrmHZ8QkpjsPRJcgPFHx3WEfABIkT6QEoYbRKiYH34OgO7ZOUXy9hcGPHnxjvw==", + "dev": true, + "requires": { + "@pixi/constants": "5.3.3", + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/sprite": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/spritesheet": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/spritesheet/-/spritesheet-5.3.3.tgz", + "integrity": "sha512-pTkOCTL8jsmyAguCgcbz03UPYu+3buRkgua1g/vGyeoZBN2eJ04iSXdB0pfPrsPisxkvThGHyU23UqEDYVtXRQ==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/loaders": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/text": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/text/-/text-5.3.3.tgz", + "integrity": "sha512-juinZC2yFXnzucWWxSdty9nfIIOAq2WA8DD2k40YL+7Y5L52/ggkgnokeQ2lrTb1BvTfx6YVNlvAsKonUek0Og==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/sprite": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/text-bitmap": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/text-bitmap/-/text-bitmap-5.3.3.tgz", + "integrity": "sha512-QRRdEAFBwmRctp8PCPii5WUPM57T1I3r/EwyTvFCCDubOYOZu4aX/iFpCKZMl5GIphDFaGp8mNvbl+BwjUmBCA==", + "dev": true, + "requires": { + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/loaders": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/mesh": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/text": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, + "@pixi/ticker": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/ticker/-/ticker-5.3.3.tgz", + "integrity": "sha512-p5F/dwJGwfZWUg5cCPqOnEx5iYGW+huQlZZtrTKKd1KoVehFsrzHeRBOEp4d584jsOmBf7fjJaUTyzsFn0YtOQ==", + "dev": true, + "requires": { + "@pixi/settings": "5.3.3" + } + }, + "@pixi/utils": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/@pixi/utils/-/utils-5.3.3.tgz", + "integrity": "sha512-GDP2h1Mph9Uei4zmJjzDK6GZ5S9O2A09VySVfWyKgWwP3SQ/Ss0bGYm4sE6+u1NMSz1WCrLgu66H82XuXs2Cbg==", + "dev": true, + "requires": { + "@pixi/constants": "5.3.3", + "@pixi/settings": "5.3.3", + "earcut": "^2.1.5", + "eventemitter3": "^3.1.0", + "url": "^0.11.0" + }, + "dependencies": { + "eventemitter3": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz", + "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==", + "dev": true + } + } + }, "@soda/friendly-errors-webpack-plugin": { "version": "1.7.1", "resolved": "https://registry.npmjs.org/@soda/friendly-errors-webpack-plugin/-/friendly-errors-webpack-plugin-1.7.1.tgz", @@ -5123,6 +5508,12 @@ "stream-shift": "^1.0.0" } }, + "earcut": { + "version": "2.2.2", + "resolved": "https://registry.npmjs.org/earcut/-/earcut-2.2.2.tgz", + "integrity": "sha512-eZoZPPJcUHnfRZ0PjLvx2qBordSiO8ofC3vt+qACLM95u+4DovnbYNpQtJh0DNsWj8RnxrQytD4WA8gj5cRIaQ==", + "dev": true + }, "easy-stack": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/easy-stack/-/easy-stack-1.0.1.tgz", @@ -5310,6 +5701,12 @@ "is-symbol": "^1.0.2" } }, + "es6-promise-polyfill": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/es6-promise-polyfill/-/es6-promise-polyfill-1.2.0.tgz", + "integrity": "sha1-84kl8jyz4+jObNqP93T867sJDN4=", + "dev": true + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -7623,6 +8020,12 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "ismobilejs": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/ismobilejs/-/ismobilejs-1.1.1.tgz", + "integrity": "sha512-VaFW53yt8QO61k2WJui0dHf4SlL8lxBofUuUmwBo0ljPk0Drz2TiuDW4jo3wDcv41qy/SxrJ+VAzJ/qYqsmzRw==", + "dev": true + }, "isobject": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/isobject/-/isobject-3.0.1.tgz", @@ -7826,6 +8229,12 @@ "integrity": "sha512-/xwPEBidtg69Q3HlqPdU3DnrXQOvQU/CCHA1tcDQVzOwm91YMYaILjNp7L4Eaw5Z4sOYdbBz6koWyibppd8Zqw==", "dev": true }, + "leaflet-pixi-overlay": { + "version": "1.8.1", + "resolved": "https://registry.npmjs.org/leaflet-pixi-overlay/-/leaflet-pixi-overlay-1.8.1.tgz", + "integrity": "sha512-YEAvD5vGuhCvVnlcgMxQyh5wIKDtfRoDXUp/iLd3HNY+mOLhVKMqogQXmyBbzjn4eYkzAOVRF1DBPJaCjYrb8w==", + "dev": true + }, "levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", @@ -8235,6 +8644,12 @@ } } }, + "mini-signals": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/mini-signals/-/mini-signals-1.2.0.tgz", + "integrity": "sha1-RbCAE8X65RokqhqTXNMXye1yHXQ=", + "dev": true + }, "minimalistic-assert": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", @@ -8965,6 +9380,12 @@ "lines-and-columns": "^1.1.6" } }, + "parse-uri": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/parse-uri/-/parse-uri-1.0.3.tgz", + "integrity": "sha512-upMnGxNcm+45So85HoguwZTVZI9u11i36DdxJfGF2HYWS2eh3TIx7+/tTi7qrEq15qzGkVhsKjesau+kCk48pA==", + "dev": true + }, "parse5": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/parse5/-/parse5-5.1.1.tgz", @@ -9103,6 +9524,48 @@ "pinkie": "^2.0.0" } }, + "pixi.js": { + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/pixi.js/-/pixi.js-5.3.3.tgz", + "integrity": "sha512-uFQOXXyPMAVVayDebSFBS1AFfPT6QYNuz9Vu11yI2/k1DAef/rbYoJpSMM6SeB6dezDJPtIAaXXNxdaYzbe+kg==", + "dev": true, + "requires": { + "@pixi/accessibility": "5.3.3", + "@pixi/app": "5.3.3", + "@pixi/constants": "5.3.3", + "@pixi/core": "5.3.3", + "@pixi/display": "5.3.3", + "@pixi/extract": "5.3.3", + "@pixi/filter-alpha": "5.3.3", + "@pixi/filter-blur": "5.3.3", + "@pixi/filter-color-matrix": "5.3.3", + "@pixi/filter-displacement": "5.3.3", + "@pixi/filter-fxaa": "5.3.3", + "@pixi/filter-noise": "5.3.3", + "@pixi/graphics": "5.3.3", + "@pixi/interaction": "5.3.3", + "@pixi/loaders": "5.3.3", + "@pixi/math": "5.3.3", + "@pixi/mesh": "5.3.3", + "@pixi/mesh-extras": "5.3.3", + "@pixi/mixin-cache-as-bitmap": "5.3.3", + "@pixi/mixin-get-child-by-name": "5.3.3", + "@pixi/mixin-get-global-position": "5.3.3", + "@pixi/particles": "5.3.3", + "@pixi/polyfill": "5.3.3", + "@pixi/prepare": "5.3.3", + "@pixi/runner": "5.3.3", + "@pixi/settings": "5.3.3", + "@pixi/sprite": "5.3.3", + "@pixi/sprite-animated": "5.3.3", + "@pixi/sprite-tiling": "5.3.3", + "@pixi/spritesheet": "5.3.3", + "@pixi/text": "5.3.3", + "@pixi/text-bitmap": "5.3.3", + "@pixi/ticker": "5.3.3", + "@pixi/utils": "5.3.3" + } + }, "pkg-dir": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-3.0.0.tgz", @@ -10258,6 +10721,16 @@ "integrity": "sha1-LGN/53yJOv0qZj/iGqkIAGjiBSo=", "dev": true }, + "resource-loader": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/resource-loader/-/resource-loader-3.0.1.tgz", + "integrity": "sha512-fBuCRbEHdLCI1eglzQhUv9Rrdcmqkydr1r6uHE2cYHvRBrcLXeSmbE/qI/urFt8rPr/IGxir3BUwM5kUK8XoyA==", + "dev": true, + "requires": { + "mini-signals": "^1.2.0", + "parse-uri": "^1.0.0" + } + }, "restore-cursor": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/restore-cursor/-/restore-cursor-2.0.0.tgz", diff --git a/package.json b/package.json index 4edb8e2..40d52dc 100644 --- a/package.json +++ b/package.json @@ -3,8 +3,8 @@ "version": "0.1.0", "private": true, "scripts": { - "serve": "vue-cli-service serve", - "build": "vue-cli-service build", + "serve": "vue-cli-service serve ", + "build": "vue-cli-service build --modern --report", "lint": "vue-cli-service lint" }, "dependencies": { @@ -27,7 +27,9 @@ "eslint": "^7.5.0", "eslint-plugin-vue": "^7.0.0-0", "leaflet": "^1.7.1", + "leaflet-pixi-overlay": "^1.8.1", "normalize-scss": "^7.0.1", + "pixi.js": "^5.3.3", "sass": "^1.29.0", "sass-loader": "^10.1.0", "typescript": "~3.9.3", diff --git a/public/images/armor.png b/public/images/armor.png new file mode 100644 index 0000000000000000000000000000000000000000..0044af123450dd6af3f96d39bcd5e603becd5a0a GIT binary patch literal 213 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1G<(@8%Ar-e6G8v}*XK-+E-r?gDa1$tQ5)c@A)6&Xz2SZv~ z&i@CG-u*8qDF6TX>Bs+xNg4kc7#W%R*KgSU|J(Qf|JSYG^`DuUxu5C(|9>w&fBwYq z?)?{rFJC@0{Qdj)1tZ8T4NdJe{QLp}A3uKhaO3*b5GEc5wg`*PS;tmt0Hql`UHx3v IIVCg!0Gu~fRR910 literal 0 HcmV?d00001 diff --git a/public/images/armor_depleted.png b/public/images/armor_depleted.png new file mode 100644 index 0000000000000000000000000000000000000000..c9bdc24eb8746fba845fe8737815131313725f12 GIT binary patch literal 192 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1GNuDl_Ar-fh1Ox>w6%`c=CpR`8WN~T!p|fVqnnhMtR$fd@ zOv_VKQzh5M>@4d4d3w73{MOdizf-49m5ke4b+q8~v$IKR3(P|O{QTI`($d%jcpn)% moUK~0V8I7QpfL=LYz)@kk_Q}e|?^Z1Bi(X-m5h*Ff%wbFfe9)Uhxqmvd$@?2>=WeE*}5@ literal 0 HcmV?d00001 diff --git a/public/images/block_nether_flat.png b/public/images/block_nether_flat.png new file mode 100644 index 0000000000000000000000000000000000000000..298cdf95ddbc4910da14c30c050c9e375e911fe5 GIT binary patch literal 2620 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 literal 0 HcmV?d00001 diff --git a/public/images/block_other.png b/public/images/block_other.png new file mode 100644 index 0000000000000000000000000000000000000000..6756844578aaf4efe2ef2ab3edc3fa2780342b4f GIT binary patch literal 330 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxUdRE{-7;bCUl2|8LK% zCm$~S4#xBIY?+gj4!pg+{b5c6Lz3h47D**l)z)xj9tIYH zHpUw~hn5MlF)(#-F6Wu$xP!rgLC~Z@fPq7yix)=hVAPM_rxJDf>}+$85r<|?IePSH zph?Z&U!ty$JB8IBcuFcXd`MH9;rRUA+`|_*m>C#bt}>J~e@IPlU|?oA&X)Mre#e9~ Qpa&T|UHx3vIVCg!032Iu%>V!Z literal 0 HcmV?d00001 diff --git a/public/images/block_skylands.png b/public/images/block_skylands.png new file mode 100644 index 0000000000000000000000000000000000000000..641155f3b7fb0ab38048af147ddb327a2bd7691a GIT binary patch literal 993 zcmV<710MW|P)4Tx0C)j~RL^S@K@|QrZmG~B2wH0nvUrdpNm;9CMbtL^5n^i$+aIn^?(HA4aZWV5ov6ELTdbo0FI&wK{O>*+w4vx20?>!`FrQsdJlnHR>OPy zcd~b_n$otK2Za4V;76L-DzNVtaSB-y0*E}{p()372;bw_^6ZZ}PI-92wGS&j#91PI zKs7DSe@(bk%_Y-7gGe}(^>I=@oY#w#*Bu9GZf3^F5WP>3rn}7Ut74&?PWBFvy`A)a zPP5)V!Xd&78LdA?xQ(9mjMYElVd13a#D+Z_7&Y|xU=_C-srWU*6kiZcC!$nw*)9$7 zn6CX+@=AhmkT}X@VSsa5NKe;HZuq)~1$`#h6R+ZTR#D-3j}vF!)ZOnz+5)dI4jl{{ z44Mr{P!L4~VVJN`K!!XTF*LGrKO?IK8z<8w`3e3jI8lUGNUta*C8 zn(P`s>{pjD=7Kek#B;Fw@hxAK%$F&Q6vg9J^Xf~4by_hu-=A!MJ3Znq&n~srbFGPs zH&&aMXZ>nO`|hf|ljc?VPhR!${AbO?W8x_>CU%PFA&Hm8F7cAsOREdwU~R_;ot1_u z(ruCYB-LPGn!NQdT|ZlRy+(fw^-+`=%+gee_kY4FWHg<*4sZI8+sFJD270UUORdLHO0nA4V) z%{fwsET5CQ>B?eK%uw4yQc~9?*JVo2}ze(;aRcp*ceL#HUJSllrgm5wQKR zQu+C;QrUh^8rFfA`ftFz{YAidi-`aL0Io?yK~y*q&6D93gCGoqlfG_bXR2$kMwz2) zI1`e{NwJ6c)BF%f?!K27K~qZ7i+K?*ekReLMi~IcqltkUK+kvva==w113fGmU&qq; z43Br9nuk?p`CcxVzgZJR^8ED=06POkGa|wO0G)vvBMJrJGeC~5abnq;8v?47@9qTX z-geS@sJW+Y?SI zaB^>EX>4U6ba`-PAZ2)IW&i+q+O1b@k}D?+{Ld+J1P}<1UBQJzo+>NA97+%;3P4}Xz>X-WGd*0J)hG$8}?m)*xUJJH;)GdlSk{% z&p32^ft^2INI~uOv)!boencI)hvWV6Yv$G0aiWeq9lO)tChL18JS|7R)YPpyfBhX2 zz~~Hro(v7|yv04PS^W7HeAB)M401m2zQ^5pevh4bbYH-G^j@&DMeY7G6vjp!5MMA- zyW7idl*;&rQfebTnS%BKkX~y z5-hQSu?{vXu4Y_z1hl6MuDjx<8@p|;A;ds~5?Z20e%r(+%GCU#Ags$xN+6>fFW{+x zH4igAVsU%kyyt7kfNs0PNOExI@Wz+Ryfgln>)~k5C0*yNJg`Dsv^0${7IW*4Q6R)k z#gsR``!Y|uu}{Q$FvuHb!2*k|$39YZoH2mM`+EBcaqyETxJ@#a}^xVsE15z4c@Q5P~ z8F`cmtxZ3}X zfSeDe5PVX>kWD9$hLg;}2eMMS;N$f0s_HmEkoC?`uYQy08zZA6Mb#A|9|!!D8{k(D zA)m+%PHG`3VUX)tdvIAHCLB-I6Zw3gDea*~q*Sc?xk0*6(^#wwl}MiU1jrNB%W6X2 z){Um9T4^(FpN1+y!8qH#iP{tA>-fD{fO44bAlPHZYF>D)ErcHr&(*KCS5^I34aL9JjbY6 zjs2yUGx0zjotiAgrEw`&WN z8(Wj6KGI@n@tgV2Laf}ibc5ui82zw3Vn)_P3^)jE-_X(2{N-VnzBSZ#6Bw+?P*lr0 ztiDIlJi*y+^>%qwucc>1;dV=g_Ojq8tNW0$jXyQ11x>VWbAGKD-)_y9Qd8^AU+jgV zi%zz6-S=?}%UZ47-6Omo#-$^Pfo;NmQ&jI-^ImH02w@c>dn=@GVmT8lq85#dN&)bKCZ6pf2GZQ-61Cajj4zV4zCmE z#teG@1t)z3^;N2lVgLXD24YJ`L;(K){{a7>y{D4^000SaNLh0L04^f{04^f|c%?sf z00007bV*G`2jm9?5d#m}H9uwm00Fs4L_t(I%axL`Zqra0M$fgcV>@=-IE9u3BoJx~ zVuKgq1&Y|1nRoyM5-cDw0W&iaGcUoIsz^jAY9Mi4$5HIq&b66Ji)2GN>(T$|m%dLt zd+~s8qo0*TU|21R)gHcQ;+6*)q^Hfpgho5yixLfKnCfYKiUm7($ZAi&}5A3}iSB z5mLkUd@S2USvtp)Ihtjn)+M9iA-(PHAO4cN&neQdO{~raX`FKWeTKK?p~{>}nP@_z zz3E|fHW>8Ij?SZHNk&=1kI4}^1-Z&t=K@`~`1<95cotKZG$Iv>APli=7pL1Ho{00000NkvXXu0mjfU>4?Z literal 0 HcmV?d00001 diff --git a/public/images/block_the_end_surface.png b/public/images/block_the_end_surface.png new file mode 100644 index 0000000000000000000000000000000000000000..69bcc329dcd335f99ea67f7d590ff54ec24f128a GIT binary patch literal 1062 zcmV+>1ljwEP)4Tx0C)j~RNrgUP!#^!Wu36$i#lf!2|j3%Ze&w*L!7p2SGvtw>Nd9_NSmf@ zT$;ut?S8Na*^6&F#dq-sKKTa>*@JI;k`2ZbVfd_wB24xov!0tYO(#d#()tZ$I5%3%!zLYh@BH>w}XODA7?mkV}ap}jU$$3 zG&Mk)3Bm`(LOM&hKscCb;PVaG&Vdx+MpZJHTQ(R_;DA31$+jOGBoLXk_De?ey1m!ik&_4G zH9n^))_*|$z4!HUisgBd@awc5jn(v9k~&t~+vLrrBg4dZQ9lDnLV}JQWGLW~LJVP= zW5lZXOcog;N~F?hbX0k=IMzETla}oqM|jC!4!B+x^;@#I_Tc-T-6hwKycLDTx1-om z?X`jFy0R0R8-I0SrK4`)H@W4T8*Qr#2vPou<*`U!Wy(*2QP*`g=8#jD{B;Y@GL-Hm zb`n?&x~%YC_$q7)PlXr4m%r4=&fcvN%Ybn#KC7Nn&Bp8{(oE9pWVpYI^+LuN`H(R~ zTAjWmO`M83^4d@fCkA(d>*nHIFV_d2yUbnT`nd?LE^;G|!WZ>Ld?E0@Grm4ww{M7H zr`x{MWb30bTI;*hk-DO>dX$gbC-yy#suLNqvA(f>RtPJ!qGM`Gvvf}Y10`)vm-7Xa z?-7Ixe2A_siI1ydSCCID3U8SVUY86>uSnT0use_K1GZDvUFKY)t}F* z)!pahe+zh{{06Bb3f97*Uorpy0K-W{K~y*qWBmXBKLajs^U)lzq`F)RBQ75p(N#{%k#mRM^07+ gNx}@mrV*wF0O;&eZ!%I$umAu607*qoM6N<$f{kqJEC2ui literal 0 HcmV?d00001 diff --git a/public/images/block_world_biome.png b/public/images/block_world_biome.png new file mode 100644 index 0000000000000000000000000000000000000000..a72932a5da2832ec80ed4ecadb8fbd3d3d58fafa GIT binary patch literal 1092 zcmV-K1iSl*P)4Tx0C)j~RL^S@K@|QrZmG~B2wH0nvUrdpNm;9CMbtL^5n^i$+aIn^?(HA4aZWV5ov6ELTdbo0FI&wK{O>*+w4vx20?>!`FrQsdJlnHR>OPy zcd~b_n$otK2Za4V;76L-DzNVtaSB-y0*E}{p()372;bw_^6ZZ}PI-92wGS&j#91PI zKs7DSe@(bk%_Y-7gGe}(^>I=@oY#w#*Bu9GZf3^F5WP>3rn}7Ut74&?PWBFvy`A)a zPP5)V!Xd&78LdA?xQ(9mjMYElVd13a#D+Z_7&Y|xU=_C-srWU*6kiZcC!$nw*)9$7 zn6CX+@=AhmkT}X@VSsa5NKe;HZuq)~1$`#h6R+ZTR#D-3j}vF!)ZOnz+5)dI4jl{{ z44Mr{P!L4~VVJN`K!!XTF*LGrKO?IK8z<8w`3e3jI8lUGNUta*C8 zn(P`s>{pjD=7Kek#B;Fw@hxAK%$F&Q6vg9J^Xf~4by_hu-=A!MJ3Znq&n~srbFGPs zH&&aMXZ>nO`|hf|ljc?VPhR!${AbO?W8x_>CU%PFA&Hm8F7cAsOREdwU~R_;ot1_u z(ruCYB-LPGn!NQdT|ZlRy+(fw^-+`=%+gee_kY4FWHg<*4sZI8+sFJD270UUORdLHO0nA4V) z%{fwsET5CQ>B?eK%uw4yQc~9?*JVo2}ze(;aRcp*ceL#HUJSllrgm5wQKR zQu+C;QrUh^8rFfA`ftFz{YAidi-`aL0TD?=K~y*qWBmXBKLakHR=N@_d8<2|5tkYk zY!YydJ>g(!$;XXggPyOb$1n)R0GLKT0bvw#pRcTk7{q{S5F^M7FpamnBN&WZccW-N zQ{#fd2XQ3B8^K~A7cqfYpC8{~0BJ@CXFOIjFq}z8V5kC^i$Gk27aAQP(g+arcW9JNFy!)4g*}0 zU~!m1I1~{!0Eb4H@E|fk;<^LORRrlY&uefs;FJjCNk%}DDnGvv!wsoCm@vZ)9&s?+ z@pLAbM$Qu`2EcT{40^8F02Y$G4xy1X!qj3l0451H2um)6sR000>S0j{QwW>@0000< KMNUMnLSTZwhV+mC literal 0 HcmV?d00001 diff --git a/public/images/block_world_cave.png b/public/images/block_world_cave.png new file mode 100644 index 0000000000000000000000000000000000000000..b6a547bad64014b18faab08cfe95d1d6e136e807 GIT binary patch literal 339 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxS$$E{-7;bCUl2|8LK% zx8V9tW}V8FcbWyKOEoYg9Z6&GSuC)hVaES=kJ;H`*ce#^x)@(bU1T(CW?TeVXp(PiYcMc;aQ5rv0y77OjSIXF@jRR!dT_NM z8&KzAh8(uU%N)!Mj7M66+0H7&Feo%|NI7sYum~JwgAsQcu3tFf$W!tAq3s-A0S67w zNVRmX8OmMLR@Bv3_AmPQ^N>|V5F3ZWnwgw;7l`}6OU!6tY+yK)#jwlKf7z1)!K{3``xI(_glj9A)#{JHVQG$Wh% eh~pdr2@ENWVV;LqpL!1rMFvk-KbLh*2~7a4w2Slr literal 0 HcmV?d00001 diff --git a/public/images/block_world_surface.png b/public/images/block_world_surface.png new file mode 100644 index 0000000000000000000000000000000000000000..603c6d5129596873c480174a9343c483964cfee9 GIT binary patch literal 342 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxTF?E{-7;bCUl2|8LK% zC$RGhtIkfFDt=`>QwGK(9cLM4xH&r78T|kI+n;9#4-TVcsCw;;YQVz#<^*Cu!1Lz}Uc`l)<3LAmAV(4I>Je?P~w{Y+3eQ-tG|4h?YrH z?#}i-C}UR_C&GK*{-4vuiY?!bY&4l!1g@QF$-A(*eZ8T{AqEGAL{o;l3+8TJUYM5N bz`)GlemEu6>xohs(6bDlu6{1-oD!M<Px#24YJ`L;(K){{a7>y{D4^000SaNLh0L01mML01mMMoQxmZ00007bV*G`2ign} z6cH;9(*BD800O;9L_t(Y$E}u2YZFlv$A32&lcr7DPOXMQI#G%Q7ZyK(g5SYiLB*91 zgi<%IL^s`73Zmksw1w>a5F+^kf(wbDk};^QwGZ1goutWi;W=SCO<&-J%N>%rzyG=C zb;oeRVXZa#Y>cr-y>Ctc4Nm|Yng@!RV}fHqJTwi^23o+bthSqoIVLy(B!M1TV*;+r z41gxkj*6H=zz%RqplP6AW?Do8U>#VMbsK0zMVvz{R`39D8aN9K7jmiRy187f-vGV| z=ePXau-0}&oX|<)VsQ${0D}U~0waZ7>KVY*%LAPg7jmh^ai z7!qqo1UwT4fB0PE(pXnE1^R-@5?gA(%p>G;3B9eJpD;)tFs!RZGKPoaeJ+Vim z8~E1b44&uV`@a4aSGcCB!UJGyz4lKdpU-y{30v&@eg{53JH?lu0ZTy40m=h~ ztebch7SW+rcLCoMun#N)n?OSgEhbU#1I|b-T*$hK7b;RJl>nHZor;1V0^flj0$vq( z0JKb~7iqzzr6xv!F+GX8C<0dKisb#46j&2z8DrWe(n+-+rNvz%z-ZP@Eb4npYmEm2 z{3Q)e!8_7TTaq-^7-PG&YduM?1b#@?q-?GM8^E6sxGDETJ9_b$11=klNm7J*B&9W- zcVyjX@kwvAxPY~ItN>d_fseeCLthUEUiSa{tK!(7D&g@L!*y8i>Yz~$00000NkvXX Hu0mjf>ttG= literal 0 HcmV?d00001 diff --git a/public/images/cave_off.png b/public/images/cave_off.png new file mode 100644 index 0000000000000000000000000000000000000000..500d9872ee1ce6c07a54ac46ad1fe00c1dcf863a GIT binary patch literal 1134 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 literal 0 HcmV?d00001 diff --git a/public/images/clock_night.png b/public/images/clock_night.png new file mode 100644 index 0000000000000000000000000000000000000000..0832f0cb7fc272a7004195827c0e5d754f17c231 GIT binary patch literal 360 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxV!#E{-7;bCUl2|8LK% z%An8?QQ*0!<|pUTl)^$gx13iDOdW!ESq*yU%;DI$I(+@N|9?0Wtt=wWxrXrzIB2jn zm@Lj_xj5gxejnEoxyBm`%{8@m%MM>*U=g^(P;z#9y8E;A%CIy5jcyk=@)-ed3T1@u0Hr>mdKI;Vst0McuDSO5S3 literal 0 HcmV?d00001 diff --git a/public/images/compass.png b/public/images/compass.png new file mode 100644 index 0000000000000000000000000000000000000000..2614dd30d6158e70edce299344712f152819eb3e GIT binary patch literal 5203 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 N002ovPDHLkV1h!?*8Kng literal 0 HcmV?d00001 diff --git a/public/images/compass_E.png b/public/images/compass_E.png new file mode 100644 index 0000000000000000000000000000000000000000..a12f9878340a212d8d56756c15d79e42ae9be827 GIT binary patch literal 4323 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 R;4uII002ovPDHLkV1m>t^bi05 literal 0 HcmV?d00001 diff --git a/public/images/compass_N.png b/public/images/compass_N.png new file mode 100644 index 0000000000000000000000000000000000000000..4794e3610185cfabc3599475cb077b7c75ad23b0 GIT binary patch literal 4187 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 literal 0 HcmV?d00001 diff --git a/public/images/compass_NE.png b/public/images/compass_NE.png new file mode 100644 index 0000000000000000000000000000000000000000..e4c6574eab8d4637cf4053f8c148256fe9ea8275 GIT binary patch literal 5165 zcmV+|6w>R7P)KLZ*U+IBfRsybQWXdwQbLP>6pAqfylh#{fb6;Z(vMMVS~$e@S=j*ftg6;Uhf59&ghTmgWD0l;*T zI709Y^p6lP1rIRMx#05C~cW=H_Aw*bJ-5DT&Z2n+x)QHX^p z00esgV8|mQcmRZ%02D^@S3L16t`O%c004NIvOKvYIYoh62rY33S640`D9%Y2D-rV&neh&#Q1i z007~1e$oCcFS8neI|hJl{-P!B1ZZ9hpmq0)X0i`JwE&>$+E?>%_LC6RbVIkUx0b+_+BaR3cnT7Zv!AJxW zizFb)h!jyGOOZ85F;a?DAXP{m@;!0_IfqH8(HlgRxt7s3}k3K`kFu>>-2Q$QMFfPW!La{h336o>X zu_CMttHv6zR;&ZNiS=X8v3CR#fknUxHUxJ0uoBa_M6WNWeqIg~6QE69c9o#eyhGvpiOA@W-aonk<7r1(?fC{oI5N*U!4 zfg=2N-7=cNnjjOr{yriy6mMFgG#l znCF=fnQv8CDz++o6_Lscl}eQ+l^ZHARH>?_s@|##Rr6KLRFA1%Q+=*RRWnoLsR`7U zt5vFIcfW3@?wFpwUVxrVZ>QdQz32KIeJ}k~{cZZE^+ya? z2D1z#2HOnI7(B%_ac?{wFUQ;QQA1tBKtrWrm0_3Rgps+?Jfqb{jYbcQX~taRB;#$y zZN{S}1|}gUOHJxc?wV3fxuz+mJ4`!F$IZ;mqRrNsHJd##*D~ju=bP7?-?v~|cv>vB zsJ6IeNwVZxrdjT`yl#bBIa#GxRa#xMMy;K#CDyyGyQdMSxlWT#tDe?p!?5wT$+oGt z8L;Kp2HUQ-ZMJ=3XJQv;x5ci*?vuTfeY$;({XGW_huIFR9a(?@3)XSs8O^N5RyOM=TTmp(3=8^+zpz2r)C z^>JO{deZfso3oq3?Wo(Y?l$ge?uXo;%ru`Vo>?<<(8I_>;8Eq#KMS9gFl*neeosSB zfoHYnBQIkwkyowPu(zdms`p{<7e4kra-ZWq<2*OsGTvEV%s0Td$hXT+!*8Bnh2KMe zBmZRodjHV?r+_5^X9J0WL4jKW`}lf%A-|44I@@LTvf1rHjG(ze6+w@Jt%Bvjts!X0 z?2xS?_ve_-kiKB_KiJlZ$9G`c^=E@oNG)mWWaNo-3TIW8)$Hg0Ub-~8?KhvJ>$ z3*&nim@mj(aCxE5!t{lw7O5^0EIO7zOo&c6l<+|iDySBWCGrz@C5{St!X3hAA}`T4 z(TLbXTq+(;@<=L8dXnssyft|w#WSTW<++3>sgS%(4NTpeI-VAqb|7ssJvzNHgOZVu zaYCvgO_R1~>SyL=cFU|~g|hy|Zi}}s9+d~lYqOB71z9Z$wnC=pR9Yz4DhIM>Wmjgu z&56o6maCpC&F##y%G;1PobR9i?GnNg;gYtchD%p19a!eQtZF&3JaKv33gZ<8D~47E ztUS1iwkmDaPpj=$m#%)jCVEY4fnLGNg2A-`YwHVD3gv};>)hAvT~AmqS>Lr``i7kw zJ{5_It`yrBmlc25DBO7E8;5VoznR>Ww5hAaxn$2~(q`%A-YuS64wkBy=9dm`4cXeX z4c}I@?e+FW+b@^RDBHV(wnMq2zdX3SWv9u`%{xC-q*U}&`cyXV(%rRT*Z6MH?i+i& z_B8C(+grT%{XWUQ+f@NoP1R=AW&26{v-dx)iK^-Nmiuj8txj!m?Z*Ss1N{dh4z}01 z)YTo*JycSU)+_5r4#yw9{+;i4Ee$peRgIj+;v;ZGdF1K$3E%e~4LaI(jC-u%2h$&R z9cLXcYC@Xwnns&bn)_Q~Te?roKGD|d-g^8;+aC{{G(1^(O7m37Y1-+6)01cN&y1aw zoqc{T`P^XJqPBbIW6s}d4{z_f5Om?vMgNQEJG?v2T=KYd^0M3I6IZxbny)%vZR&LD zJpPl@Psh8QyPB@KTx+@RdcC!KX7}kEo;S|j^u2lU7XQ}Oo;f|;z4Ll+_r>@1-xl3| zawq-H%e&ckC+@AhPrP6BKT#_XdT7&;F71j}Joy zkC~6lh7E@6o;W@^IpRNZ{ptLtL(gQ-CY~4mqW;US7Zxvm_|@yz&e53Bp_lTPlfP|z zrTyx_>lv@x#=^!PzR7qqF<$gm`|ZJZ+;<)Cqu&ot2z=0000WV@Og>004R=004l4008;_004mL004C`008P>0026e000+nl3&F} z000SCNkl%lbPYUOxbYvdIi(PImYzigUmsaU2p7 zx(GOzP39&+JXV19v}hu1c4#J+3{R!J_&+MFE9?Ir#X=efu+D+fWg7Riu_CqFbvF{ z#zd|MUI3n+s?#0@o&zob4^J}^X8?Z#_5f=Dr5X{?QSnp`0)G{av^$mv0qO^S0-UdA zIQetnG;qIYWOG*kAo7P|Sm&y!ps)?8dXWim z5qK=md0AvrUJ~I~GlqnRfIk#4o@<3|pq^v`yeN)*SS?(ryPJr}KJlFjjhttNO`s8E zBBb+Tf%oPLS7V3JtBu4a;O|Ak;`e|JQ)!?YWJb}G%1RL@_!puh^BX|oOyCXR*F_$v zQlbxaBNO0V15dS2DExss61iLq>#vu%ZiA3ant{xWz8N;)(;Wa_1-ffUqFqSh>jv6+ znJP^nR(q;<9dOCOb@vGu>b@EfIajEZO$H;nb=7LP+GIwA|CumwZMTrn{Z)+b9~8s7 zugqLSQ<4dA7I>nJ>mL=mbkP)j$AiFM%J6Nsh)9QKCKF&cdJeRlu@| z45QEc*BsV5ckBtF|N83G-X&mNZy->Y#_C;$otf^4)T^!DKp;}D&NZT@{ZciP3&lo3 zZy<2Co*iq{0)b&_TQ`3KQLR=_SZN$K0AHny0tU;(1n$vM9I8snY9;P+G4Anz$uueQ z>J0=^(}~Q?RKYtK$ndW2NyZ>L%G$l^IvA6u*O}(f!$Hcp{Pa z8`+UYOXh==m|*i?y^6`;l0=89K9g`bZk>)~&PAVGD+*^UX1p`0lnp(5ZkbMG+R>*N zAC4d<_@DFWy0Yo`{qtX`MxSi{nLTWrIb<28I>y#UQNT6NDL?K_U?8j zvl~5wZYhV%37f6#hAa~kPR(R`yPe5=0loIexR9AlT$w$)QvKPhO45OLB=d19T%n`DkCR-wL z7??$kQ-Af4Oj)==<<@7O2Nq|D1n3HdUK{s%Z&h8cPs-GvuDH|rRyz#y1C+n+@_FFS z-az1kNlzvi9C0d2&v;kYD>>PsQqj?u6u0{~txV>C2pJ8lN8pj(KwxceAW*dM)IB=7 zYD|^~QdX85POInf4`RL18jN+1zM#IsIHyr-`xYzr|gwtuLlFg)PCTlYJ zDm7ZTP%APs;iAzmeFXS21{QnfqAL`7->#}R#XO!fhAeI}>4`g?Z?+Pdd(f-XN+E^5 z!1X&*w6rNH8og`G=i8b#n@hS+4pr?_-0m%{KxRLaSA6BeW#AiBv9!+{4*zn@=es9q zx0fvRcPh$e#qD0)%w+bU7oC>EX<#vx*B}-4!-B!L6t{bpB1!))sz1|dvo)DKMirOT zRFh1Lh`UQI?B5IALygM%gdUB!VzJwzvV2f0;y&3D37_h6z0r(hZl(BYv?#Cwy%Mc1 zGl!l?j=&(ao4I*$ZB5>0xqOErq^N>DyFs z)mP0d{phIT8Xv!7%;(#ung36NIhC@S)9F)GFkM_Jnftl1EP6ylGuvn}c*EhLQJ?Rd z6E@rRdUsnqK2wpTL-io@d*aC8ZQ=HuphY1EgGa}`-dj{jx}J1)Da!qd+r7G$WX=~- znZfUB9nI8{&=F@W))STG!*RBO(UhmA9*L%hli>--yJS$6m#>iCfO!Pf~9;3u&ob3kx$VdXXYt--GCPkxv zOxo=ySMJ}x5`A0Tj}ZH_i~mbXrWt+5z7^Ar$QzveP-ydbS66?^YW=aW6`6$gJhC<& z$d$~ES+94YZ&bVy7-O14zIN?YsJr_tn)WqJrgP-k(>|BXW!$)JO^^<&(Hy!MblR{^5cJt-|dlG2D-5PULPa{-I#I^J5`}aV%@}?i5_@0zlKW?BiI@ z|JRT0+qd6R=)LK7?Af!&obPL8&c@uwVYAufj!^0t8XC&`0|2JZFI80!onNY| bBK|i3_WP>6KgXn>00000NkvXXu0mjf-D=sD literal 0 HcmV?d00001 diff --git a/public/images/compass_NW.png b/public/images/compass_NW.png new file mode 100644 index 0000000000000000000000000000000000000000..b657abf895972500d531cd74aae748fe74ac830a GIT binary patch literal 5133 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 DYI)0F literal 0 HcmV?d00001 diff --git a/public/images/compass_alt.png b/public/images/compass_alt.png new file mode 100644 index 0000000000000000000000000000000000000000..0e7cc4b8e3cc2db5076aaa844e60e08cf25b718a GIT binary patch literal 802 zcmeAS@N?(olHy`uVBq!ia0vp^Mj*_=1|;R|J2nC-mUKs7M+SzCeKo%WCj zoWL5q%%{;>PhGuTaDG$+LlQ$}iov6klhyw}fApv+SwMb%6;NmTx949WGFl2aCUQ%M9 z^qg7Xz%$Mie`b~o4ab2J3dau^bolq!%}`Hx#RycK%)-)9YQwXj8f^aoTc@DI6Bu`^ zJ1{6Tuw7A(@LKr)hfVWouro`(a}@DR2;zM7nT3OaMIhGJ$&0sdwaMY7ataL@?*x;Q z8TS5Tu-vt1(Ia~!`T4d$mnxj)&p9i&K>cIc9FD{WhDB990>aD-E-YWNw3`=bLa42i zkKBQ)eTzhZoTGAzOGTP0nIdHuUh0SdhDymv<_$FsYdQTg?==85EA3wE>J{j<`bi59 z15?M6C9Zy{Zv5b|5_~II^y|utc?&%6H83?W9C|HSG+S7~!5=L7@C%3PVyQ1)ekXuF z3b1kV?Yoqqrvb4eDz6r^}A z+y)04vz>#xE>Qd7W6BJSE~WiEE}NJbfk6@3bKx>r!C|#bMsQG^pCuy;3KX|MhppB| z3(6ttsxC=!fIY%H+lC2dOlUt(2t<08JKH6&F+B?ct%?|n78femGBN>e3jEk@H{Wq% fwgv+OBO8O$t5h5QH(N!4iJZaH)z4*}Q$iB}8^RWj literal 0 HcmV?d00001 diff --git a/public/images/compass_flat.png b/public/images/compass_flat.png new file mode 100644 index 0000000000000000000000000000000000000000..3a81604545d0428a448501a46c720e30ea66db32 GIT binary patch literal 1844 zcmV-42g~@0P)ccu^tuQPt+mjDt$R7IO z3dK%a+Icl7whJWID#@fKF^Q>vc8)u@yQcn0Y-`Rr{=RU?y?BkDeDeLB?>RR);^N|> zz`8D8yjWFHQSoCa6#BEVvGMHX%a=d1+wEsP9#4(Hfg~O}k(ZZOe)Q0)CXt6g#8q%eBoh5suU>84 zHaR(79F4}VUAy+REX(BzK%z&LK3_oa`MyDi-=gNUt7;pJN%YE&nvt_UaS6!+kmy!9 zYSRFTUKvm;L9LKCNIXSw8ySzx1QtM|6q3H|0VGPtqZg?hAkmxbG&unh{g9yCfs+8z z>wu&sJ%B`!aO_r6=1fk2L~s1ovXaq=fJITPqxWoe_`zt^MNSh$T zI^~Xwafx+EHUbKq#Biy@+5?d24GjQ^f%vVdfHZ(av6Gk&4_m=W%!dbX5{tE`6wjh9B>f$LXyC$xdFJO3e|d?Ikd5nLH7O zl&%&kIy-s&TXBYoj;#A zapL%uD_1hvlT3TDS98Q`(_C)u5-53QX5DMiSR^bMUO6{lC%;Qts=PXA`Vtz<^pBL4 zmGz>B7kl+}b#>j99zTAZEGa3ey>#i)J55bZlGSR>ZEkLUtEQ%A-|+BobcMNza}7sq zszQn)l=^&vxUjG$%zuiC-t@n+XO%Z$l(awb_KCu`$6gH}}%d zXJr|uIvg6ZIpN;B${Sb@OvX}MQJWg&XlG6%fN>}21y3~l>Gyl@WRTM8Qpf_415>d%{-qK`NRn@53Y&KrM ze%*>7Sx%ljIf~##Z{NP1?tPfODbO^C+-=6R+B9s1ap&PiqPwCfzwO_@|3mG(udna! zn_h7!B;cyan5Kbbb-~lCbAw985q#`tdBq_}THh*(NOPW+j>?7Xe_iE?E=s3H>n*q9 z;9aHTTFkH1InVi;!^^a8T=40Zgs?l3uq?E`qGXU=X)d9Kmrqe^$}|X~c96iBVyVWm zw%?tfw5bh1!f<+790*yxpER;kd|gK>qkUx-QhWo63Ls_4O_>H2fV9ba1d!O6SZ0zK zeE^AWNM45fxn;_o3Xrm+C!~yllQ>Cu0EvNd3CbPiPMRjA5g<_<9#qErxFyP+7a(Q7 z+|7fN=oY>~=LaXTA07Y_<&f~_D<%LE-QmH@LI1dSk_wRMpDTfrDA8IAtpJH4sZj3N zEP2>rQviwX@Zja7ZvY7(Wp}OwkSH9J=6MVaJV9D%p%oxeJ}&u%^gpBmkg_ya@^aKK zt{^?)vDEQg?!ZalB!IM0Y0E(K0;OA`ZB&xLmoOZKr)0+?qz&zi1&}u1NoIgVw{WVH z0TTUAG6Ez@r6|hp$BrFyPfkwG6%-UmH*VbcArgt4=Am!PHh^N^o<)QVYRfh)D;&OHy$~1 z=bfGxqAL+SSe)X_FVErh6+)<@vmm&XQWZJpR_)bkIq)&YDzfTWT+3T^gon2m z!RaXPf`0y)&pEH(UiU8Jz5a!3rE6Gx%k<{G{d>NxT)V%V;g(;o;eW&VBOA!YBLG}_)Usv{9+`^o)CbL=ouLlZAmbgZgIOpf) zrskC}I2WZRmZYXAlxLP?D7bt2281{Ai31f~@pN$v(Kw%+uz%*A7I^Fo2cjokT?AZ<79+~MJ8-!m)G?9pdsVM8;e)1BSjkFWWha4Rh>z2o=R zC;b0<#p8eFr8X*72+h=syNeS3j3^ HP6OGP- literal 0 HcmV?d00001 diff --git a/public/images/follow_on.png b/public/images/follow_on.png new file mode 100644 index 0000000000000000000000000000000000000000..2fb7be7a830cb0db93246700a0f6551e47417d70 GIT binary patch literal 376 zcmeAS@N?(olHy`uVBq!ia0vp^{2A!YBLG}_)Usv{9+`^o4a@mtocL9YYOI#yLobz*Y zQ}ap~oQqNuOHxx5$}>wc6x=<11Hv2m#DR(~d%8G=Xq-<@SioSAB>ZHv`R{(cZ!aHc zx;4%E`R44u)$3-S`tbOibDRD8`-TQUu%Uy?&}Q9>qz(V-<~znUO-?+v%)Q6uwoc@n zLkECh!j`Sm6aRfv=P{S7O;x#-KQC?3|E>M&f3Dw`d96OBd1l8&4gG^BKbYIhYXkzv zZHsx{8L2r*$?~*H@WoV|E_i$~{Mhro#Sb1fgd3l6tEo#-I_8#QDHf=BXBOwp6AP94 zZ?ByD^3{LSCp-Q>{=`?5VZ7*e$Jv!Ei<>9(S~#}Nm#LJ}WMsG?o9OXzkEb8ddkmhg KelF{r5}E)!GMOO& literal 0 HcmV?d00001 diff --git a/public/images/heart.png b/public/images/heart.png new file mode 100644 index 0000000000000000000000000000000000000000..fba847742a3065726c8766713370cc26bacc7761 GIT binary patch literal 198 zcmeAS@N?(olHy`uVBq!ia0vp^tRT$61|)m))t&+=$r9IylHmNblJdl&R0hYC{G?O` z&)mfH)S%SFl*+=BsWw1G8J;eVAr-fHPT2A}IEXMjOf2VCx|l7><+s_&ed3GQW1%Wu z(pC?d>ZBs$OlRu~w|6Yq+E&{0(QRT7lV`u_wxCmw-%Vcf_`KZp e$xCXOco-HxmYp+m<$gDyB@CXfelF{r5}E)@A41ar literal 0 HcmV?d00001 diff --git a/public/images/home.png b/public/images/home.png new file mode 100644 index 0000000000000000000000000000000000000000..5b15f03d91972545039ccf87464053d071b81127 GIT binary patch literal 428 zcmV;d0aN~oP)P000>X1^@s6#OZ}&0004VNkl6WQ8AUp{_yYF`;#)}FoSa&UZ%}XQpfBJo4t5h9L?K8t z0Ym&lpq&Z{-XV#mgWv7GpW{CK?zjg;QmfTyr_=dF%4{|p%w{tT27~r+IBfm(5^wW% zyLDXGT}L}vmY)iR0^u@C{qx=y;??iY^%0Ap-l{Cos@-P2;cW{I-O$uO&>m=&!b+iqf{!zdhR5us^$nGKI2U#p66jQnZUN~>?DW70k&&rsi0UaW+#y(35udX zmgVduqTG)p_T%^aJ(kO5>cfH{xGV{R;3|nIXOpZ}tE&&)?RH{G7=~vX(&TtN#@4?< W{-EnTc7+oF0000A!YBLG}_)Usv{9+`^oavU_hWIt~<)EOCt}an8@p zP0cG|a4t$sEJ;mKD9}%WVsZNFMr%S-rs_>evELm1!MM0+hlUzO8Uj<2qD$JbVAUe}6aU2sCaD>o^*v?OAj# zbK6AyX(s0%@E=ie4Z1knbJeO<+dV^aCS}cXQ)xcho!^`&G2yvo*CL18+thZL*dN_7 zS<`OL>DWa&u@7q^7q83yQro}o^@BOT=PtOTYW1eLaQDRjT6G(Pb>a`})IO-#bujil oTf_d)gXYI;^d56J=gEF#X?wMYPtmzs5aA!YBLG}_)Usv{9+`^oaCjT?6Rs)43OI#yLobz*Y zQ}ap~oQqNuOHxx5$}>wc6x=<11Hv2m#DR(qd%8G=Se$-4*^ujygMjOQHf`P9tCxIU z3byz>HeAD8JtyX<63d#kIZTs-v>kP>e!9J5k$~{zTj9DopP!%AdF|3tk(9yH0v;xlI&8<$($-5BSquvyS~VTXa|B)4Wqi=47__J-3( zB_#Y_?sboxrqQ+NcAiU1-_ww{@ yt0p};ueM_L&SDSQ{}Y}Ggf4yknfbo8J%b$kPNOr%tFHmQ!{F)a=d#Wzp$PyCu8B7Q literal 0 HcmV?d00001 diff --git a/public/images/moon.png b/public/images/moon.png new file mode 100644 index 0000000000000000000000000000000000000000..0283a879121166dc7a60fc2461656c17892bbe27 GIT binary patch literal 3248 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 CmGP7S literal 0 HcmV?d00001 diff --git a/public/images/player.png b/public/images/player.png new file mode 100644 index 0000000000000000000000000000000000000000..212039f167b29d0e1ff27752aea61c26021e3b6e GIT binary patch literal 328 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxVoxE{-7;bCUl2|8IZT z(d}3j8(SNjLfQ{qjfx3}PjoO!|0sMX!~FX9_iq7t&42&@?`2>-(zIl!pvtRH?_|Ty z8Zd;^Go&$0ILjd5pm48#M%sc--ilIXp2n4{**O%{W+-f&b@3__i-7Qn^A3zjUzMbp z`1BhXk`9R`9ATIc2V^O&;jw4nQ0U^tB-8>vsfagx0qNGrZJwmEl3@w^5^;rw853Bf zJTCBra|<{u*wZj+CdX1QmFrDk8Ce9Bm)?|AIKc2&!_Q%+TPZ8hdkmhgelF{r5}E)$ CYiq^; literal 0 HcmV?d00001 diff --git a/public/images/player_death.png b/public/images/player_death.png new file mode 100644 index 0000000000000000000000000000000000000000..9abd211a8b3273ac6311798def0469793b6c87b7 GIT binary patch literal 404 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;Uj1_nlXPZ!6KiaAMt{{OdU zHYzA8auRi5`u*)~vWqw~hl1M-MuXn%`S*`4o$nYLDtcI+Cc4r3#u zf9$U^89EtR1i1BDcjUOUGcX=`@#4h~`ABBNIEIVAzrRmsc$Le-p`ez-wRg{+0}^w4 zc^Oy)!t`VI*2!v1OEYjN@Gv`Uv)xlKe`Z>ENXzEJolFW1275N--Zrz4UM3kUBMDT_ ztRdUL6y)Z{*0;a@|37(^HlM>!9BLNW3EXA4#4X@Zpnt$gLWKLo!roptHU>r(|IP(R z7#B6F9ac>YGK!eM+sGL7seUv-K8h%!wgk- z%<^R30|SBS6B;c#V;Ow%?``LIP3&mrol*Dch1raK-#_WjVB=8eie(UR(21{e4!W{l zUBG_5T?v~{b(ZuZjhy*gIee;bN;u3pcYtddBU49`IM4;B|3240*i-*M!1!oicltpS qV{PuPAJg9pi2widmm6la0YkyFNY)Mhej>owVDNPHb6Mw<&;$UI-?I?_ literal 0 HcmV?d00001 diff --git a/public/images/player_follow_off.png b/public/images/player_follow_off.png new file mode 100644 index 0000000000000000000000000000000000000000..935e7c65e8607696f36e4db42909fb2d5ab4d737 GIT binary patch literal 361 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxQ5I!_VyZTYioPXs{j9wcbkMlLxjVe>+9o2qGag?FBxVWwQ z`@4l1P!VOG38s5$eok7T$iV1QsKCS`(8j3lKQAR9D2QqE(W6HVkGQzH23}!i5fIip zYtZ_`q?y5iVWEVAj!w@)f!Pv9Kr6*k`ntNf+L&^ZPB17mXz;l-Ffi?Y0Q3RRrAwDo uGFej91c0jC0?OE)dKBfbzcOXyFkq;e7TrCOBl;}R{|uh4elF{r5}E+xNpv6p literal 0 HcmV?d00001 diff --git a/public/images/player_follow_on.gif b/public/images/player_follow_on.gif new file mode 100644 index 0000000000000000000000000000000000000000..6937e81d9518be0d50e481832f280ab5eb89a801 GIT binary patch literal 332 zcmZ?wbhEHb6krfwSj5io9|X>v`9ITmCWCPrgRwCK5CDY~|8x7fh6Fo12DlpO889;f zMHPRtuyQf5Gw6VnfV49(J5_AEvo_^~r)s3i>$U99621sBE|_{(gF$WMwt0?Q^23fB zpS`8CZ}(#?ks#>xmGjAdeAVc=ol02(XhA+_Mh zDnUk-+|bzA?`P(#hly28UGdpt^&})$ASvf47de%usdR1)gR$@Sui@pjBI#ZVKTNWx zU{@~Tu*S!$!Bu5ysnPE3=cbg`F4$3+`pje@4%0mtuiWn9IG~ct6d(O?jr@_!Sc^$3 MmX!2QW?--e0L_1Bc>n+a literal 0 HcmV?d00001 diff --git a/public/images/player_follow_on.png b/public/images/player_follow_on.png new file mode 100644 index 0000000000000000000000000000000000000000..fa790c51bb44aecefc0fbd1a705bb7fbca3f56f9 GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxSqyE{-7;bCUl2|8LK1 zZ~tGC&FH}KetBO8rjFK$6F>HpvNjuPi)YTNuTN%(<`HnvQ8+g{n}^3{(-O%8+I$Nc znL0X6a%V6oGz9S6uzIR=c%tTeNsdlM<^~2Q!w0&Dbq#vgFwT}RV&G5^VSZHPB>B$p zjpM}zUmi&Y76CU2g=`Cxr>2h1K-(TUuy82wFpK#{o?UR{0aM!TryWThH=l4UTEWht zpcXSLk?Uh78zabAhZ&B%&WdJ+DGWg8oRE1hQ_I78G%3&OG+>MJCYa44$rjF6*2UngDIVb8G+r literal 0 HcmV?d00001 diff --git a/public/images/player_travel.png b/public/images/player_travel.png new file mode 100644 index 0000000000000000000000000000000000000000..03ea7568b4ee907a0e5af314af76428c553fa0e7 GIT binary patch literal 454 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;Uj1_s7JPZ!6KiaAMt{{OdU z=CH7|bd+#usQvxT@X!qg#v@FZZBkE(EHdHd=5D@vn1_)=K`g~nRaLc#t>(`U!Do-y z7AW!pl|6Xz;>Z72S63(e`TP6(|BpYF{_j1^dLTz&-v^WD|8E6r75wWK*FPYUajbs9 zJ+=mhB&Y4QZpV|^1UbdpCa`!K#K*3f?HGBhIq`OCtik;G^W~4a0u4RVz$#$GBFVra z5XS7m$iUcgwV{VAvdvuopTU~Q4>D)=|6Dia;k(PSJaN0G8(v{z5xBz;@j~blPcS<( zP&b2@hnt(*g9)vzCU!kC%nEyf22A3c;Oowx%QJf+gF?dry#}@C|4&HVbTx=hlt{DY zU}h0e*3n>#6rA7b% literal 0 HcmV?d00001 diff --git a/public/images/scrolldown.png b/public/images/scrolldown.png new file mode 100644 index 0000000000000000000000000000000000000000..9d07aa655b1472a2342ba38c3c47e8d37a3399d4 GIT binary patch literal 1019 zcmaJ=O-$2J9IwInF+yTYOjJmGmT)nweeE{3RVvJN$kdW$OkF&vrG2mh?W^s>6%HOa z7$6)xc<^BK#MPr5I2gewCMJ5&#Au8OiSeKYKVtBPuVWh?oK0Te`@j5tzyIf(%?+Gt z2^|hG4AYYBSMqe<7py(I=>N#iO^t4cNTx^z@h~ZCE@Dy!9zq~%X(K3)G-LAWGt|v6 zO{`fclA?N6(y_&90fzG}hq4)_yT^AleGCyWghotTX4jV8tvpmdut2aU`BEm&;r^ z!eMuW7m~?jpdpH3iU@m?HqrdB?X}kw6y)iy=@1j!AW+nX@C1=r8tGOFmQ$Cty=s|g z!FXSDc!7gKN;ROW{vT>tb+kwFXgl723VVe~2l092;R#o#jT>$cTsczOMH<0w0psyn z6>}v_uvfwkNDszQ%W$ z<5*xrE>DBugU0^V=-L&Xp5Ry?TWZt?AK7%;T{_m6r*psPlfYReRq)?_$)5@J9^LbN z@kiVHP3Zwy-#hwgrFZ7~POJ6isC#2|$GMjH@8yoWj~@3b(S_*zlf+VM+xWco@y_eH znODho$3JYWbnafbbz1b~rw2dJ&Ai-LKGtuq-JV^%_a=0H?Q8R;{V%>ZLnid?VuyKP QwJCUx+01})|KyeFKl%enRsaA1 literal 0 HcmV?d00001 diff --git a/public/images/scrollup.png b/public/images/scrollup.png new file mode 100644 index 0000000000000000000000000000000000000000..b6b6c0c4673acbbf9079e83bf8095789aa11f802 GIT binary patch literal 1005 zcmah|PiWIn7|)oLF&OS9D(E9qPiyjC+9qvSH?pR61v^GoSWjY_yscqNUY5MsZ0s-` zC|<;?Ui2)ogP@lkbb4{9H%0KGXD=cm3Rdb%*KQ(X243F#9>3rB{k}i%=FIfPp}|vw z9LEinr*wnu!|@tOu>aX_ZIf*$X|YCU$vkbE0p>KD)G;W#<^ndbX|FEt;xUfv=bdVe z)+(1&i@1UrV}!`{8JpwACL-Uo7BK~Nyx@3w{>%1L9yoTMpUYHW#V_E7Gqo1r*|q7a zwYF&GY<^-Kj72JQ;9_cm$X)V6HOliHzbZS&*CG!(5W1M>_e9kyGoU~M3^D>tT2O{S zMgmHu6-5~Z5=2mhl8B@vQq+{BLI}Da&!PqPylUvBZY*Zy`39xFDvHf!Q)s3H5-f-) zm&?UIBq_-d$#B)9W|Z{8k)8(~hgRVD)FB>-J(_j0Li0RRx|f3MS1S9Cy|7y-Rx&X% zeGv&zblo_w4mzX;K9F%JI;^hxSTt}*RsxIlV}7JZX0hRGnBWF7{|y&-3g? zupL{?XsL``)a7(GB_mXXSuG8VIiw&|$Ydp$>T%0nNKMbeJ+5=W%^b;93jsDM395uF z^}09HAe4j+;)B9$22@PT@nSnJZC5D{oU7O_1;hoN+NjPy@nh`}Wg$XJ8Y&W$vj}Nv zxui{MX)T*8rLsEHzQ?r>$@{-tk%n%!`c^;^q5|l6>sk4PpH5?Zjpu*O%ao2lwu*KY70# OKfrQvTHl(ya_tY@TS5^4 literal 0 HcmV?d00001 diff --git a/public/images/server.png b/public/images/server.png new file mode 100644 index 0000000000000000000000000000000000000000..e28417c7ff650355b8e63e8726e3e3b9bf2c30e0 GIT binary patch literal 368 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxWP_E{-7;bCUl2|8LK% zCm3Ul-t7$@r)x4pk!#~e9-<0)nq0Y~O*u~x?q z7<2AU6JX#_Xo{N<{fT|OI0F+$UPDt8Qy8m&e#hdD&Ti!&42&(Nn)VeR81%krw6VTu z4cCzJpJT!3e+npJZhVj@DK+)z1rBB)?C_k+}|Y_9h%`@{4)Jz|^61R)I&rK_rtcv4LTcg%l%GN0L4Ri@?!F4grUXzwSMX z42(xST;d%3Jc2K(0%cn}60EqF9^T-|#3Ep*cYqsc#G?!r4g-eyPfe8kGrJvtu4M3Z L^>bP0l+XkK3L8!a literal 0 HcmV?d00001 diff --git a/public/images/sign.png b/public/images/sign.png new file mode 100644 index 0000000000000000000000000000000000000000..8d8573ceffab5aef9d2417b6e5aeab7ce89b7604 GIT binary patch literal 681 zcmV;a0#^NrP)P000>X1^@s6#OZ}&0007TNkl1we^xo3arX@&0OVAC0L=#|POdxJ_V`7Z|#J}KwaOqN)E)5ALMiwdfHOESfh%;UxP)BaA; zG%e(&N+94T77g`XU|(&cs5+t#d;$!_BLF?~E<|psJbAiGek{wv)yck))wLqeSH6%L z@DU9O+`c&j&@=BsEE^Qfrd(_BVPluk;RK0zn8MZ`TH7QV5{O1Z3}q5-Sks+T-rlJK zPQFt+6k=LXku556osXM!?%tSU@?x09e@@?%+sGTmLuEgQLI^YTpraPmWH zA4oJ3V19PooyewR0L)D1P*j~{RA4aa)Gp)(nWyWGhXnwdE!)i<3W-I6%+HN`471at zo?1SaBA-in>b>Ouy-?h#vhuFL?Bxk^=h7^_SVwNE0E`T$nY%LKdSAaS@V!*!`n4$l zo-MC?JcmMb!=x@L=!OY6x2`TJ0Pb;hq^KHoNx`x#0P2$B@%#cQSND1F=v8+C&1v9H z&7xd$fEDQno7-g?ibX@|HduG2*{YbtlU8qtC<}BPthCPzVp_J-_-HsV zb(Hze!lkv&SW< P00000NkvXXu0mjfdR0DT literal 0 HcmV?d00001 diff --git a/public/images/sign_home.png b/public/images/sign_home.png new file mode 100644 index 0000000000000000000000000000000000000000..5ecfcec81e6cad5fc313180680c6be12c2beb2cc GIT binary patch literal 407 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;Uj1_nkYPZ!6KiaAMt{{OdU zHYzA8auRi5`u*)~vWqw~hk{s1@9F9K$Ct{lW4o!)z>vhS^T5T6AOClY>mT@We}BD& zp~t^hS63@I|6~4Y((r|eMSyz~Z&Ff{hMAF}p&^55Q>FmJ#{2tfyBD!3G(6b0_%_=n z*0#iLhn`G`WfXAmx$%cGom|kKsuQjJhyq9Twbkt5*V@!p00i_>zopr09P!IrT_o{ literal 0 HcmV?d00001 diff --git a/public/images/sign_sign.png b/public/images/sign_sign.png new file mode 100644 index 0000000000000000000000000000000000000000..5eae09b9852c15a228489fd8975833c01dfdd43e GIT binary patch literal 431 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;Uj1_nkePZ!6KiaAMt{{Odc zo;>+-J)6+MeYL+O98DY;Ha3_<|J&uc}S$SF|=agv+zm$k^u=l$?0*=yje287v$MVkwR(A6~uA=kdM405q;4NJ-$2`*F@+ zi~jEoVL2di#BDnJt$PfYxCI<~0+^d?Nt+5MUE$(?40hs*|*2|;-b7eW3A zH7{E(-;n9V#=zJj$i0NAf#J|grUlm}9PcTl0L^~*mFa{)LSF8H+-J)6+MeYL+O98DY;Ha3{}&opYy=sk4kP{OQ<6F=@>yf|4ID1Ja?%9JS`W=|d; z?=LU|@)f*OQd13WDn2wkyu93>r>d%o=khY&WDgIIga?lvCH?sJR{HSj@bwbga&NaK zNjNYplwi2jz$c~?(Qw_Af$>PonU9}7G2P}!R_;D{o z!f54C^owzZS_;q_iH#0g3>*q`^cbpo1)uObu`#d+#OXKiFh0A+R+GiR#KW-QcwK>6 TNoo=>#2GwY{an^LB{Ts5surOk literal 0 HcmV?d00001 diff --git a/public/images/sign_warp.png b/public/images/sign_warp.png new file mode 100644 index 0000000000000000000000000000000000000000..9f05e517e1b8a5ddbc1047402682e38801df5fc5 GIT binary patch literal 435 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 DumVSe literal 0 HcmV?d00001 diff --git a/public/images/weather_stormy.png b/public/images/weather_stormy.png new file mode 100644 index 0000000000000000000000000000000000000000..45808fb140cdc8742683a387a07153634a537033 GIT binary patch literal 1285 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 literal 0 HcmV?d00001 diff --git a/public/images/weather_stormy_night.png b/public/images/weather_stormy_night.png new file mode 100644 index 0000000000000000000000000000000000000000..fe08ba889f8a0bfd7e0a12efd947fd557bf1d9d2 GIT binary patch literal 2968 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 literal 0 HcmV?d00001 diff --git a/public/images/weather_sunny.png b/public/images/weather_sunny.png new file mode 100644 index 0000000000000000000000000000000000000000..8ef8cbdaec0adfcc3c1dfe1330b190c21e0d44b6 GIT binary patch literal 905 zcmV;419tq0P)P000*V1^@s6{^R5m0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU#D@jB_RCwC#R7+12Q562}%(P4ypy=={ z3Kirbno{F)fl^%|X=F-jpm(Ljq( z-jCKgrOe}cr=_)&RyXR(lbqa{Gv|Kao%5Y@$&H)0kjW^e8RHM71`OCL<2j8-?fu51 z;8E_B{Z&q1a92ZpGOy#a63<~yrUHQ!@~hSx;WcHm-4{o z&cEPuesfI@n+Lq|QAuKv$*DkA!0G9kA-1^JUwce<4WIK_T(ZX?j&?{}3yHc2YApo~ zg+OQL`VjWFTeh$GmKy!ROQUOXlCP6f83|@)W}75Q@;aTB;CUXANCeBvD+mvsN9pzo z$kGfbp95Hm0d*N9x(JBXfD(lE&R6*D>|l`NI1oa>nZK6FP0P!ZVg#nyY&JL?<)}H+ zi?Fa4?^g~(m0MwKIRYx51}$m@R=Jw`*F4ze}@PGx10_Jf4p!r+|l)e+*QC*u1A6Ce&Y%zXN)aHWd||-t^-sp0MWysQKm^`2_&e1RJjUm$47gjlpafx=(^s` z%HgT2t$|s`1aPKSPP{6iQHUgp(0mA6M2!)xffyP)6-&?sP$G9}njX_c@cH~LJTJ7> zRPTtN+%S-7MAFH01|U~m!-5jPn12xJVn2$-2r5v5+-;03y3o$jp2alLbiGZM<@iS3 z*a(~`65SjV3wFlbw6faEh<=~M{K$O_j6Fx$hl!Ohx0l+Q?~-^6_da<#)KFjN&d<;L z?Zjuh1yaLNWgZ`%-h(1oy(rx0Idt$qkkd5H$uxAZ#jh>Wt8AVx;NDWWf5B)lIUA`6rFTKd}EK f|4IIy{SaUP?0FoMp=4%e00000NkvXXu0mjf<$|<~ literal 0 HcmV?d00001 diff --git a/public/images/weather_sunny_day.png b/public/images/weather_sunny_day.png new file mode 100644 index 0000000000000000000000000000000000000000..94ce5b077ec74f98c0d50e4d459b43a6c4050332 GIT binary patch literal 2385 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 literal 0 HcmV?d00001 diff --git a/public/images/weather_thunder.png b/public/images/weather_thunder.png new file mode 100644 index 0000000000000000000000000000000000000000..07bbfc0a4766796f99f1faa69f226c949ecf9540 GIT binary patch literal 627 zcmV-(0*w8MP)P000*V1^@s6{^R5m0000PbVXQnQ*UN; zcVTj606}DLVr3vnZDD6+Qe|Oed2z{QJOBU!6-h)vRCwBA{Qv(y1FZoQ10BGqC8L&% zT0$7GkfOM_q+DN+|JwyY{_j}jpS|Q``0Va*aV-?FCi~ z<}SCm0AwyeFRDllE(HO;?+pL`F{0q7FZdZ|FSGpc?K@k5xrN2Yix)35fG}CX1+qk3 z_!GmwzX*Z%pST$2EVBRw(*l^-hmRl0j}w00?+(BJpn9H%>jy(z@F|7|PlO5{0`bN3 zhaUX+$#wuJwIM7da3jf<{QSl8Vf%hf0XeC63?R(T{s$~BB=8xGjdUJ@eY@v~M#7m( z3Iaej>6WCV<_k`lGR^+vJL`M z&%vovr}wk7{51IXl?mochT|928E)Q}T9BNanD|OSj=VUzeEAB*wFeLJb8~)AkP>;$ z@cA<%!>>Oq40}%+0}}ynLTXC#LV0;vhO^IqlV{15D_7z9MN;^=!23@O44=R9GMu}i z_u<6_B@==6rQ{iVYqhRZAiMwo literal 0 HcmV?d00001 diff --git a/public/images/weather_thunder_day.png b/public/images/weather_thunder_day.png new file mode 100644 index 0000000000000000000000000000000000000000..89005210e0019ee5f95221735eafa6a30a560f9b GIT binary patch literal 2818 zcmZuzc{J4PAO4aVgQ`eAV<;s%myX-rmYbmaf z$cXGFlxQJo6h_8xy64>ge&_u>pXZ#!$!4kF zfPmROfJ)tF6WHlbV*`No`+R}DiDgfCC?<9RY&Q89Mnj(3zlXt;i#OOT^)C{bdHDdq zD~dC?Xl>h9X5&e*{vaCpG+>^-t~$#FK_Z5XPDmkB6SGpp#%wwNiXJoVel>H&7nC~w zu1L>nAlCb&s8JKxJGI1;OH9_pA~s%yCllZhM379>)Mrw>nsdAJwb~nWNS7@PnrI{0 zd!rfwqx)U7eeJ~^fpG4EC{|cpANiZ6uMJW4TEn!67!=IihjPHezosCoa>!ZwX~?5j z6ok_=GTseFFUUR#-}4XccKoJUC?P0lm6MicsTsaY@ia4o5{M@q8#03f19u&irYr81 zq%#j#=iS}iZU6TFpeRW470N@AISmMA?XzCF1Idy)TI%YTB}GJx1OJc!dfspGCl8kL z-PXGfK|H`qTRBPb!?gU~J}+@8L=Ye~j1e;Ah&JJL%K@9pK@cPu7}^)@V%Mm^SFqNLf_`D_ z!Erd;N!ONH`piryL)5m}nFkC8OCx1uqOdM6(+TX`qI5kW!`*{Ow&5104dH>R?#_6~ z-FmsQC!d7^gM&Q|4h}jYh==LpSK`T|ZaL+(b}g$^P`P>GeI}Ett)X!^<`@{(GwST#2{+pMmvJpZnltSc2wLc!`X_&JG01nvX`!Oz73AB;E`wsMu93kC(- z*4aGo?-!_<^l*ihr&!7piNwi~EcC5YI+1WV0F~5+g$Bt-ooQciZ* z|M5!`V!EYC8r!}eKRDo^zPFeM!cP>ri^1aWrmbL)8I=my)C9%#JIRf_dewb`8|ZdC z4iuD>lt?G^p~pR1jiQ8`u1WC&K$e*_+hIe36z+ry{s@ghI4fJmj#pfYTVg%&j*DGo z^&ifxUwcLAjtI)hr$@Mtt)I^}D{X0R9#<6RgT==nagf`3DK4UDGlHKbt(XT$xrMnoDtYs5w|(BF8!`w35Ty*nIHk$q zDui-r%zUke@Pys&Z%JN1*e;>k9XUt zeW)YKm(kpw5x%D`BzZJUdq%E`pdU!yZ2XwXQ=U=WU9WsjeM58>wO_n(JHKvHM(nUG)q9wJ=VHa$1Ph=9^o1RM-@K2QelGdpwr zqgWH;^{c(pJ&hdS1~JTqgMuAaQiK@g=MvK;ogDP1>g4{z^6Eu=(MQ&qthSV*J)!Id z@WZs5g<;NHk_F7AgSCR(G%yBh27PwU+LuQ)4t%SrOTaGOBG0?GNqF(<+I4;J;?esI zRuOD;e9#V?k4}#Ij(A2kiqRk?hs3Wbb%jnOB8~t`n?Q!pvr&(`Pwir6hUKiyEa&FH z!yk&;J6XG#HzE&nXjWk}N58$1eGSvvF7;bmSzoA2)Lscuue(%$2%44Z36oQlBsH_i#ircI|WB6%u&}vmkM3>8GTVh4QMnvHK z4Fs^xXO1lChC3_`@4uaA4m>iw-wy?tzNaSCCH|?gPysl6#bZxj-|teQJUTd`^+ha7 zY;0r#iO$L?)Y%wM-&%w2%X`*@*xc9L?>Q4u<*DW`GM1A`t*)Z8GLNo>$jg7`xT90~ zP8_7}9dHqqrL|v^bf}n3cURu+S&yjlcexF-$rBeLig$LZd|A}-lDBVsH*RfIwih>M zJf|a``Xf2{S5QSxzdZjazQ}QH$DWhC4Uob}c75;7>r;Q4b*zOe)H{%}r)cn&=>`}R zObYtt5wJMLJ=L}$nf*;;BGJ6xtl7>)TQ^Sf^Z`B!6Zmnw|@J^_~U zi{t-#n6W3%;s&Pz}T^JrTsu6=} z!Kh5BqFOO10JyjT5;XONLjF^F`#QV3hW#HU1dVCLs5D_z$Np2cf{uru+oQ3S0M77| JLA@R+_J1M#Ai4km literal 0 HcmV?d00001 diff --git a/public/images/weather_thunder_night.png b/public/images/weather_thunder_night.png new file mode 100644 index 0000000000000000000000000000000000000000..63b359e99601920e2f63809667af38dd5146935d GIT binary patch literal 2898 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 K=d#Wzp$Pz;7k-2Q literal 0 HcmV?d00001 diff --git a/public/images/window_close_hover.png b/public/images/window_close_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..08656f6af74f90239fcc112f05fa9d1b8333709a GIT binary patch literal 355 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxSqyE{-7;bCUl2|8MVX zrTn;_&FH|H|NkW!nL1dPL<*mHs1*3wT!HN+D~E!Zz;ks0@5zr@6c{)Z)Qr9!WOQIi zlt}pgh_(6s_xBQ9j!6oZB@6-%5^Q^Fe~YbSj5OXPV+2$xR?@io)-8ueTFsLt2{oHd zXltzg{?75q1qP7zhMbNK8w}#kDgZT#uqy$DN*T|bSD5X{@I+R1!HqOOKc1}&j4cnD zQqC%*HyfN$$l1sYv_kL4kB`b0TPFVPkyAUtpwM8zW1*thcsAUESx})tgU_Xbfob;x rTY>w^fy&lX*fufpsw zt%<>b!EoXdZf*t+g)UwcVh>x*uP>ZE9~m1M4rMZIa;s@$U=avoPLOg4aWUPQjiIp%$Mn#iRXMxA{y$qZkAQ=Y!nxPi*GtZ`tM!`1xNO<7 z1Sy4v86KBfSs7RaxOW|xYhCW($Gq9q)wQteAcF%#;2Wj}hDA3RuoH(mj&0As-(;w0 zD8ay?z{7mz>{&yRMB!#ekaBJYMwYUJ{qpt_OO`J6yu@+JM}a}Wp=U~3dIJM9!zx9i UWuNrsgM7f?>FVdQ&MBb@04R-P-2eap literal 0 HcmV?d00001 diff --git a/public/images/window_pinned_hover.png b/public/images/window_pinned_hover.png new file mode 100644 index 0000000000000000000000000000000000000000..e1f963d2abc69ced69a46b837a4c1430e0770dd4 GIT binary patch literal 290 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`Ea{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxU*bE{-7;bCUl2|8LKn zZ1uRF&FH}Yng1mjnK~va_CB<4R-16#bgcq| zdFf3_g#!!=ziYfz)-5^;bU1^j LtDnm{r-UW|ufpsw zt%<>b!EoXdZf*t+g)UwcVh>x*uP>ZE9~m1M4rMZIa;s@$U=avoPLOg4aWUPQjiIp%$Mn`xb7liW(xO!> zBN!YQF5Y1RaunCFFzwm1C*a5JZMn)`@(v6WpES4g%QqQnGAJ};$hj~ux)gIWFm*f= zV_*@u=Hb>KkaRVnyP|>N(9Et6u4-nMm6erivKWC199aY$ENUChx*AM>T%>EpAm9+8 zdq8a3lQfeChDDVEj4T3Ybs0DmW<>*KT*S*19K4gTe~DWM4fP8EF? literal 0 HcmV?d00001 diff --git a/public/images/zoom_out.png b/public/images/zoom_out.png new file mode 100644 index 0000000000000000000000000000000000000000..9af74b2d1a2895f9fe062eb8f9ec40fc662c9bec GIT binary patch literal 336 zcmeAS@N?(olHy`uVBq!ia0vp^5+KaM1|%Pp+x`GjEa{HEjtmSN`)Ym%P6qN7l0AZa z85pWm85kOx85n;42huMX7)lKo7+xhXFj!4zU=YurzH;UjpxWD>E{-7;bCUl2|8L*C zZ{NRqHmQbgaeX5u76I-PAC%quE*z?1b8|cPpMmiR)8h}HJ_TL`xb7liW(xO!> zBN!YQF5Y2kU|6)1m4VTvn45vA + +
+ + + + + + +
+ diff --git a/src/App.vue b/src/App.vue index 1c4de4d..4b8c3ce 100644 --- a/src/App.vue +++ b/src/App.vue @@ -1,11 +1,11 @@ - - \ No newline at end of file diff --git a/src/components/Map.vue b/src/components/Map.vue new file mode 100644 index 0000000..ce8462c --- /dev/null +++ b/src/components/Map.vue @@ -0,0 +1,195 @@ + + + + + \ No newline at end of file diff --git a/src/components/PlayerListItem.vue b/src/components/PlayerListItem.vue deleted file mode 100644 index a7bbfcd..0000000 --- a/src/components/PlayerListItem.vue +++ /dev/null @@ -1,24 +0,0 @@ - - - - - \ No newline at end of file diff --git a/src/components/Sidebar.vue b/src/components/Sidebar.vue index 3f72479..2d2e53e 100644 --- a/src/components/Sidebar.vue +++ b/src/components/Sidebar.vue @@ -1,30 +1,112 @@ - \ No newline at end of file diff --git a/src/components/map/control/ClockControl.vue b/src/components/map/control/ClockControl.vue new file mode 100644 index 0000000..4628de6 --- /dev/null +++ b/src/components/map/control/ClockControl.vue @@ -0,0 +1,56 @@ + + + \ No newline at end of file diff --git a/src/components/map/control/CoordinatesControl.vue b/src/components/map/control/CoordinatesControl.vue new file mode 100644 index 0000000..e72804c --- /dev/null +++ b/src/components/map/control/CoordinatesControl.vue @@ -0,0 +1,43 @@ + + + \ No newline at end of file diff --git a/src/components/map/control/LinkControl.vue b/src/components/map/control/LinkControl.vue new file mode 100644 index 0000000..700d747 --- /dev/null +++ b/src/components/map/control/LinkControl.vue @@ -0,0 +1,40 @@ + + + \ No newline at end of file diff --git a/src/components/map/control/LogoControl.vue b/src/components/map/control/LogoControl.vue new file mode 100644 index 0000000..c1322c8 --- /dev/null +++ b/src/components/map/control/LogoControl.vue @@ -0,0 +1,42 @@ + + + \ No newline at end of file diff --git a/src/components/map/layer/MapLayer.vue b/src/components/map/layer/MapLayer.vue index ef11b66..71f7cf1 100644 --- a/src/components/map/layer/MapLayer.vue +++ b/src/components/map/layer/MapLayer.vue @@ -3,7 +3,8 @@ import {defineComponent} from "@vue/runtime-core"; import {DynmapMap} from "@/dynmap"; import L from 'leaflet'; import {useStore} from "@/store"; -import {hdMapType} from "@/leaflet/mapType/HDMapType"; +import {HDMapType} from "@/leaflet/mapType/HDMapType"; +import {MutationTypes} from "@/store/mutation-types"; export default defineComponent({ props: { @@ -21,15 +22,11 @@ export default defineComponent({ } }, - data() { + setup() { + let layer = undefined as HDMapType | undefined; + return { - layer: hdMapType({ - maxZoom: this.map.nativeZoomLevels + this.map.extraZoomLevels, - maxNativeZoom: this.map.nativeZoomLevels, - errorTileUrl: 'images/blank.png', - worldName: this.map.world, - prefix: this.map.prefix, - }) as L.Layer, + layer, } }, @@ -41,7 +38,7 @@ export default defineComponent({ watch: { active(newValue) { - console.warn(`Active for ${this.map.world} ${this.map.name} now ${newValue}`); + console.warn(`Active for ${this.map.world.name} ${this.map.name} now ${newValue}`); if(newValue) { this.enableLayer(); @@ -52,7 +49,13 @@ export default defineComponent({ }, mounted() { - console.log('mounted ' + this.name + ' ' + this.active); + // console.log('mounted ' + this.name + ' ' + this.active); + this.layer = new HDMapType({ + errorTileUrl: 'images/blank.png', + mapSettings: Object.freeze(JSON.parse(JSON.stringify(this.map))), + }) as HDMapType; + + console.log(this.layer); if(this.active) { this.enableLayer(); @@ -60,7 +63,8 @@ export default defineComponent({ }, unmounted() { - console.log('unmounted ' + this.name); + // console.log('unmounted ' + this.name); + this.disableLayer(); }, render() { @@ -69,12 +73,24 @@ export default defineComponent({ methods: { enableLayer() { - console.warn('Enabling layer ' + this.map.world + ' ' + this.map.name); + if(!this.layer) { + return; + } + + console.warn('Enabling layer ' + this.map.world.name + ' ' + this.map.name); + + useStore().commit(MutationTypes.SET_CURRENT_PROJECTION, this.layer.getProjection()); this.leaflet.addLayer(this.layer); + this.leaflet.panTo(this.layer.getProjection().locationToLatLng(this.map.world.center), { + noMoveStart: true, + animate: false, + }); }, disableLayer() { - console.warn('Disabling layer ' + this.map.world + ' ' + this.map.name); - this.layer.remove(); + if(this.layer) { + console.warn('Disabling layer ' + this.map.world.name + ' ' + this.map.name); + this.layer.remove(); + } } } }) diff --git a/src/components/map/layer/MarkerSetLayer.vue b/src/components/map/layer/MarkerSetLayer.vue new file mode 100644 index 0000000..beb2505 --- /dev/null +++ b/src/components/map/layer/MarkerSetLayer.vue @@ -0,0 +1,65 @@ + + + + + \ No newline at end of file diff --git a/src/components/map/layer/PlayersLayer.vue b/src/components/map/layer/PlayersLayer.vue index 9beab9e..599f917 100644 --- a/src/components/map/layer/PlayersLayer.vue +++ b/src/components/map/layer/PlayersLayer.vue @@ -1,33 +1,49 @@ + + \ No newline at end of file diff --git a/src/components/map/marker/PlayerMarker.vue b/src/components/map/marker/PlayerMarker.vue index 0db7c1c..cb0fa5e 100644 --- a/src/components/map/marker/PlayerMarker.vue +++ b/src/components/map/marker/PlayerMarker.vue @@ -1,29 +1,108 @@ diff --git a/src/components/map/vector/Areas.vue b/src/components/map/vector/Areas.vue new file mode 100644 index 0000000..694d90c --- /dev/null +++ b/src/components/map/vector/Areas.vue @@ -0,0 +1,237 @@ + + + \ No newline at end of file diff --git a/src/components/map/vector/Circle.vue b/src/components/map/vector/Circle.vue new file mode 100644 index 0000000..d37c55d --- /dev/null +++ b/src/components/map/vector/Circle.vue @@ -0,0 +1,81 @@ + + + \ No newline at end of file diff --git a/src/components/sidebar/FollowTarget.vue b/src/components/sidebar/FollowTarget.vue new file mode 100644 index 0000000..98680c3 --- /dev/null +++ b/src/components/sidebar/FollowTarget.vue @@ -0,0 +1,65 @@ + + + + + \ No newline at end of file diff --git a/src/components/PlayerList.vue b/src/components/sidebar/PlayerList.vue similarity index 67% rename from src/components/PlayerList.vue rename to src/components/sidebar/PlayerList.vue index e9fb0e5..67b33ca 100644 --- a/src/components/PlayerList.vue +++ b/src/components/sidebar/PlayerList.vue @@ -1,12 +1,10 @@ + + \ No newline at end of file diff --git a/src/components/WorldList.vue b/src/components/sidebar/WorldList.vue similarity index 66% rename from src/components/WorldList.vue rename to src/components/sidebar/WorldList.vue index 7c1328b..2f24f8e 100644 --- a/src/components/WorldList.vue +++ b/src/components/sidebar/WorldList.vue @@ -1,12 +1,10 @@ - \ No newline at end of file diff --git a/src/dynmap.d.ts b/src/dynmap.d.ts index a9ba9c5..8fc9ab0 100644 --- a/src/dynmap.d.ts +++ b/src/dynmap.d.ts @@ -1,8 +1,12 @@ -import {LatLng} from "leaflet"; +import {CircleMarkerOptions, PathOptions, PointTuple, PolylineOptions} from "leaflet"; +import {CoordinatesControlOptions} from "@/leaflet/control/CoordinatesControl"; +import {LogoControlOptions} from "@/leaflet/control/LogoControl"; +import {ClockControlOptions} from "@/leaflet/control/ClockControl"; declare global { interface Window { - config: DynmapConfig + config: DynmapConfig; + hideSplash: Function; } } @@ -27,9 +31,9 @@ interface DynmapServerConfig { chatInterval: number; defaultMap?: string; defaultWorld?: string; - defaultZoom?: number; + defaultZoom: number; followMap?: string; - followZoom?: number; + followZoom: number; updateInterval: number; showLayerControl: boolean; title: string; @@ -52,7 +56,16 @@ interface DynmapMessageConfig { } interface DynmapComponentConfig { + markers: DynmapMarkersConfig; playerMarkers?: DynmapPlayerMarkersConfig; + coordinatesControl?: CoordinatesControlOptions; + clockControl ?: ClockControlOptions; + linkControl: boolean; + logoControls: Array; +} + +interface DynmapMarkersConfig { + showLabels: boolean } interface DynmapPlayerMarkersConfig { @@ -72,7 +85,7 @@ interface DynmapWorld { title: string; height: number; center: Coordinate; - maps: Array; + maps: Map; } interface DynmapMap { @@ -95,6 +108,12 @@ interface DynmapMap { extraZoomLevels: number; } +interface DynmapWorldState { + raining: boolean; + thundering: boolean; + timeOfDay: number; +} + interface Coordinate { x: number; y: number; @@ -116,12 +135,10 @@ interface DynmapConfigurationResponse { } interface DynmapUpdateResponse { + worldState: DynmapWorldState; configHash: number; playerCount: number; - raining: boolean; - thundering: boolean; - timeOfDay: number; - players: Array; + players: Set; timestamp: number; //TODO: Tiles etc } @@ -135,4 +152,61 @@ interface DynmapPlayer { location: DynmapLocation; } +interface DynmapMarkerSet { + label: string; + hidden: boolean; + priority: number; + minZoom?: number; + maxZoom?: number; + showLabels?: boolean; + markers: Map; + areas: Map; + lines: Map; + circles: Map; +} +interface DynmapMarker { + dimensions: PointTuple; + icon: string; + label: string; + isHTML: boolean; + location: Coordinate; + minZoom?: number; + maxZoom?: number; + popupContent?: string; +} + +interface DynmapArea { + style: PolylineOptions; + label: string; + isHTML: boolean; + x: Array; + y: PointTuple; + z: Array; + minZoom?: number; + maxZoom?: number; + popupContent?: string; +} + +interface DynmapLine { + x: Array; + y: Array; + z: Array; + style: PolylineOptions; + label: string; + isHTML: boolean; + minZoom?: number; + maxZoom?: number; + popupContent?: string; +} + +interface DynmapCircle { + location: Coordinate; + radius: PointTuple; + style: CircleMarkerOptions; + label: string; + isHTML: boolean; + minZoom?: number; + maxZoom?: number; + popupContent?: string; +} diff --git a/src/leaflet/control/ClockControl.ts b/src/leaflet/control/ClockControl.ts new file mode 100644 index 0000000..3211658 --- /dev/null +++ b/src/leaflet/control/ClockControl.ts @@ -0,0 +1,123 @@ +import L, {ControlOptions} from 'leaflet'; +import {useStore} from "@/store"; +import {DynmapWorldState} from "@/dynmap"; + +export interface ClockControlOptions extends ControlOptions { + showDigitalClock: boolean; + showWeather: boolean; +} + +export class ClockControl extends L.Control { + // @ts-ignore + options: ClockControlOptions; + + private _map ?: L.Map; + private _container?: HTMLElement; + private _sun?: HTMLElement; + private _moon?: HTMLElement; + private _clock?: HTMLElement; + private _weather?: HTMLElement; + + constructor(options: ClockControlOptions) { + super(options); + + options.position = 'topleft'; + L.Util.setOptions(this, options); + } + + onAdd(map: L.Map) { + this._container = L.DomUtil.create('div', 'largeclock timeofday'); + this._sun = L.DomUtil.create('div', 'timeofday sun', this._container); + this._moon = L.DomUtil.create('div', 'timeofday moon', this._sun); + + this._sun.style.backgroundPosition = (-150) + 'px ' + (-150) + 'px'; + this._moon.style.backgroundPosition = (-150) + 'px ' + (-150) + 'px'; + + if (this.options.showDigitalClock) { + this._clock = L.DomUtil.create('div', 'timeofday digitalclock', this._container) + } + + if (this.options.showWeather) { + this._weather = L.DomUtil.create('div', 'weather', this._container) + } + + return this._container; + } + + update(worldState: DynmapWorldState) { + const timeOfDay = worldState.timeOfDay; + let sunAngle; + + if (timeOfDay > 23100 || timeOfDay < 12900) { + //day mode + let movedTime = timeOfDay + 900; + movedTime = (movedTime >= 24000) ? movedTime - 24000 : movedTime; + //Now we have 0 -> 13800 for the day period + //Divide by 13800*2=27600 instead of 24000 to compress day + sunAngle = ((movedTime) / 27600 * 2 * Math.PI); + } else { + //night mode + const movedTime = timeOfDay - 12900; + //Now we have 0 -> 10200 for the night period + //Divide by 10200*2=20400 instead of 24000 to expand night + sunAngle = Math.PI + ((movedTime) / 20400 * 2 * Math.PI); + } + + const moonAngle = sunAngle + Math.PI; + + if (timeOfDay >= 0) { + this._sun!.style.backgroundPosition = (-50 * Math.cos(sunAngle)) + 'px ' + (-50 * Math.sin(sunAngle)) + 'px'; + this._moon!.style.backgroundPosition = (-50 * Math.cos(moonAngle)) + 'px ' + (-50 * Math.sin(moonAngle)) + 'px'; + } else { + this._sun!.style.backgroundPosition = '-150px -150px'; + this._moon!.style.backgroundPosition = '-150px -150px'; + } + + const minecraftTime = this.getMinecraftTime(timeOfDay); + + if(timeOfDay >= 0) { + this._clock!.classList.remove(minecraftTime.night ? 'day' : 'night'); + this._clock!.classList.add(minecraftTime.day ? 'day' : 'night'); + this._clock!.textContent = [ + minecraftTime.hours.toString().padStart(2, '0'), + minecraftTime.minutes.toString().padStart(2, '0') + ].join(':'); + } else { + this._clock!.classList.remove(minecraftTime.night ? 'day' : 'night'); + this._clock!.textContent = ''; + } + + if(this.options.showWeather) { + const dayNight = (timeOfDay > 23100 || timeOfDay < 12900) ? "day" : "night"; + let className = 'sunny'; + + if (worldState.raining) { + className = 'stormy'; + + if (worldState.thundering) { + className = 'thunder'; + } + } + + this._weather?.classList.remove('stormy_day', 'stormy_night', 'sunny_day', 'sunny_night', 'thunder_day', 'thunder_night'); + this._weather?.classList.add(`${className}_${dayNight}`); + } + } + + getMinecraftTime(serverTime: number) { + const day = serverTime >= 0 && serverTime < 13700; + + return { + serverTime: serverTime, + days: Math.floor((serverTime + 8000) / 24000), + + // Assuming it is day at 6:00 + hours: (Math.floor(serverTime / 1000) + 6) % 24, + minutes: Math.floor(((serverTime / 1000) % 1) * 60), + seconds: Math.floor(((((serverTime / 1000) % 1) * 60) % 1) * 60), + + day: day, + night: !day + }; + } +} \ No newline at end of file diff --git a/src/leaflet/control/CoordinatesControl.js b/src/leaflet/control/CoordinatesControl.js deleted file mode 100644 index e3bdc98..0000000 --- a/src/leaflet/control/CoordinatesControl.js +++ /dev/null @@ -1,108 +0,0 @@ -import L from 'leaflet'; - -L.Control.CoordinatesControl = L.Control.extend({ - options: { - showY: true, - showRegion: false, - showChunk: false, - label: 'x,y,z: ', - position: 'topleft', - }, - - _coordsContainer: undefined, - _regionContainer: undefined, - _chunkContainer: undefined, - - constructor(options) { - L.Util.setOptions(this, options); - }, - - onAdd(map) { - var container = L.DomUtil.create('div', 'coord-control'); - - if (!this.options.showY) { - container.classList.add('coord-control-noy'); - } - - this._coordsContainer = L.DomUtil.create('span', 'coord-control-value'); - this._coordsContainer.innerText = this.options.showY ? '---,---,---' : '---,---'; - this._coordsContainer.dataset.label = this.options.label; - container.insertAdjacentElement('beforeend', this._coordsContainer); - - if (this.options.showRegion) { - this._regionContainer = L.DomUtil.create('span', 'coord-control-value'); - this._regionContainer.innerText = '--------'; - container.insertAdjacentElement('beforeend', this._regionContainer); - } - - if (this.options.showChunk) { - this._chunkContainer = L.DomUtil.create('span', 'coord-control-value'); - this._chunkContainer.innerText = '---,---'; - this._chunkContainer.dataset.label = 'Chunk: '; - container.insertAdjacentElement('beforeend', this._chunkContainer); - } - - map.on('mousemove', this._onMouseMove, this); - map.on('mouseout', this._onMouseOut, this); - - this._update(); - return container; - }, - - remove() { - if (!this._map) { - return this; - } - - this._map.on('mousemove', this._onMouseMove, this); - this._map.on('mouseout', this._onMouseOut, this); - L.Control.prototype.remove.call(this); - - return this; - }, - - _onMouseMove(event) { - if (!this._map) { - return; - } - - var loc = dynmap.getProjection().fromLatLngToLocation(event.latlng, dynmap.world.sealevel + 1); - - if (this.options.showY) { - this._coordsContainer.innerText = Math.round(loc.x) + ',' + loc.y + ',' + Math.round(loc.z); - } else { - this._coordsContainer.innerText = Math.round(loc.x) + ',' + Math.round(loc.z); - } - - if (this.options.showRegion) { - this._regionContainer.innerText = 'r.' + Math.floor(loc.x / 512) + '.' + Math.floor(loc.z / 512) + '.mca'; - } - - if (this.options.showChunk) { - this._chunkContainer.innerText = 'Chunk: ' + Math.floor(loc.x / 16) + ',' + Math.floor(loc.z / 16); - } - }, - - _onMouseOut() { - if (!this._map) { - return; - } - - if (this.options.showY) { - this._coordsContainer.innerText = '---,---'; - } else { - this._coordsContainer.innerText = '---,---,---'; - } - - if (this.options.showRegion) { - this._regionContainer.innerText = '--------'; - } - - if (this.options.showChunk) { - this._chunkContainer.innerText = '---,---'; - } - }, - - _update() { - } -}); diff --git a/src/leaflet/control/CoordinatesControl.ts b/src/leaflet/control/CoordinatesControl.ts new file mode 100644 index 0000000..aec6a14 --- /dev/null +++ b/src/leaflet/control/CoordinatesControl.ts @@ -0,0 +1,148 @@ +import L, {ControlOptions, LeafletMouseEvent} from 'leaflet'; +import {useStore} from "@/store"; +import {Coordinate} from "@/dynmap"; + +const store = useStore(); + +export interface CoordinatesControlOptions extends ControlOptions { + showY: boolean; + showRegion: boolean; + showChunk: boolean; + label: string; +} + +export class CoordinatesControl extends L.Control { + // @ts-ignore + options: CoordinatesControlOptions; + + private _map ?: L.Map; + private _location?: Coordinate; + private _locationChanged: boolean = false; + private readonly _coordsContainer: HTMLSpanElement; + private readonly _regionContainer: HTMLSpanElement; + private readonly _chunkContainer: HTMLSpanElement; + + constructor(options: CoordinatesControlOptions) { + super(options); + + options.showRegion = true; + options.showChunk = true; + this._coordsContainer = L.DomUtil.create('span', 'value coordinates'); + this._chunkContainer = L.DomUtil.create('span', 'value chunk'); + this._regionContainer = L.DomUtil.create('span', 'value region'); + + options.position = 'bottomleft'; + L.Util.setOptions(this, options); + } + + onAdd(map: L.Map) { + const container = L.DomUtil.create('div', 'leaflet-control-coordinates'); + + this._coordsContainer.textContent = this.options.showY ? '-----, ----- , -----' : '-----, -----'; + this._coordsContainer.dataset.label = this.options.label; + container.appendChild(this._coordsContainer); + + if (this.options.showRegion) { + this._regionContainer.textContent = '--------------'; + this._regionContainer.dataset.label = 'Region'; + container.appendChild(this._regionContainer); + } + + if (this.options.showChunk) { + this._chunkContainer.textContent = '----, ----'; + this._chunkContainer.dataset.label = 'Chunk'; + container.appendChild(this._chunkContainer); + } + + map.on('mousemove', this._onMouseMove, this); + map.on('mouseout', this._onMouseOut, this); + + return container; + } + + remove() { + if (!this._map) { + return this; + } + + this._map.on('mousemove', this._onMouseMove, this); + this._map.on('mouseout', this._onMouseOut, this); + L.Control.prototype.remove.call(this); + + return this; + } + + _onMouseMove(event: LeafletMouseEvent) { + if (!this._map || !store.state.currentWorld) { + return; + } + + this._location = store.state.currentProjection.latLngToLocation(event.latlng, store.state.currentWorld.seaLevel + 1); + + if(!this._locationChanged) { + this._locationChanged = true; + requestAnimationFrame(() => this._update()); + } + } + + _onMouseOut() { + if (!this._map) { + return; + } + + this._location = undefined; + + if(!this._locationChanged) { + this._locationChanged = true; + requestAnimationFrame(() => this._update()); + } + } + + _update() { + if (!this._map || !store.state.currentWorld || !this._locationChanged) { + return; + } + + this._locationChanged = false; + + if(!this._location) { + if (this.options.showY) { + this._coordsContainer.textContent = '-----, ----- , -----'; + } else { + this._coordsContainer.textContent = '-----, -----'; + } + + if (this.options.showRegion) { + this._regionContainer.textContent = '--------------'; + } + + if (this.options.showChunk) { + this._chunkContainer.textContent = '----, ----'; + } + + return; + } + + const x = Math.round(this._location.x).toString().padStart(5, ' '), + y = this._location.y.toString().padStart(3, ' '), + z = Math.round(this._location.z).toString().padStart(5, ' '), + regionX = Math.floor(this._location.x / 512).toString().padStart(3, ' '), + regionZ = Math.floor(this._location.z / 512).toString().padStart(3, ' '), + chunkX = Math.floor(this._location.x / 16).toString().padStart(4, ' '), + chunkZ = Math.floor(this._location.z / 16).toString().padStart(4, ' '); + + if (this.options.showY) { + this._coordsContainer.textContent = `${x}, ${y}, ${z}`; + } else { + this._coordsContainer.textContent = `${x}, ${z}`; + } + + if (this.options.showRegion) { + this._regionContainer.textContent = `r.${regionX}, ${regionZ}.mca`; + } + + if (this.options.showChunk) { + this._chunkContainer.textContent = `${chunkX}, ${chunkZ}`; + } + } +} diff --git a/src/leaflet/control/LayerControl.ts b/src/leaflet/control/LayerControl.ts new file mode 100644 index 0000000..f996de5 --- /dev/null +++ b/src/leaflet/control/LayerControl.ts @@ -0,0 +1,78 @@ +import L, {ControlOptions, ControlPosition, LeafletMouseEvent} from 'leaflet'; +import {useStore} from "@/store"; + +const store = useStore(); + +export interface CoordinatesControlOptions extends ControlOptions { + showY: boolean; + showRegion: boolean; + showChunk: boolean; + label: string; +} + +export class LayerControl extends L.Control.Layers { + constructor() { + super(); + } + + // Function override to include pos + addOverlay(layer: L.Layer, name: string, pos: number) { + this._addLayer(layer, name, true, pos); + this._update(); + return this; + } + + // Function override to order layers by pos + _addLayer(layer: L.Layer, name: string, overlay, pos: number) { + var id = L.stamp(layer); + + this._layers[pos] = { + layer: layer, + name: name, + overlay: overlay, + id: id + }; + + if (this.options.autoZIndex && layer.setZIndex) { + this._lastZIndex++; + layer.setZIndex(this._lastZIndex); + } + } + + // Function override to convert the position-based ordering into the id-based ordering + _onInputClick() { + var i, input, obj, + inputs = this._form.getElementsByTagName('input'), + inputsLen = inputs.length, + baseLayer; + + this._handlingClick = true; + + // Convert ID to pos + var id2pos = {}; + for (i in this._layers) { + id2pos[this._layers[i].id] = i; + } + + for (i = 0; i < inputsLen; i++) { + input = inputs[i]; + obj = this._layers[id2pos[input.layerId]]; + + if (input.checked && !this._map.hasLayer(obj.layer)) { + this._map.addLayer(obj.layer); + if (!obj.overlay) { + baseLayer = obj.layer; + } + } else if (!input.checked && this._map.hasLayer(obj.layer)) { + this._map.removeLayer(obj.layer); + } + } + + if (baseLayer) { + this._map.setZoom(this._map.getZoom()); + this._map.fire('baselayerchange', {layer: baseLayer}); + } + + this._handlingClick = false; + } +} diff --git a/src/leaflet/control/LinkControl.ts b/src/leaflet/control/LinkControl.ts index 72da8ec..5dbb432 100644 --- a/src/leaflet/control/LinkControl.ts +++ b/src/leaflet/control/LinkControl.ts @@ -1,51 +1,26 @@ import L, {ControlOptions} from 'leaflet'; - -export interface LinkControlOptions extends ControlOptions {} +import {useStore} from "@/store"; export class LinkControl extends L.Control { - options: LinkControlOptions + // @ts-ignore + options: ControlOptions - onAdd(map) { - this._map = map; - this._container = L.DomUtil.create('div', 'dynmap-link'); + private _map ?: L.Map; - this._linkButton = this._createButton( - 'Link', 'dynmap-link-button', this._follow, this); - - this._container.appendChild(this._linkButton); - return this._container; + constructor(options: ControlOptions) { + super(options); } - getContainer() { - return this._container; - } + onAdd(map: L.Map) { + const linkButton = L.DomUtil.create('button', 'leaflet-control-link') as HTMLButtonElement; - getPosition() { - return this.options.position; - } + linkButton.type = 'button'; + linkButton.title = 'Link'; + linkButton.addEventListener('click', () => { + const projection = useStore().state.currentProjection; + console.log(projection.latLngToLocation(this._map!.getCenter(), 64)); + }); - _createButton(title, className, fn, context) { - const link = document.createElement('a'); - link.href = '#'; - link.title = title; - link.className = className; - link.onmouseover = function () { - link.href = dynmap.getLink(); - }; - - L.DomEvent.disableClickPropagation(link); - L.DomEvent.addListener(link, 'click', L.DomEvent.preventDefault); - L.DomEvent.addListener(link, 'click', fn, context); - - return link; - } - - _follow() { - // var url = dynmap.getLink(); - // window.location = url; + return linkButton; } } - -// var link = new dynmapLink(); -// dynmap.map.addControl(link); -// } diff --git a/src/leaflet/control/LogoControl.js b/src/leaflet/control/LogoControl.js deleted file mode 100644 index a3787a0..0000000 --- a/src/leaflet/control/LogoControl.js +++ /dev/null @@ -1,50 +0,0 @@ -import L from 'leaflet'; - -const LogoControl = L.Control.extend({ - onAdd: function (map) { - this._container = L.DomUtil.create('div', 'leaflet-control-attribution'); - this._map = map; - this._update(); - return this._container; - }, - - getPosition: function () { - if (configuration.position == 'top-left') - return 'topleft'; - else if (configuration.position == 'top-right') - return 'topright'; - else if (configuration.position == 'bottom-left') - return 'bottomleft'; - else - return 'bottomright'; - }, - - getContainer: function () { - return this._container; - }, - - _update: function () { - if (!this._map) return; - var c = this._container; - if (configuration.linkurl) { - c = $('').attr('href', configuration.linkurl).appendTo(c)[0]; - } - if (configuration.logourl) { - $(c).append($('').attr('src', configuration.logourl).attr('alt', configuration.text).appendTo(c)[0]); - } else { - $(c).text(configuration.text); - } - } -}); -// -// dynmap.map.options.attributionControl = false; -// if (dynmap.map.attributionControl) { -// dynmap.map.removeControl(dynmap.map.attributionControl); -// dynmap.map.attributionControl = null; -// } -// var l = new Logo(); -// dynmap.map.addControl(l); -// } -// ; - -export default LogoControl; \ No newline at end of file diff --git a/src/leaflet/control/LogoControl.ts b/src/leaflet/control/LogoControl.ts new file mode 100644 index 0000000..ed0d5b5 --- /dev/null +++ b/src/leaflet/control/LogoControl.ts @@ -0,0 +1,45 @@ +import L, {ControlOptions} from 'leaflet'; + +export interface LogoControlOptions extends ControlOptions { + url?: string; + image?: string; + text: string; +} + +export class LogoControl extends L.Control { + // @ts-ignore + options: LogoControlOptions; + + constructor(options: LogoControlOptions) { + super(options); + } + + onAdd(map: L.Map) { + const container = L.DomUtil.create('div', 'leaflet-control-logo'); + let link; + + if (this.options.url) { + link = L.DomUtil.create('a', '', container) as HTMLAnchorElement; + link.href = this.options.url; + } + + if (this.options.image) { + const image = L.DomUtil.create('img', '', link) as HTMLImageElement; + image.src = this.options.image; + image.alt = this.options.text; + } else { + container.textContent = this.options.text; + } + + return container; + } +} + +// +// dynmap.map.options.attributionControl = false; +// if (dynmap.map.attributionControl) { +// dynmap.map.removeControl(dynmap.map.attributionControl); +// dynmap.map.attributionControl = null; +// } +// } +// ; diff --git a/src/leaflet/icon/DynmapIcon.ts b/src/leaflet/icon/DynmapIcon.ts new file mode 100644 index 0000000..109be28 --- /dev/null +++ b/src/leaflet/icon/DynmapIcon.ts @@ -0,0 +1,69 @@ +import L, {PointTuple} from 'leaflet'; + +export interface DynmapIconOptions { + icon: string; + label: string; + dimensions: PointTuple; + showLabel: boolean; + isHtml?: boolean; + className?: string; +} + +export class DynmapIcon extends L.Icon { + static defaultOptions: DynmapIconOptions = { + icon: 'default', + label: '', + dimensions: [16,16], + showLabel: false, + isHtml: false, + className: '', + }; + private _container?: HTMLDivElement; + + constructor(options: DynmapIconOptions) { + super(Object.assign(DynmapIcon.defaultOptions, options)); + } + + createIcon(oldIcon: HTMLElement) { + if (oldIcon) { + L.DomUtil.remove(oldIcon); + } + + const + img = document.createElement('img'), + label = document.createElement('span'), + url = `${window.config.url.markers}_markers_/${this.options.icon}.png`; + + // this._container.classList.add('Marker', 'mapMarker'); + this._container = document.createElement('div'); + this._container.classList.add('leaflet-div-icon'); + this._container.style.backgroundColor = 'pink'; + this._container.style.width = '16px'; + this._container.style.height = '16px'; + + if(this.options.className) { + this._container.classList.add(this.options.className); + } + + img.classList.add('markerIcon', `markerIcon${this.options.dimensions.join('x')}`); + img.src = url; + + label.classList.add(this.options.showLabel ? 'markerName-show' : 'markerName'); + label.classList.add(/*'markerName_' + set.id,*/ `markerName${this.options.dimensions.join('x')}`); + + if (this.options.isHtml) { + label.insertAdjacentHTML('afterbegin', this.options.label); + } else { + label.textContent = this.options.label; + } + + // this._container.insertAdjacentElement('beforeend', img); + // this._container.insertAdjacentElement('beforeend', label); + + return this._container; + } + + update() { + + } +} diff --git a/src/leaflet/icon/PlayerIcon.ts b/src/leaflet/icon/PlayerIcon.ts index 66a81fb..f1aee05 100644 --- a/src/leaflet/icon/PlayerIcon.ts +++ b/src/leaflet/icon/PlayerIcon.ts @@ -1,7 +1,12 @@ -import L, {DivIconOptions} from 'leaflet'; +import L, {MarkerOptions} from 'leaflet'; import {DynmapPlayer} from "@/dynmap"; -export interface PlayerIconOptions extends DivIconOptions { +const noSkinImage: HTMLImageElement = document.createElement('img'); +noSkinImage.height = 16; +noSkinImage.width = 16; +noSkinImage.src = 'images/player.png'; + +export interface PlayerIconOptions extends MarkerOptions { smallFace: boolean, showSkinFace: boolean, showBody: boolean, @@ -37,23 +42,15 @@ export class PlayerIcon extends L.DivIcon { 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) { + this._playerImage = document.createElement('img'); + this._playerImage.classList.add(this.options.smallFace ? 'playerIconSm' : 'playerIcon'); // if (this.options.smallFace) { // getMinecraftHead(player.account, 16, head => { @@ -68,13 +65,19 @@ export class PlayerIcon extends L.DivIcon { // this._playerImage!.src = head.src; // }); // } + } else { + this._playerImage = noSkinImage.cloneNode(false) as HTMLImageElement; + this._playerImage.classList.add(this.options.smallFace ? 'playerIconSm' : 'playerIcon'); } + this._container.appendChild(this._playerImage); + this._container.appendChild(this._playerName); + 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._container.appendChild(this._playerHealth) this._playerHealthBar = document.createElement('div'); this._playerHealthBar.classList.add('playerHealth'); @@ -88,11 +91,11 @@ export class PlayerIcon extends L.DivIcon { this._playerHealthBg.classList.add('playerHealthBackground'); this._playerArmourBar.classList.add('playerArmorBackground'); - this._playerHealthBg.insertAdjacentElement('beforeend', this._playerHealthBar); - this._playerArmourBg.insertAdjacentElement('beforeend', this._playerArmourBar); + this._playerHealthBg.appendChild(this._playerHealthBar); + this._playerArmourBg.appendChild(this._playerArmourBar); - this._playerHealth.insertAdjacentElement('beforeend', this._playerHealthBg); - this._playerHealth.insertAdjacentElement('beforeend', this._playerArmourBg); + this._playerHealth.appendChild(this._playerHealthBg); + this._playerHealth.appendChild(this._playerArmourBg); this._playerHealth.hidden = true; } else { @@ -103,14 +106,20 @@ export class PlayerIcon extends L.DivIcon { } update() { + if(!this._container) { + return; + } + 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; + if(this.options.showHealth) { + 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/map.ts b/src/leaflet/map.ts new file mode 100644 index 0000000..ff99c6f --- /dev/null +++ b/src/leaflet/map.ts @@ -0,0 +1,29 @@ +import L from 'leaflet'; + +L.Map.include({ + _initControlPos: function () { + const corners: any = this._controlCorners = {}, + l = 'leaflet-', + container = this._controlContainer = + L.DomUtil.create('div', l + 'control-container', this._container); + + function createCorner(vSide: string, hSide: string) { + const className = l + vSide + ' ' + l + hSide; + + corners[`${vSide}${hSide}`] = L.DomUtil.create('div', className, container); + } + + createCorner('top', 'left'); + createCorner('top', 'bar'); + createCorner('top', 'right'); + createCorner('top', 'center'); + createCorner('bottom', 'center'); + createCorner('bottom', 'bar'); + createCorner('bottom', 'left'); + createCorner('bottom', 'right'); + }, + + getUrl() { + + } +}); \ No newline at end of file diff --git a/src/leaflet/mapType/HDMapType.ts b/src/leaflet/mapType/HDMapType.ts index c877c29..15be0df 100644 --- a/src/leaflet/mapType/HDMapType.ts +++ b/src/leaflet/mapType/HDMapType.ts @@ -19,11 +19,11 @@ export class HDMapType extends DynmapTileLayer { options.minZoom = 0; L.Util.setOptions(this, options); - this._projection = new HDProjection({ + this._projection = Object.freeze(new HDProjection({ mapToWorld: this._mapSettings.mapToWorld, worldToMap: this._mapSettings.worldToMap, nativeZoomLevels: this._mapSettings.nativeZoomLevels, - }); + })); } getTileName(coords: Coordinate) { diff --git a/src/leaflet/marker/DynmapMarker.ts b/src/leaflet/marker/DynmapMarker.ts new file mode 100644 index 0000000..3ce119c --- /dev/null +++ b/src/leaflet/marker/DynmapMarker.ts @@ -0,0 +1,25 @@ +import L, {LatLng} from "leaflet"; + +export class DynmapMarker extends L.Marker { + options: { + image: null, + showLabel: true, + label: "", + minZoom: 0, + maxZoom: Infinity, + } + + constructor(latlng: LatLng, options) { + options.icon = new LabelledIcon(player, { + smallFace: options.smallFace, + showSkinFace: options.showSkinFace, + showBody: options.showBody, + showHealth: options.showHealth, + }); + super(latlng, options); + } + + update() { + + } +} diff --git a/src/leaflet/marker/PlayerMarker.ts b/src/leaflet/marker/PlayerMarker.ts index d8c0756..fc9b325 100644 --- a/src/leaflet/marker/PlayerMarker.ts +++ b/src/leaflet/marker/PlayerMarker.ts @@ -13,9 +13,10 @@ export class PlayerMarker extends L.Marker { private _player: DynmapPlayer; constructor(player: DynmapPlayer, options: PlayerMarkerOptions) { - super(new LatLng(0,0), options); + super(new LatLng(0, 0), options); this._player = player; options.draggable = false; + options.icon = new PlayerIcon(player, { smallFace: options.smallFace, showSkinFace: options.showSkinFace, @@ -24,6 +25,24 @@ export class PlayerMarker extends L.Marker { }); L.Util.setOptions(this, options); - //this._latlng = toLatLng(latlng); //TODO + } + + getIcon(): PlayerIcon { + return this.options.icon as PlayerIcon; + } + + panTo() { + if (!this._map) { + return; + } + + this._map.panTo(this.getLatLng(), { + animate: false, + noMoveStart: true, + }); + } + + _resetZIndex() { + //Don't chnage the zindex } } diff --git a/src/leaflet/projection/DynmapProjection.ts b/src/leaflet/projection/DynmapProjection.ts index e5d2038..4977389 100644 --- a/src/leaflet/projection/DynmapProjection.ts +++ b/src/leaflet/projection/DynmapProjection.ts @@ -10,16 +10,16 @@ export interface DynmapProjection { export class DynmapProjection extends L.Class { - constructor(options: DynmapProjectionOptions) { + constructor(options?: DynmapProjectionOptions) { super(); L.Util.setOptions(this, options); } locationToLatLng(location: Coordinate): L.LatLng { - throw new Error("fromLocationToLatLng not implemented"); + return new L.LatLng(location.x, location.z); } latLngToLocation(latLng: L.LatLng, y: number): Coordinate { - throw new Error("fromLatLngToLocation not implemented"); + return {x: latLng.lat, y, z: latLng.lng}; } } diff --git a/src/leaflet/tileLayer/DynmapTileLayer.ts b/src/leaflet/tileLayer/DynmapTileLayer.ts index 8a496ab..08b0203 100644 --- a/src/leaflet/tileLayer/DynmapTileLayer.ts +++ b/src/leaflet/tileLayer/DynmapTileLayer.ts @@ -9,17 +9,30 @@ export interface DynmapTileLayerOptions extends TileLayerOptions { export interface DynmapTileLayer extends L.TileLayer { options: DynmapTileLayerOptions; - _projection: any; - _mapSettings: any; - _cachedTileUrls: any; - _namedTiles: any; + _projection: DynmapProjection; + _mapSettings: DynmapMap; + _cachedTileUrls: Map; + _namedTiles: Map; + _tileTemplate: DynmapTileElement; + _loadQueue: DynmapTileElement[]; + _loadingTiles: Set; locationToLatLng(location: Coordinate): L.LatLng; latLngToLocation(latLng: L.LatLng): Coordinate; } -export interface DynmapTile extends HTMLImageElement { +export interface DynmapTile { + active?: boolean; + coords: Coords; + current: boolean; + el: DynmapTileElement; + loaded?: Date; + retain?: boolean; + complete: boolean; +} + +export interface DynmapTileElement extends HTMLImageElement { tileName: string; } @@ -36,8 +49,10 @@ export interface TileInfo { } export class DynmapTileLayer extends L.TileLayer { + constructor(options: DynmapTileLayerOptions) { super('', options); + L.Util.setOptions(this, options); if (options.mapSettings === null) { throw new TypeError("mapSettings missing"); @@ -45,9 +60,22 @@ export class DynmapTileLayer extends L.TileLayer { this._projection = new DynmapProjection({}); this._mapSettings = options.mapSettings; - this._cachedTileUrls = {}; - this._namedTiles = {}; - L.Util.setOptions(this, options); + this._cachedTileUrls = Object.seal(new Map()); + this._namedTiles = Object.seal(new Map()); + this._loadQueue = []; + this._loadingTiles = Object.seal(new Set()); + + this._tileTemplate = L.DomUtil.create('img', 'leaflet-tile') as DynmapTileElement; + this._tileTemplate.style.width = this._tileTemplate.style.height = this.options.tileSize + 'px'; + this._tileTemplate.alt = ''; + this._tileTemplate.tileName = ''; + this._tileTemplate.setAttribute('role', 'presentation'); + + Object.seal(this._tileTemplate); + + if(this.options.crossOrigin || this.options.crossOrigin === '') { + this._tileTemplate.crossOrigin = this.options.crossOrigin === true ? '' : this.options.crossOrigin; + } } getTileName(coords: Coordinate): string { @@ -56,20 +84,20 @@ export class DynmapTileLayer extends L.TileLayer { getTileUrl(coords: Coordinate) { const tileName = this.getTileName(coords); - let url = this._cachedTileUrls[tileName]; + let url = this._cachedTileUrls.get(tileName); if (!url) { const path = escape(`${this._mapSettings.world.name}/${tileName}`); url = `${window.config.url.tiles}${path}`; - this._cachedTileUrls[tileName] = url; + this._cachedTileUrls.set(tileName, url); } return url; } updateNamedTile(name: string) { - const tile = this._namedTiles[name]; - delete this._cachedTileUrls[name]; + const tile = this._namedTiles.get(name); + this._cachedTileUrls.delete(name); if (tile) { //tile.src = this._cachedTileUrls[name] = this.getTileUrl(name); @@ -77,32 +105,68 @@ export class DynmapTileLayer extends L.TileLayer { } createTile(coords: Coords, done: DoneCallback) { - const tile = super.createTile.call(this, coords, done) as DynmapTile, - name = this.getTileName(coords); + //Clone template image instead of creating a new one + const tile = this._tileTemplate.cloneNode(false) as DynmapTileElement; - tile.tileName = name; + tile.tileName = this.getTileName(coords); + tile.dataset.src = this.getTileUrl(coords); - // console.log("Adding " + tile.tileName); - this._namedTiles[name] = tile; + this._namedTiles.set(tile.tileName, tile); + this._loadQueue.push(tile); + + //Use addEventListener here + tile.onload = () => { + this._tileOnLoad(done, tile); + this._loadingTiles.delete(tile); + this._tickLoadQueue(); + }; + tile.onerror = () => { + this._tileOnError(done, tile, {name: 'Error', message: 'Error'}); + this._loadingTiles.delete(tile); + this._tickLoadQueue(); + }; + + this._tickLoadQueue(); return tile; } + _tickLoadQueue() { + if (this._loadingTiles.size > 4) { + return; + } + + const tile = this._loadQueue.shift(); + + if (!tile) { + return; + } + + this._loadingTiles.add(tile); + tile.src = tile.dataset.src as string; + } + // 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]; + tile = this._tiles[i] as DynmapTile; 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]; + if (tile.loaded && tile.el && tile.el.tileName) { + this._namedTiles.delete(tile.el.tileName); } + + if(this._loadQueue.includes(tile.el)) { + this._loadQueue.splice(this._loadQueue.indexOf(tile.el), 1); + } + + this._loadingTiles.delete(tile.el); } } @@ -110,18 +174,25 @@ export class DynmapTileLayer extends L.TileLayer { } _removeTile(key: string) { - const tile = this._tiles[key]; + const tile = this._tiles[key] as DynmapTile; if (!tile) { return; } - const tileName = (tile.el as DynmapTile).tileName; + const tileName = tile.el.tileName as string; if (tileName) { - // console.log("Removing " + tileName); - delete this._namedTiles[tileName]; - delete this._cachedTileUrls[tileName]; + this._namedTiles.delete(tileName); + this._cachedTileUrls.delete(tileName); + this._loadingTiles.delete(tile.el); + + if(this._loadQueue.includes(tile.el)) { + this._loadQueue.splice(this._loadQueue.indexOf(tile.el), 1); + } + + tile.el.onerror = null; + tile.el.onload = null; } // @ts-ignore diff --git a/src/main.ts b/src/main.ts index cd4f023..58a2319 100644 --- a/src/main.ts +++ b/src/main.ts @@ -4,10 +4,10 @@ import {store} from "@/store"; import 'leaflet/dist/leaflet.css'; import 'normalize-scss/sass/normalize/_import-now.scss'; -import './assets/css/dynmap_style.css'; -import './assets/css/rtgame.css'; +import '@/scss/style.scss'; +// import './assets/css/rtgame.css'; const app = createApp(App).use(store); -app.config.performance = true; +// app.config.performance = true; app.mount('#mcmap'); \ No newline at end of file diff --git a/src/scss/_placeholders.scss b/src/scss/_placeholders.scss new file mode 100644 index 0000000..738b6b5 --- /dev/null +++ b/src/scss/_placeholders.scss @@ -0,0 +1,39 @@ +%button { + appearance: none; + border: none; + box-shadow: none; + background-color: $global-background; + color: $global-text-color; + border-radius: $global-border-radius; + cursor: pointer; + display: block; + + &:hover, &.active { + background-color: #cccccc; + color: #222222; + } + + &:focus { + outline-color: #cccccc; + outline-width: 0.2rem; + } + + &:active { + background-color: #eeeeee; + color: #121212; + } +} + +%panel { + background-color: $global-background; + color: $global-text-color; + border-radius: $global-border-radius; + display: flex; + flex-direction: column; + padding: 1.5rem; + position: relative; + + h2:first-child { + margin-top: 0; + } +} \ No newline at end of file diff --git a/src/scss/_variables.scss b/src/scss/_variables.scss index e69de29..b8061f6 100644 --- a/src/scss/_variables.scss +++ b/src/scss/_variables.scss @@ -0,0 +1,4 @@ +$global-text-color: #cccccc; +$global-border-radius: 1rem; +$global-background: #222222; +$global-focus-color: #cccccc; \ No newline at end of file diff --git a/src/scss/leaflet/_controls.scss b/src/scss/leaflet/_controls.scss new file mode 100644 index 0000000..e05ad0f --- /dev/null +++ b/src/scss/leaflet/_controls.scss @@ -0,0 +1,164 @@ +.leaflet-control { + background-color: $global-background; + border-radius: $global-border-radius; + margin: 0; + box-sizing: border-box; + overflow: hidden; + + a, button { + @extend %button; + } +} + +.leaflet-bar { + display: flex; + align-items: center; + padding: 0; + border: none; + + a { + border-radius: 0; + border-bottom: 0.1rem solid #333333; + } +} + +.leaflet-touch { + .leaflet-bar, .leaflet-control-layers { + border: none; + } + + .leaflet-control-layers-toggle { + width: 5rem; + height: 5rem; + } + + .leaflet-top { + .leaflet-bar a { + height: 5rem; + width: 5rem; + line-height: 5rem; + + &:first-child { + border-top-left-radius: 1rem; + border-top-right-radius: 1rem; + } + + &:last-child { + border-bottom-left-radius: 1rem; + border-bottom-right-radius: 1rem; + } + } + } +} + +.leaflet-control-link { + @extend %button; + width: 5rem; + height: 5rem; + background: $global-background url('../assets/images/link.png') no-repeat center; + background-size: 3.2rem 3.2rem; +} + +.leaflet-control-coordinates { + display: flex; + align-items: center; + padding: 0.5rem 1.5rem; + + .value { + font-size: 1.6rem; + line-height: 1; + font-family: monospace; + white-space: pre; + + &[data-label]:before { + content: attr(data-label); + display: block; + line-height: 1; + margin-bottom: 0.5rem; + font-size: 1.4rem; + font-family: Raleway, sans-serif;; + } + + & + .value { + margin-left: 2rem; + } + } +} + +.leaflet-control-layers { + width: 5rem; + border: none; +} + +.leaflet-control-logo { + width: 5rem; + height: 5rem; + + a { + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + } +} + +.leaflet-top, .leaflet-bottom, +.leaflet-left, .leaflet-right { + display: flex; +} + +.leaflet-left { + padding-left: 1rem; + + .leaflet-control { + margin: 0; + } +} + +.leaflet-top { + padding-top: 1rem; + flex-direction: column; + + .leaflet-control { + order: 2; + min-width: 5rem; + + & + .leaflet-control { + margin-top: 1rem; + } + } + + .leaflet-bar { + flex-direction: column; + + a { + width: 5rem; + height: 5rem; + line-height: 5rem; + } + } + + .leaflet-control-logo { + order: 1; + margin-bottom: 1rem; + margin-top: 0 !important; + + & + .leaflet-control-logo { + margin-top: 1rem !important; + } + } +} + +.leaflet-bottom { + padding-bottom: 1rem; + + &.leaflet-left .leaflet-control + .leaflet-control { + margin-left: 1rem; + } +} + +.leaflet-control-layers-toggle { + width: 5rem; + height: 5rem; +} \ No newline at end of file diff --git a/src/scss/leaflet/_popups.scss b/src/scss/leaflet/_popups.scss new file mode 100644 index 0000000..fc45c4b --- /dev/null +++ b/src/scss/leaflet/_popups.scss @@ -0,0 +1,13 @@ +.leaflet-popup-content-wrapper, .leaflet-popup-tip { + background-color: $global-background; + color: $global-text-color; +} + +.leaflet-popup-content-wrapper { + border-radius: $global-border-radius; +} + +.leaflet-popup-content { + margin: 1.5rem; + word-break: break-all; +} \ No newline at end of file diff --git a/src/scss/style.scss b/src/scss/style.scss new file mode 100644 index 0000000..16c29a7 --- /dev/null +++ b/src/scss/style.scss @@ -0,0 +1,658 @@ +/* TILE DEBUGGING */ +/*.leaflet-tile { + margin: -1; + border: 1px solid red; +}*/ +@import "variables"; +@import "placeholders"; +@import "leaflet/controls"; +@import "leaflet/popups"; + +@font-face { + font-family: 'Raleway'; + font-style: normal; + font-weight: 400; + src: local('Raleway'), local('Raleway-Regular'), url(https://fonts.gstatic.com/s/raleway/v14/1Ptug8zYS_SKggPNyC0ITw.woff2) format('woff2'); + unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD; +} + +* { + scrollbar-width: thin; + scrollbar-color: $global-text-color transparent; +} + +*::-webkit-scrollbar { + width: 1.2rem; +} + +*::-webkit-scrollbar-track { + background: transparent; +} + +*::-webkit-scrollbar-thumb { + background-color: #333333; + border-radius: 2rem; + transition: background 1s ease-in; +} + +*:hover::-webkit-scrollbar-thumb { + background-color: $global-focus-color; +} + +*::-webkit-scrollbar-button { + display: none; +} + +html { + font-size: .625em; +} + +body { + font-family: Raleway, sans-serif; + color: $global-text-color; + text-shadow: 0.1rem 0.1rem #000000; + letter-spacing: 0.02rem; + font-size: 1.2em; +} + +html, body { + background-color: $global-background; + height: 100%; +} + +h1, h2, h3, h4, h5, h6 { + font-weight: normal; + margin-top: 0; +} + +h1 { + font-size: 3rem; +} + +h2 { + font-size: 2rem; + line-height: 2.4rem; + margin-bottom: 1rem; +} + +button { + @extend %button; +} + +.dynmap { + height: 100%; +} + +/* End of new things */ + +.dynmap .map .tile img, img { + image-rendering: pixelated; +} + + +/******************* + * Map Setup + */ + +/* Map Controls */ +.gmnoprint{ + margin-top:-75px; + margin-left:-20px; +} + + +/******************* + * Alerts are pretty. + */ + +.alertbox { + position: fixed; + width: 50%; + z-index: 999; + + top: 0; + left: 0; + right: 0; + + text-align: center; + font-size: 16px; + font-weight: bold; + + color: #fff; + background: #c00; + + border-color: #a00; + + margin: auto; + padding: 8px; +} + + +/******************* + * shared rules + */ + +.dynmap ul, .dynmap li { + list-style: none; + + padding: 0; + margin: 0; +} + +.maplist li a, +.playerlist li a { + outline: none; + text-decoration: none; +} + +.alertbox, +.largeclock { + border-style: solid; + border-width: 0 1px 1px 1px; + + + + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.4); + border-radius: 0 0 3px 3px; +} + +/******************* + * sidebar panels + */ + + + + +/******************* + * Sidebar clock style + */ +/* +.dynmap .clock { + display: inline-block; + height: 16px; + z-index:50; + + font-weight: bold; + background-repeat: no-repeat; + + padding-left: 20px; + margin-left: 8px; +} +*/ +.largeclock.digitalclock { + text-align: center; + font-size: 50px; + font-weight: bold; +} + +.digitalclock { + text-align: center; + font-size: 20px; + font-weight:bold; +} + +.digitalclock.night { + /* background-image: url(../assets/images/clock_night.png); */ + color: #dff; +} + +.digitalclock.day { + /* background-image: url(../assets/images/clock_day.png); */ + color: #fd3; +} + +.digitalclock.night, .digitalclock.day { + transition: color 8s 8s linear; +} + + +/******************* + * Large clock style + */ + +.largeclock { + position: absolute; + top: 0; + left: 0; + right: 0; + border-color: rgba(0,0,0,0.5); + width: 150px; + height: 60px; + background: rgba(0,0,0,0.6); + z-index:50; + + margin: auto; +} + +.timeofday { + background-repeat: no-repeat; +} + +.timeofday.sun { + background-image: url(../assets/images/sun.png); +} + +.timeofday.moon { + background-image: url(../assets/images/moon.png); +} + +.timeofday.digitalclock { + position: relative; + bottom: 25px; +} + +/******************* + * Clock weather style + */ + +.weather { + position: absolute; + top: 5px; + right: 0; + width: 32px; + height: 32px; + display: block; + background-repeat: no-repeat; +} + +.weather.sunny_day { + background-image: url(../assets/images/weather_sunny_day.png); +} + +.weather.stormy_day { + background-image: url(../assets/images/weather_stormy_day.png); +} +.weather.thunder_day { + background-image: url(../assets/images/weather_thunder_day.png); +} + +.weather.sunny_night { + background-image: url(../assets/images/weather_sunny_night.png); +} + +.weather.stormy_night { + background-image: url(../assets/images/weather_stormy_night.png); +} +.weather.thunder_night { + background-image: url(../assets/images/weather_thunder_night.png); +} + +/******************* + * players on the map + */ + +/* smooth player movements (contrib from KillahKiwi) */ +.dynmap .playerMarker { + transition: transform 0.3s ease-in 0s; +} + +.dynmap .playerIcon { + margin-top: -16px; + margin-left: -16px; + width: 32px; + height: 32px; +} + +.dynmap .playerIconSm { + margin-top: -8px; + margin-left: -8px; + width: 16px; + height: 16px; +} + +.dynmap .playerName { + position: absolute; + top: -19px; + left: 18px; + z-index:20; + + white-space: nowrap; + + color: $global-text-color; + background: #121212; + padding: 0.2rem; +} + +.dynmap .playerNameSm { + position: absolute; + top: -16px; + left: 10px; + + white-space: nowrap; + + color: $global-text-color; + background: #121212; + padding: 0.2rem; +} + +.dynmap .playerNameNoHealth { + top: -7px; +} + +.dynmap .healthContainer { + display: block; + position: absolute; + top: 1px; + left: 18px; + + width: 50px; + + background: rgba(0,0,0,0.6); + padding: 2px; + + border-radius: 3px; + + z-index: 21; +} + +.dynmap .healthContainerSm { + display: block; + position: absolute; + top: -2px; + left: 10px; + + width: 50px; + + background: rgba(0,0,0,0.6); + padding: 2px; + + border-radius: 3px; +} + +.dynmap .playerHealth { + height: 7px; + + background: url(../assets/images/heart.png) repeat-x left center; +} + +.dynmap .playerHealthBackground { + height: 7px; + width: 50px; + + background: url(../assets/images/heart_depleted.png) repeat-x left center; +} + +.dynmap .playerArmor { + height: 7px; + + background: url(../assets/images/armor.png) repeat-x left center; +} + +.dynmap .playerArmorBackground { + height: 7px; + width: 50px; + + background: url(../assets/images/armor_depleted.png) repeat-x left center; +} + + +/******************* + * Compass + */ + +.compass, .compass_NE, .compass_SE, .compass_NW, .compass_SW { + display: block; + position: absolute; + z-index: 10; + top: 20px; + right: 32px; + height: 84px; + width: 83px; + background-repeat: no-repeat; +} + +.compass, .compass_SE { + background-image: url(../assets/images/compass.png); +} + +.compass_NE { + background-image: url(../assets/images/compass_NE.png); +} + +.compass_NW { + background-image: url(../assets/images/compass_NW.png); +} + +.compass_SW { + background-image: url(../assets/images/compass_SW.png); +} + +.compass_flat, .compass_N, .compass_E, .compass_W, .compass_S { + top: 10px; + right: 21px; + + height: 105px; + width: 105px; +} + +.compass_flat, .compass_S { + background-image: url(../assets/images/compass_flat.png); +} + +.compass_N { + background-image: url(../assets/images/compass_N.png); +} + +.compass_E { + background-image: url(../assets/images/compass_E.png); +} + +.compass_W { + background-image: url(../assets/images/compass_W.png); +} + +.mobilecompass { + top: 5px; + right: 10px; + height: 42px; + width: 42px; + background-size: cover; +} + +/******************* + * Chat + */ + +.chat { + position: absolute; + bottom: 0; + left: 32px; + z-index:50; + + border-color: rgba(0,0,0,0.5); + background: rgba(0,0,0,0.6); + + border-style: solid; + border-width: 1px 1px 0 1px; + + border-radius: 3px 3px 0 0; + + margin-left: 10px; +} + +.chatinput { + + width: 608px; + height: 16px; + + outline: none; + color: #fff; + border: 0; + background: rgba(0, 0, 0, 0.6) url(../assets/images/chat_cursor.png) no-repeat 1px center; + + margin: 4px; + padding: 1px 1px 1px 15px; +} + +.chatsendbutton { + background-color: #bbb; +} + +.loginbutton { + color: #000; + font-family: sans-serif; + font-size: 11px; + border: 1px solid rgba(128,128,128,0.6); + background-color: #bbb; + padding: 2px; + border-radius: 5px; + cursor: pointer; + margin: 0; +} + +.messagelist { + color: white; + overflow: hidden; + + width: 622px; + max-height: 6em; + + margin: 4px 4px 0 4px; + padding: 1px; +} + +.scrollback:hover { + overflow-y: auto !important; +} + + +.messagerow { + position: relative; + max-height: 200px; + + left: 0; + bottom: 0; + + color: #fff; + font-weight: bold; +} + +.messageicon { + position: relative; + top: 1px; + left: 0; +} + +.messagetext { + position: relative; + top: -3px; + left: 0; +} + +.leaflet-popup { + color: black; +} + +.balloonmessage { + word-wrap: break-word; +} + +/* Marker styles */ +.dynmap .mapMarker .markerName { + display: none; + z-index: 101; +} + +.dynmap .mapMarker:hover .markerName, +.dynmap .mapMarker .markerName-show { + display: block; + position: absolute; + z-index: 16; + + white-space: nowrap; + + color: #fff; + background: rgba(0,0,0,0.6); + padding: 2px; + + border-radius: 3px; +} + +.dynmap .mapMarker .markerName16x16 { + top: -6px; + left: 10px; +} + +.dynmap .mapMarker .markerName8x8 { + top: -4px; + left: 6px; +} + +.dynmap .mapMarker .markerName32x32 { + top: -8px; + left: 18px; +} + +.dynmap .mapMarker .markerIcon16x16 { + transform: translate(-50%, -50%); + width: 16px; + height: 16px; +} + +.dynmap .mapMarker .markerIcon8x8 { + transform: translate(-50%, -50%); + width: 8px; + height: 8px; +} + +.dynmap .mapMarker .markerIcon32x32 { + transform: translate(-50%, -50%); + width: 32px; + height: 32px; +} + +.dynmap .mapMarker .markerName_offline_players { + font-style: italic; +} + +/* Login/register panel */ +.dynmaplogin { + text-align: center; + width: 100%; + font-weight: bold; + color: #FFFFFF; + background: #000000; +} + +table.loginregister { + color: #ffffff; + border: 1px solid rgba(64,64,64,0.6); + background: #bbb; + font-weight: bold; + margin: auto; +} + +td.login { + vertical-align: top; + color: #000000; + background-color: #bbb; + border: 1px solid rgba(64,64,64,0.6); + font-weight: bold; + margin: 2em; + width: 40em; +} + +td.register { + vertical-align: top; + color: #000000; + background-color: #bbb; + border: 1px solid rgba(64,64,64,0.6); + font-weight: bold; + margin: 2em; + width: 40em; +} + +div.statusmessage { + color: #FF0000; + font-weight: bold; + font-size: 24px; + +} + +.logincontainer { + background-color: rgba(0,0,0,0.0); +} + +.pinnedloginbutton { + margin-right: 201px; +} diff --git a/src/store/action-types.ts b/src/store/action-types.ts index cdb0b41..78b36c4 100644 --- a/src/store/action-types.ts +++ b/src/store/action-types.ts @@ -1,4 +1,6 @@ export enum ActionTypes { LOAD_CONFIGURATION = "loadConfiguration", GET_UPDATE = "getUpdate", + GET_MARKER_SETS = "getMarkerSets", + SET_PLAYERS = "setPlayers", } \ No newline at end of file diff --git a/src/store/actions.ts b/src/store/actions.ts index db3a3a7..2866f98 100644 --- a/src/store/actions.ts +++ b/src/store/actions.ts @@ -4,7 +4,7 @@ import {State} from "@/store/state"; import {ActionTypes} from "@/store/action-types"; import API from '@/api'; import {Mutations} from "@/store/mutations"; -import {DynmapConfigurationResponse, DynmapUpdateResponse} from "@/dynmap"; +import {DynmapConfigurationResponse, DynmapMarkerSet, DynmapPlayer, DynmapUpdateResponse} from "@/dynmap"; type AugmentedActionContext = { commit( @@ -20,6 +20,13 @@ export interface Actions { [ActionTypes.GET_UPDATE]( {commit}: AugmentedActionContext, ):Promise + [ActionTypes.GET_MARKER_SETS]( + {commit}: AugmentedActionContext, + ):Promise> + [ActionTypes.SET_PLAYERS]( + {commit}: AugmentedActionContext, + payload: Set + ):Promise> } export const actions: ActionTree & Actions = { @@ -32,28 +39,63 @@ export const actions: ActionTree & Actions = { if(config.config.defaultWorld && config.config.defaultMap) { commit(MutationTypes.SET_CURRENT_MAP, { - world: config.config.defaultWorld, - map: config.config.defaultMap + worldName: config.config.defaultWorld, + mapName: config.config.defaultMap }); } return config; }); }, - [ActionTypes.GET_UPDATE]({commit, state}) { + [ActionTypes.GET_UPDATE]({commit, dispatch, state}) { if(!state.currentWorld) { return Promise.reject("No current world"); } return API.getUpdate(state.updateRequestId, state.currentWorld.name, state.updateTimestamp.getUTCMilliseconds()).then(update => { - commit(MutationTypes.SET_PLAYERS, update.players); - commit(MutationTypes.SET_TIME_OF_DAY, update.timeOfDay); - commit(MutationTypes.SET_RAINING, update.raining); - commit(MutationTypes.SET_THUNDERING, update.thundering); + commit(MutationTypes.SET_WORLD_STATE, update.worldState); commit(MutationTypes.SET_UPDATE_TIMESTAMP, new Date(update.timestamp)); commit(MutationTypes.INCREMENT_REQUEST_ID, undefined); - return update; + return dispatch(ActionTypes.SET_PLAYERS, update.players).then(() => { + return update; + }); + }); + }, + [ActionTypes.SET_PLAYERS]({commit, state}, players: Set) { + const keep: Set = new Set(); + + for(const player of players) { + keep.add(player.account); + } + + //Remove any players that aren't in the set + commit(MutationTypes.SYNC_PLAYERS, keep); + + const processQueue = (players: Set, resolve: Function) => { + commit(MutationTypes.SET_PLAYERS_ASYNC, players); + + if(!players.size) { + resolve(); + } else { + requestAnimationFrame(() => processQueue(players, resolve)); + } + } + + //Set players every frame until done + return new Promise((resolve) => { + requestAnimationFrame(() => processQueue(players, resolve)); + }); + }, + [ActionTypes.GET_MARKER_SETS]({commit, state}) { + if(!state.currentWorld) { + return Promise.reject("No current world"); + } + + return API.getMarkerSets(state.currentWorld.name).then(markerSets => { + commit(MutationTypes.SET_MARKER_SETS, markerSets); + + return markerSets; }); } } \ No newline at end of file diff --git a/src/store/getters.ts b/src/store/getters.ts index e3c1965..41bafeb 100644 --- a/src/store/getters.ts +++ b/src/store/getters.ts @@ -3,10 +3,20 @@ import {State} from "@/store/state"; export type Getters = { playerMarkersEnabled(state: State): boolean; + coordinatesControlEnabled(state: State): boolean; + clockControlEnabled(state: State): boolean; } export const getters: GetterTree & Getters = { playerMarkersEnabled(state: State): boolean { return state.components.playerMarkers !== undefined; + }, + + coordinatesControlEnabled(state: State): boolean { + return state.components.coordinatesControl !== undefined; + }, + + clockControlEnabled(state: State): boolean { + return state.components.clockControl !== undefined; } } \ No newline at end of file diff --git a/src/store/index.ts b/src/store/index.ts index 42607c6..7a5dfae 100644 --- a/src/store/index.ts +++ b/src/store/index.ts @@ -1,7 +1,7 @@ import { createStore, Store as VuexStore, - createLogger, + // createLogger, CommitOptions, DispatchOptions, } from 'vuex'; @@ -37,7 +37,7 @@ export const store = createStore({ mutations, getters, actions, - plugins: [createLogger()], + // plugins: [createLogger()], }); // define your own `useStore` composition function diff --git a/src/store/mutation-types.ts b/src/store/mutation-types.ts index 193c962..4d98dcd 100644 --- a/src/store/mutation-types.ts +++ b/src/store/mutation-types.ts @@ -3,12 +3,16 @@ export enum MutationTypes { SET_MESSAGES = 'setMessages', SET_WORLDS = 'setWorlds', SET_COMPONENTS = 'setComponents', + SET_MARKER_SETS = 'setMarkerSets', ADD_WORLD = 'addWorld', - SET_TIME_OF_DAY = 'setTimeOfDay', - SET_RAINING = 'setRaining', - SET_THUNDERING = 'setThundering', + SET_WORLD_STATE = 'setWorldState', SET_UPDATE_TIMESTAMP = 'setUpdateTimestamp', INCREMENT_REQUEST_ID = 'incrementRequestId', SET_PLAYERS = 'setPlayers', + SET_PLAYERS_ASYNC = 'setPlayersAsync', + SYNC_PLAYERS = 'syncPlayers', SET_CURRENT_MAP = 'setCurrentMap', + SET_CURRENT_PROJECTION = 'setCurrentProjection', + FOLLOW_PLAYER = 'followPlayer', + CLEAR_FOLLOW = 'clearFollow', } \ No newline at end of file diff --git a/src/store/mutations.ts b/src/store/mutations.ts index 1a897dc..7d3a1fd 100644 --- a/src/store/mutations.ts +++ b/src/store/mutations.ts @@ -1,11 +1,19 @@ import {MutationTree} from "vuex"; import {MutationTypes} from "@/store/mutation-types"; import {State} from "@/store/state"; -import {DynmapComponentConfig, DynmapMessageConfig, DynmapPlayer, DynmapServerConfig, DynmapWorld} from "@/dynmap"; +import { + DynmapComponentConfig, DynmapMarkerSet, + DynmapMessageConfig, + DynmapPlayer, + DynmapServerConfig, + DynmapWorld, + DynmapWorldState +} from "@/dynmap"; +import {DynmapProjection} from "@/leaflet/projection/DynmapProjection"; export type CurrentMapPayload = { - world: string - map: string + worldName: string; + mapName: string; } export type Mutations = { @@ -13,40 +21,43 @@ export type Mutations = { [MutationTypes.SET_MESSAGES](state: S, messages: DynmapMessageConfig): void [MutationTypes.SET_WORLDS](state: S, worlds: Array): void [MutationTypes.SET_COMPONENTS](state: S, worlds: DynmapComponentConfig): void + [MutationTypes.SET_MARKER_SETS](state: S, worlds: Map): void [MutationTypes.ADD_WORLD](state: S, world: DynmapWorld): void - [MutationTypes.SET_TIME_OF_DAY](state: S, time: number): void - [MutationTypes.SET_RAINING](state: S, raining: boolean): void - [MutationTypes.SET_THUNDERING](state: S, thundering: boolean): void + [MutationTypes.SET_WORLD_STATE](state: S, worldState: DynmapWorldState): void [MutationTypes.SET_UPDATE_TIMESTAMP](state: S, time: Date): void [MutationTypes.INCREMENT_REQUEST_ID](state: S): void - [MutationTypes.SET_PLAYERS](state: S, players: Array): void + // [MutationTypes.SET_PLAYERS](state: S, players: Array): void + [MutationTypes.SET_PLAYERS_ASYNC](state: S, players: Set): void + [MutationTypes.SYNC_PLAYERS](state: S, keep: Set): void [MutationTypes.SET_CURRENT_MAP](state: S, payload: CurrentMapPayload): void + [MutationTypes.SET_CURRENT_PROJECTION](state: S, payload: DynmapProjection): void + [MutationTypes.FOLLOW_PLAYER](state: S, payload: DynmapPlayer): void + [MutationTypes.CLEAR_FOLLOW](state: S, a?: void): void } export const mutations: MutationTree & Mutations = { + // Sets configuration options from the initial config fetch [MutationTypes.SET_CONFIGURATION](state: State, config: DynmapServerConfig) { state.configuration = Object.assign(state.configuration, config); }, + + //Set messsages from the initial config fetch [MutationTypes.SET_MESSAGES](state: State, messages: DynmapMessageConfig) { state.messages = Object.assign(state.messages, messages); }, + //Sets the list of worlds, and their settings, from the initial config fetch [MutationTypes.SET_WORLDS](state: State, worlds: Array) { state.worlds.clear(); state.maps.clear(); - state.configuration.followMap = undefined; - state.configuration.followZoom = undefined; - state.configuration.defaultMap = undefined; - state.configuration.defaultWorld = undefined; - state.currentMap = undefined; state.currentWorld = undefined; state.following = undefined; - state.timeOfDay = 0; - state.raining = false; - state.thundering = false; + state.currentWorldState.timeOfDay = 0; + state.currentWorldState.raining = false; + state.currentWorldState.thundering = false; worlds.forEach(world => { state.worlds.set(world.name, world); @@ -54,45 +65,74 @@ export const mutations: MutationTree & Mutations = { }); }, + //Sets the state and settings of optional compontents, from the initial config fetch [MutationTypes.SET_COMPONENTS](state: State, components: DynmapComponentConfig) { state.components = components; }, + //Sets the existing marker sets from the last marker fetch + [MutationTypes.SET_MARKER_SETS](state: State, markerSets: Map) { + state.markerSets = markerSets; + }, + [MutationTypes.ADD_WORLD](state: State, world: DynmapWorld) { state.worlds.set(world.name, world); }, - [MutationTypes.SET_TIME_OF_DAY](state: State, time: number) { - if (time < 0 || time > 24000) { - throw new RangeError("Time must be between 0 and 24000"); - } - - state.timeOfDay = time; - }, - - [MutationTypes.SET_RAINING](state: State, raining: boolean) { - state.raining = raining; - }, - - [MutationTypes.SET_THUNDERING](state: State, thundering: boolean) { - state.thundering = thundering; + //Sets the current world state from the last update fetch + [MutationTypes.SET_WORLD_STATE](state: State, worldState: DynmapWorldState) { + state.currentWorldState = Object.assign(state.currentWorldState, worldState); }, + //Sets the timestamp of the last update fetch [MutationTypes.SET_UPDATE_TIMESTAMP](state: State, timestamp: Date) { state.updateTimestamp = timestamp; }, + //Increments the request id for the next update fetch [MutationTypes.INCREMENT_REQUEST_ID](state: State) { state.updateRequestId++; }, - [MutationTypes.SET_PLAYERS](state: State, players: Array) { - const existingPlayers: Set = new Set(); + // [MutationTypes.SET_PLAYERS](state: State, players: Array) { + // const existingPlayers: Set = new Set(); + // + // players.forEach(player => { + // existingPlayers.add(player.account); + // + // if (state.players.has(player.account)) { + // const existing = state.players.get(player.account); + // + // existing!.health = player.health; + // existing!.armor = player.armor; + // existing!.location = Object.assign(existing!.location, player.location); + // existing!.name = player.name; + // existing!.sort = player.sort; + // } else { + // state.players.set(player.account, { + // account: player.account, + // health: player.health, + // armor: player.armor, + // location: player.location, + // name: player.name, + // sort: player.sort, + // }); + // } + // }); + // + // for (const key of state.players.keys()) { + // if (!existingPlayers.has(key)) { + // state.players.delete(key); + // } + // } + // }, - players.forEach(player => { - existingPlayers.add(player.account); + //Set up to 10 players at once, returning the rest for future setting + [MutationTypes.SET_PLAYERS_ASYNC](state: State, players: Set) { + let count = 0; - if (state.players.has(player.account)) { + for(const player of players) { + if(state.players.has(player.account)) { const existing = state.players.get(player.account); existing!.health = player.health; @@ -110,27 +150,54 @@ export const mutations: MutationTree & Mutations = { sort: player.sort, }); } - }); - for (const key of state.players.keys()) { - if (!existingPlayers.has(key)) { + players.delete(player); + + if(++count >= 10) { + return players; + } + } + }, + + //Removes all players not found in the provided keep set + [MutationTypes.SYNC_PLAYERS](state: State, keep: Set) { + for(const [key, player] of state.players) { + if(!keep.has(player.account)) { state.players.delete(key); } } }, - [MutationTypes.SET_CURRENT_MAP](state: State, {world, map}) { - const mapName = [world, map].join('_'); + [MutationTypes.SET_CURRENT_MAP](state: State, {worldName, mapName}) { + mapName = [worldName, mapName].join('_'); - if(!state.worlds.has(world)) { - throw new RangeError(`Unknown world ${world}`); + if(!state.worlds.has(worldName)) { + throw new RangeError(`Unknown world ${worldName}`); } if(!state.maps.has(mapName)) { - throw new RangeError(`Unknown map ${map}`); + throw new RangeError(`Unknown map ${mapName}`); + } + + const newWorld = state.worlds.get(worldName); + + if(state.currentWorld !== newWorld) { + state.currentWorld = state.worlds.get(worldName); + state.markerSets.clear(); } - state.currentWorld = state.worlds.get(world); state.currentMap = state.maps.get(mapName); }, + + [MutationTypes.SET_CURRENT_PROJECTION](state: State, projection) { + state.currentProjection = projection; + }, + + [MutationTypes.FOLLOW_PLAYER](state: State, player: DynmapPlayer) { + state.following = player; + }, + + [MutationTypes.CLEAR_FOLLOW](state: State) { + state.following = undefined; + } } \ No newline at end of file diff --git a/src/store/state.ts b/src/store/state.ts index efc14d8..6aac7cb 100644 --- a/src/store/state.ts +++ b/src/store/state.ts @@ -1,11 +1,12 @@ import { DynmapComponentConfig, - DynmapMap, + DynmapMap, DynmapMarker, DynmapMarkerSet, DynmapMessageConfig, DynmapPlayer, DynmapServerConfig, - DynmapWorld + DynmapWorld, DynmapWorldState } from "@/dynmap"; +import {DynmapProjection} from "@/leaflet/projection/DynmapProjection"; export type State = { configuration: DynmapServerConfig; @@ -15,16 +16,15 @@ export type State = { worlds: Map; maps: Map; players: Map; + markerSets: Map; following?: DynmapPlayer; // currentServer?: string; + currentWorldState: DynmapWorldState; currentWorld?: DynmapWorld; currentMap?: DynmapMap; - - raining: boolean; - thundering: boolean; - timeOfDay: number; + currentProjection: DynmapProjection; updateRequestId: number; updateTimestamp: Date; @@ -62,22 +62,43 @@ export const state: State = { anonymousQuit: '', }, - worlds: new Map(), - maps: new Map(), - players: new Map(), + worlds: new Map(), //Defined (loaded) worlds with maps from configuration.json + maps: new Map(), //Defined maps from configuration.json + players: new Map(), //Online players from world.json + markerSets: new Map(), //Markers from world_markers.json. Contents of each set isn't reactive for performance reasons. + //Dynmap optional components components: { + // "markers" component. Only used for default showLabels settings + markers: { + showLabels: false, + }, + // Optional "playermarkers" component. Settings for online player markers. + // If not present, player markers will be disabled playerMarkers: undefined, - }, - raining: false, - thundering: false, - timeOfDay: 0, + //Optional "coords" component. Adds control showing coordinates on map mouseover + coordinatesControl: undefined, + + //Optional clock component. Used for both "digitalclock" and "timeofdayclock". Shows world time/weather. + clockControl: undefined, + + //Optional "link" component. Adds button to get url for current position + linkControl: false, + + logoControls: [], + }, following: undefined, currentWorld: undefined, + currentWorldState: { + raining: false, + thundering: false, + timeOfDay: 0, + }, currentMap: undefined, + currentProjection: new DynmapProjection(), //Projection for converting location <-> latlg. Object itself isn't reactive for performance reasons updateRequestId: 0, updateTimestamp: new Date(),