diff --git a/.github/workflows/azure-static-web-apps-lively-pebble-0b97b301e.yml b/.github/workflows/azure-static-web-apps-lively-pebble-0b97b301e.yml
new file mode 100644
index 0000000..e35c4be
--- /dev/null
+++ b/.github/workflows/azure-static-web-apps-lively-pebble-0b97b301e.yml
@@ -0,0 +1,46 @@
+name: Azure Static Web Apps CI/CD
+
+on:
+ push:
+ branches:
+ - maps
+ pull_request:
+ types: [opened, synchronize, reopened, closed]
+ branches:
+ - maps
+
+jobs:
+ build_and_deploy_job:
+ if: github.event_name == 'push' || (github.event_name == 'pull_request' && github.event.action != 'closed')
+ runs-on: ubuntu-latest
+ name: Build and Deploy Job
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ submodules: true
+ lfs: false
+ - name: Build And Deploy
+ id: builddeploy
+ uses: Azure/static-web-apps-deploy@v1
+ with:
+ azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LIVELY_PEBBLE_0B97B301E }}
+ repo_token: ${{ secrets.GITHUB_TOKEN }} # Used for Github integrations (i.e. PR comments)
+ action: "upload"
+ ###### Repository/Build Configurations - These values can be configured to match your app requirements. ######
+ # For more information regarding Static Web App workflow configurations, please visit: https://aka.ms/swaworkflowconfig
+ app_location: "/" # App source code path
+ api_location: "" # Api source code path - optional
+ output_location: "build" # Built app content directory - optional
+ ###### End of Repository/Build Configurations ######
+
+ close_pull_request_job:
+ if: github.event_name == 'pull_request' && github.event.action == 'closed'
+ runs-on: ubuntu-latest
+ name: Close Pull Request Job
+ steps:
+ - name: Close Pull Request
+ id: closepullrequest
+ uses: Azure/static-web-apps-deploy@v1
+ with:
+ azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_LIVELY_PEBBLE_0B97B301E }}
+ action: "close"
diff --git a/package.json b/package.json
index 99062f7..163663b 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "lynn",
- "version": "22.2.az",
+ "version": "23.0.0",
"private": true,
"dependencies": {
"@date-io/dayjs": "^2.16.0",
diff --git a/public/assets/maps/bsf.png b/public/assets/maps/bsf.png
new file mode 100644
index 0000000..75e9085
Binary files /dev/null and b/public/assets/maps/bsf.png differ
diff --git a/public/assets/maps/hydatos.png b/public/assets/maps/hydatos.png
new file mode 100644
index 0000000..dc2d67c
Binary files /dev/null and b/public/assets/maps/hydatos.png differ
diff --git a/public/assets/maps/markers/aetheryte.png b/public/assets/maps/markers/aetheryte.png
new file mode 100644
index 0000000..fb10cf1
Binary files /dev/null and b/public/assets/maps/markers/aetheryte.png differ
diff --git a/public/assets/maps/markers/baportal.png b/public/assets/maps/markers/baportal.png
new file mode 100644
index 0000000..ddac20a
Binary files /dev/null and b/public/assets/maps/markers/baportal.png differ
diff --git a/public/assets/maps/markers/box.png b/public/assets/maps/markers/box.png
new file mode 100644
index 0000000..517a268
Binary files /dev/null and b/public/assets/maps/markers/box.png differ
diff --git a/public/assets/maps/markers/bsfFragment.png b/public/assets/maps/markers/bsfFragment.png
new file mode 100644
index 0000000..fe4b226
Binary files /dev/null and b/public/assets/maps/markers/bsfFragment.png differ
diff --git a/public/assets/maps/markers/coffer.png b/public/assets/maps/markers/coffer.png
new file mode 100644
index 0000000..aa7fa1a
Binary files /dev/null and b/public/assets/maps/markers/coffer.png differ
diff --git a/public/assets/maps/markers/elemental.png b/public/assets/maps/markers/elemental.png
new file mode 100644
index 0000000..f6cba43
Binary files /dev/null and b/public/assets/maps/markers/elemental.png differ
diff --git a/public/assets/maps/markers/fate-boss.png b/public/assets/maps/markers/fate-boss.png
new file mode 100644
index 0000000..7589454
Binary files /dev/null and b/public/assets/maps/markers/fate-boss.png differ
diff --git a/public/assets/maps/markers/fate-defense.png b/public/assets/maps/markers/fate-defense.png
new file mode 100644
index 0000000..6b298a6
Binary files /dev/null and b/public/assets/maps/markers/fate-defense.png differ
diff --git a/public/assets/maps/markers/fate-duel.png b/public/assets/maps/markers/fate-duel.png
new file mode 100644
index 0000000..9e1294f
Binary files /dev/null and b/public/assets/maps/markers/fate-duel.png differ
diff --git a/public/assets/maps/markers/fate-enemies.png b/public/assets/maps/markers/fate-enemies.png
new file mode 100644
index 0000000..6004a29
Binary files /dev/null and b/public/assets/maps/markers/fate-enemies.png differ
diff --git a/public/assets/maps/markers/fate-gather.png b/public/assets/maps/markers/fate-gather.png
new file mode 100644
index 0000000..88b7254
Binary files /dev/null and b/public/assets/maps/markers/fate-gather.png differ
diff --git a/public/assets/maps/markers/fate-nm.png b/public/assets/maps/markers/fate-nm.png
new file mode 100644
index 0000000..09cba96
Binary files /dev/null and b/public/assets/maps/markers/fate-nm.png differ
diff --git a/public/assets/maps/markers/fate-wave.png b/public/assets/maps/markers/fate-wave.png
new file mode 100644
index 0000000..cae3ae1
Binary files /dev/null and b/public/assets/maps/markers/fate-wave.png differ
diff --git a/public/assets/maps/markers/fieldNote.png b/public/assets/maps/markers/fieldNote.png
new file mode 100644
index 0000000..fe2b4af
Binary files /dev/null and b/public/assets/maps/markers/fieldNote.png differ
diff --git a/public/assets/maps/markers/garleanFabric.png b/public/assets/maps/markers/garleanFabric.png
new file mode 100644
index 0000000..c9eb966
Binary files /dev/null and b/public/assets/maps/markers/garleanFabric.png differ
diff --git a/public/assets/maps/markers/logos-manipulator.png b/public/assets/maps/markers/logos-manipulator.png
new file mode 100644
index 0000000..4bd1124
Binary files /dev/null and b/public/assets/maps/markers/logos-manipulator.png differ
diff --git a/public/assets/maps/markers/lostfindscache.png b/public/assets/maps/markers/lostfindscache.png
new file mode 100644
index 0000000..d4c25e7
Binary files /dev/null and b/public/assets/maps/markers/lostfindscache.png differ
diff --git a/public/assets/maps/markers/magia-melder.png b/public/assets/maps/markers/magia-melder.png
new file mode 100644
index 0000000..ffadfa8
Binary files /dev/null and b/public/assets/maps/markers/magia-melder.png differ
diff --git a/public/assets/maps/markers/mob.png b/public/assets/maps/markers/mob.png
new file mode 100644
index 0000000..4904033
Binary files /dev/null and b/public/assets/maps/markers/mob.png differ
diff --git a/public/assets/maps/markers/poi-pin.png b/public/assets/maps/markers/poi-pin.png
new file mode 100644
index 0000000..f95ba85
Binary files /dev/null and b/public/assets/maps/markers/poi-pin.png differ
diff --git a/public/assets/maps/markers/quest.png b/public/assets/maps/markers/quest.png
new file mode 100644
index 0000000..1b82ec2
Binary files /dev/null and b/public/assets/maps/markers/quest.png differ
diff --git a/public/assets/maps/markers/repair.png b/public/assets/maps/markers/repair.png
new file mode 100644
index 0000000..9c0f61c
Binary files /dev/null and b/public/assets/maps/markers/repair.png differ
diff --git a/public/assets/maps/markers/shop.png b/public/assets/maps/markers/shop.png
new file mode 100644
index 0000000..e19e28a
Binary files /dev/null and b/public/assets/maps/markers/shop.png differ
diff --git a/public/assets/maps/markers/skirmish.png b/public/assets/maps/markers/skirmish.png
new file mode 100644
index 0000000..633c53b
Binary files /dev/null and b/public/assets/maps/markers/skirmish.png differ
diff --git a/public/assets/maps/markers/trader.png b/public/assets/maps/markers/trader.png
new file mode 100644
index 0000000..2fb83a8
Binary files /dev/null and b/public/assets/maps/markers/trader.png differ
diff --git a/src/App.jsx b/src/App.jsx
index 5020145..f4f3339 100644
--- a/src/App.jsx
+++ b/src/App.jsx
@@ -24,6 +24,7 @@ import ChangelogComponent from './ChangelogComponent';
import DRSNewHolsterMainComponent from './drs/DRSNewHolsterMainComponent';
import DRSRunHolsterCreatorContainerComponent from './drs/create/DRSRunHolsterCreatorContainerComponent';
import BAMorbolMapComponent from './BAMorbolMapComponent';
+import MapPageComponent from './map/MapPageComponent';
export const ColorModeContext = React.createContext({ toggleColorMode: () => { } });
@@ -104,6 +105,15 @@ function WrappedMainComponent({ component, page }) {
colorModeContext={ColorModeContext}
/>
);
+ } if (page === 'map') {
+ return (
+
+ );
}
return (
@@ -194,6 +204,16 @@ const router = createBrowserRouter([
},
],
},
+ {
+ path: '/map',
+ element: } page="map" />,
+ children: [
+ {
+ path: '/map/:mapId',
+ element: } page="map" />,
+ },
+ ],
+ },
{
path: '/weather-finder',
element: } page="weatherfinder" />,
diff --git a/src/BAPortalMapComponent.jsx b/src/BAPortalMapComponent.jsx
index 6be7048..d7f9e9f 100644
--- a/src/BAPortalMapComponent.jsx
+++ b/src/BAPortalMapComponent.jsx
@@ -1,10 +1,11 @@
import React from 'react';
import { Box } from '@mui/material';
import { Helmet } from 'react-helmet';
+import MapPageComponent from './map/MapPageComponent';
export default function BAPortalMapComponent() {
return (
-
+
@@ -24,7 +25,10 @@ export default function BAPortalMapComponent() {
BA Portal Map - forays.info
-
+
);
diff --git a/src/MainComponent.jsx b/src/MainComponent.jsx
index 5d40ac2..5cc49f0 100644
--- a/src/MainComponent.jsx
+++ b/src/MainComponent.jsx
@@ -15,6 +15,7 @@ export default function MainComponent({
encodedHolster,
lostAction,
logosAction,
+ mapId,
colorModeContext,
}) {
/**
@@ -83,11 +84,12 @@ export default function MainComponent({
encodedHolster,
lostAction,
logosAction,
+ mapId,
resetTimer,
})}
-
+ { (page !== 'map' && page !== 'portals') ? : null}
diff --git a/src/SidebarComponent.jsx b/src/SidebarComponent.jsx
index 2ebe69b..f3c18b6 100644
--- a/src/SidebarComponent.jsx
+++ b/src/SidebarComponent.jsx
@@ -41,6 +41,7 @@ function SidebarComponent({
const handleSidebarClickFromSidebar = useCallback((event, item) => {
navigate(`/${item}`);
+
handleSidebarClick();
}, [navigate, handleSidebarClick]);
@@ -85,92 +86,122 @@ function SidebarComponent({
- EUREKA AND BA
+ OCCULT CRESCENT
{ handleSidebarClickFromSidebar(e, 'ba'); }}
+ disabled
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'oc'); }}
key={uuidv4()}
>
- BA Loadouts
+ Coming soon!
+
+
+ BOZJA AND DRS
+
+
{ handleSidebarClickFromSidebar(e, 'portals'); }}
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'drs/holster'); }}
key={uuidv4()}
>
- BA Portal Map
+ DRS Holsters
{ handleSidebarClickFromSidebar(e, 'eureka/logos'); }}
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'drs/holster/c'); }}
key={uuidv4()}
>
- Logos Action Info
+ Holster Creator
{ handleSidebarClickFromSidebar(e, 'eureka/loadout'); }}
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'map/bsf'); }}
key={uuidv4()}
>
- Loadout Creator
+ Bozja Maps
handleSidebarForecastClickFromSidebar(e, ResultsFilter.EUREKA_NMS)}
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'bozja/lostaction'); }}
key={uuidv4()}
>
- NM Spawn Times
+ Lost Action Info
handleSidebarForecastClickFromSidebar(e, ResultsFilter.EUREKA_FARMS)}
+ onClick={(e) => handleSidebarForecastClickFromSidebar(e, ResultsFilter.FRAGMENT_FARM)}
key={uuidv4()}
>
- Logogram/Box Farm Times
+ Fragment Farm Times
- BOZJA AND DRS
+ EUREKA AND BA
{ handleSidebarClickFromSidebar(e, 'drs/holster'); }}
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'ba'); }}
key={uuidv4()}
>
- DRS Holsters
+ BA Loadouts
{ handleSidebarClickFromSidebar(e, 'drs/holster/c'); }}
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'portals'); }}
key={uuidv4()}
>
- Holster Creator
+ BA Portal Map
{ handleSidebarClickFromSidebar(e, 'bozja/lostaction'); }}
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'map/hydatos'); }}
key={uuidv4()}
>
- Lost Action Info
+ Eureka Maps
handleSidebarForecastClickFromSidebar(e, ResultsFilter.FRAGMENT_FARM)}
+ onClick={(e) => { handleSidebarClickFromSidebar(e, 'eureka/logos'); }}
key={uuidv4()}
>
- Fragment Farm Times
+ Logos Action Info
+
+
+ { handleSidebarClickFromSidebar(e, 'eureka/loadout'); }}
+ key={uuidv4()}
+ >
+
+ Loadout Creator
+
+
+ handleSidebarForecastClickFromSidebar(e, ResultsFilter.EUREKA_NMS)}
+ key={uuidv4()}
+ >
+
+ NM Spawn Times
+
+
+ handleSidebarForecastClickFromSidebar(e, ResultsFilter.EUREKA_FARMS)}
+ key={uuidv4()}
+ >
+
+ Logogram/Box Farm Times
diff --git a/src/TimeAndWeatherPopoverComponent.jsx b/src/TimeAndWeatherPopoverComponent.jsx
index f29a5d1..6731988 100644
--- a/src/TimeAndWeatherPopoverComponent.jsx
+++ b/src/TimeAndWeatherPopoverComponent.jsx
@@ -86,6 +86,7 @@ export default function TimeAndWeatherPopoverComponent() {
spacing={1}
alignItems="center"
justifyContent="center"
+ key={uuidv4()}
>
{name}
@@ -97,33 +98,35 @@ export default function TimeAndWeatherPopoverComponent() {
alignItems="center"
>
{conditions.map((item, index) => (
-
-
- {dayjs(item.time.getTime())
- .format('h:mm:ss A')}
-
-
- {
+
+
+ {dayjs(item.time.getTime())
+ .format('h:mm:ss A')}
+
+
+ {
new EorzeaTime(
dayjs(item.time.getTime())
.toDate(),
)
.getHours().toLocaleString('en-US', { minimumIntegerDigits: 2 })
}
- :
- {
+ :
+ {
new EorzeaTime(
dayjs(item.time.getTime())
.toDate(),
)
.getMinutes().toLocaleString('en-US', { minimumIntegerDigits: 2 })
}
- {' '}
- ET
-
-
+ {' '}
+ ET
+
+
)}
+ key={uuidv4()}
>
{
+ handleMouseMove(e);
+ },
+ });
+}
+
+export default function FullscreenMapComponent({
+ mapData,
+ mapParameters,
+ displayLabels,
+ selectedLayers,
+ handleMouseMove,
+}) {
+ const markers = [];
+ const annotations = [];
+
+ // Add all json markers to a flat markers array to display on the map
+ Object.keys(mapData).forEach((markerType) => {
+ if (selectedLayers.includes(markerType)) {
+ // Push all markers of this type to the markers array
+ /**
+ * Most options for these markers are set in the map's json file.
+ * - markerIcon: The icon image displayed. Can be overridden for a specific marker with
+ * iconOverride.
+ * - circle: If present, draws a circle around the marker.
+ * (e.g. FATE/Skirmish/CE/NM boundaries)
+ * - position: Corresponds to the x/y values in-game. Since we're using leaflet (and have
+ * to pretend that we're using latitude/longitude values), the y value needs to be
+ * provided as negative.
+ *
+ * TODO: Eventually we'll add support for actual tooltips, but for now it just shows
+ * the marker's name.
+ * NOTE: For region names, the marker will be slightly offset due to lacking an icon.
+ */
+ markers.push(...(mapData[markerType].waymarks.map((marker) => (
+
+
+
+
+
+ {displayLabels ? marker.name : ''}
+
+ {
+ mapData[markerType].circle && (
+
+ )
+ }
+
+ ))));
+
+ /**
+ * Add annotations, if they exist. These are things drawn on the map that aren't associated
+ * with a specific marker.
+ */
+ if (mapData[markerType].annotations) {
+ annotations.push(...mapData[markerType].annotations.map((annotation) => {
+ switch (annotation.type) {
+ case 'polyline':
+ return (
+ (
+ [point.y, point.x]
+ ))}
+ />
+ );
+ case 'largeText':
+ // TODO: Adjust font size with zoom. Or make these images, maybe?
+ return (
+ ${annotation.text}`,
+ iconSize: [0, 0],
+ iconAnchor: [0, 0],
+ popupAnchor: [0, 0],
+ tooltipAnchor: [0, 0],
+ })}
+ />
+ );
+ default:
+ return null;
+ }
+ }));
+ }
+ }
+ });
+
+ return (
+
+
+
+ {markers}
+ {annotations}
+
+ );
+}
diff --git a/src/map/MapContainerComponent.css b/src/map/MapContainerComponent.css
new file mode 100644
index 0000000..ee04a8c
--- /dev/null
+++ b/src/map/MapContainerComponent.css
@@ -0,0 +1,65 @@
+.full-screen-map {
+ height: 100%;
+}
+
+.leaflet-tooltip {
+ background-color: transparent !important;
+ border: 0px !important;
+ box-shadow: none !important;
+ font-size:14px;
+ font-weight: 700;
+ color: #fff !important;
+ text-shadow: 1px 0 0 #000, 0 -1px 0 #000, 0 1px 0 #000, -1px 0 0 #000;
+}
+
+.leaflet-tooltip-left::before {
+ display: none !important;
+}
+
+.leaflet-tooltip-right::before {
+ display: none !important;
+}
+
+.map-container {
+ position: relative;
+ flex-grow: 1;
+ touch-action: manipulation;
+}
+
+.map-layer-selector {
+ position: absolute;
+ right: 20px;
+ z-index: 1000;
+ padding: 10px;
+ padding-left: 15px;
+ border-radius: 10px;
+ width: 280px;
+ opacity: 0.8;
+}
+
+.map-zone-selector {
+ position: absolute;
+ right: 20px;
+ z-index: 1000;
+ padding: 10px;
+ padding-left: 15px;
+ border-radius: 10px;
+ width: 320px;
+ opacity: 0.8;
+}
+
+.layer-selector-button {
+ position: absolute;
+ top: 20px;
+ right: 20px;
+ z-index: 1010;
+}
+
+.mouse-coordinates {
+ position: absolute;
+ bottom: 20px;
+ left: 20px;
+ z-index: 1010;
+ padding: 10px;
+ border-radius: 10px;
+}
diff --git a/src/map/MapContainerComponent.jsx b/src/map/MapContainerComponent.jsx
new file mode 100644
index 0000000..0d1f88e
--- /dev/null
+++ b/src/map/MapContainerComponent.jsx
@@ -0,0 +1,165 @@
+import React, { useCallback, useMemo } from 'react';
+import {
+ Box,
+ Button,
+ Stack,
+ Typography,
+ useTheme,
+} from '@mui/material';
+import LayersIcon from '@mui/icons-material/Layers';
+import MapIcon from '@mui/icons-material/Map';
+import ExpandLessIcon from '@mui/icons-material/ExpandLess';
+import ExpandMoreIcon from '@mui/icons-material/ExpandMore';
+import CheckBoxIcon from '@mui/icons-material/CheckBox';
+import CheckBoxOutlineBlankIcon from '@mui/icons-material/CheckBoxOutlineBlank';
+import AbcIcon from '@mui/icons-material/Abc';
+import './MapContainerComponent.css';
+
+import FullscreenMapComponent from './FullscreenMapComponent';
+import MapLayerSelectorComponent from './MapLayerSelectorComponent';
+
+// TODO: Attempt to lazy-load these
+import bsfMapData from './lib/poi/bsf.json';
+import hydMapData from './lib/poi/hydatos.json';
+import MapZoneSelectorComponent from './MapZoneSelectorComponent';
+// import MapData from './MapData';
+
+export default function MapContainerComponent({ mapId, inputSelectedLayers }) {
+ const theme = useTheme();
+
+ // TODO: Have a mapping so we can use multiple identifiers for each zone
+ const mapData = {};
+ mapData.hydatos = hydMapData;
+ mapData.bsf = bsfMapData;
+
+ // const mapDataManager = MapData.getInstance();
+
+ const [selectedMapId, setSelectedMapId] = React.useState(mapId ?? 'hydatos');
+
+ const initialSelectedLayers = inputSelectedLayers
+ ?? mapData[selectedMapId].layers.map((layer) => layer.id);
+ const [selectedLayers, setSelectedLayers] = React.useState(initialSelectedLayers);
+ const [displayLayerSelector, setDisplayLayerSelector] = React.useState(false);
+ const [displayZoneSelector, setDisplayZoneSelector] = React.useState(false);
+ const [displayLabels, setDisplayLabels] = React.useState(true);
+ const [mouseCoordinates, setMouseCoordinates] = React.useState({ lat: 1, lon: -1 });
+ const availableLayers = mapData[selectedMapId].categories;
+
+ const handleLayerSelectorUpdate = useCallback((newLayers) => {
+ setSelectedLayers(newLayers);
+ }, [selectedLayers, setSelectedLayers]);
+
+ const handleZoneSelectorUpdate = useCallback((data) => {
+ setSelectedMapId(data);
+
+ // Close selector
+ setDisplayZoneSelector(false);
+
+ // Update current href
+ window.history.pushState(
+ {},
+ 'FFXIV Field Operations Assistant - forays.info',
+ `/map/${data}`,
+ );
+ }, [setSelectedMapId, setDisplayZoneSelector]);
+
+ const handleMouseMove = useCallback((e) => {
+ setMouseCoordinates({ lat: e.latlng.lat, lon: e.latlng.lng });
+ }, [setMouseCoordinates]);
+
+ const displayMap = useMemo(() => (
+
+ ), [displayLabels, selectedMapId, selectedLayers, handleLayerSelectorUpdate, handleMouseMove]);
+
+ return (
+
+ { displayMap }
+ {
+ displayLayerSelector
+ && (
+
+ )
+ }
+ {
+ displayZoneSelector
+ && (
+
+ )
+ }
+
+
+ : }
+ endIcon={}
+ onClick={() => {
+ setDisplayLabels(!displayLabels);
+ }}
+ />
+ : }
+ endIcon={}
+ onClick={() => {
+ setDisplayZoneSelector(!displayZoneSelector);
+ setDisplayLayerSelector(false);
+ }}
+ >
+
+ { mapData[selectedMapId].name }
+
+
+ : }
+ endIcon={}
+ onClick={() => {
+ setDisplayZoneSelector(false);
+ setDisplayLayerSelector(!displayLayerSelector);
+ }}
+ >
+ {
+ /* { displayLayerSelector ? 'Close' : 'Change Layers' } */
+ ''
+ }
+
+
+
+
+ {`${mouseCoordinates.lon.toFixed(1)}, ${(mouseCoordinates.lat * -1.0).toFixed(1)}`}
+
+
+
+ );
+}
+
+/**
+ * Some notes on this:
+ * - We'll need a layer selector. Probably also need a map switcher.
+ * - Need to add support for regions as well as points, for CE/FATE boundaries and mob spawn areas.
+ * - Need to make an inline version of this for the forecast/ section.
+ */
diff --git a/src/map/MapLayerSelectorComponent.jsx b/src/map/MapLayerSelectorComponent.jsx
new file mode 100644
index 0000000..405bd77
--- /dev/null
+++ b/src/map/MapLayerSelectorComponent.jsx
@@ -0,0 +1,116 @@
+import {
+ Box, Checkbox, FormControlLabel, FormGroup,
+ Typography,
+ useTheme,
+} from '@mui/material';
+import React from 'react';
+import Scrollbars from 'react-custom-scrollbars-2';
+
+export default function MapLayerSelectorComponent({
+ selectedLayers,
+ availableLayers,
+ handleLayerSelectorUpdate,
+}) {
+ const theme = useTheme();
+
+ const checkboxesToDisplay = [];
+
+ // TODO: Fix this error.
+ // eslint-disable-next-line no-restricted-syntax
+ for (const category of availableLayers) {
+ // Push parent checkbox
+ checkboxesToDisplay.push(
+ {category.name}}
+ key={category.name}
+ control={
+ (
+ selectedLayers.includes(layer.id))}
+ indeterminate={
+ category.layers.some((layer) => selectedLayers.includes(layer.id))
+ && !category.layers.every((layer) => selectedLayers.includes(layer.id))
+ }
+ disabled={!category.enabled}
+ />
+ )
+ }
+ onChange={(e) => {
+ // TODO: This doesn't work yet, need to see why
+ const newLayers = [...selectedLayers];
+ if (e.target.checked) {
+ category.layers.forEach((layer) => {
+ if (!newLayers.includes(layer.id)) {
+ newLayers.push(layer.id);
+ }
+ });
+ } else {
+ category.layers.forEach((layer) => {
+ const index = newLayers.indexOf(layer.id);
+ if (index > -1) {
+ newLayers.splice(index, 1);
+ }
+ });
+ }
+ handleLayerSelectorUpdate(newLayers);
+ }}
+ />,
+ );
+
+ // Push child checkboxes
+ const categoryLayers = category.layers.map((layer) => (
+
+ )
+ }
+ label={layer.name}
+ onChange={(e) => {
+ const newLayers = [...selectedLayers];
+ if (e.target.checked) {
+ newLayers.push(layer.id);
+ } else {
+ const index = newLayers.indexOf(layer.id);
+ if (index > -1) {
+ newLayers.splice(index, 1);
+ }
+ }
+ handleLayerSelectorUpdate(newLayers);
+ }}
+ />
+ ));
+
+ checkboxesToDisplay.push(
+
+ {categoryLayers}
+ ,
+ );
+ }
+
+ /**
+ * TODO: For xs-s, the selector should be a popover modal
+ */
+
+ return (
+
+
+
+ {checkboxesToDisplay}
+
+
+
+ );
+}
diff --git a/src/map/MapPageComponent.jsx b/src/map/MapPageComponent.jsx
new file mode 100644
index 0000000..a593ad2
--- /dev/null
+++ b/src/map/MapPageComponent.jsx
@@ -0,0 +1,18 @@
+import { Box } from '@mui/material';
+import React, { Suspense } from 'react';
+
+const MapContainerComponent = React.lazy(() => import('./MapContainerComponent'));
+
+export default function MapPageComponent({ mapId, inputSelectedLayers }) {
+ return (
+
+ Loading...}>
+
+
+
+ );
+}
diff --git a/src/map/MapZoneSelectorComponent.jsx b/src/map/MapZoneSelectorComponent.jsx
new file mode 100644
index 0000000..c6279ce
--- /dev/null
+++ b/src/map/MapZoneSelectorComponent.jsx
@@ -0,0 +1,43 @@
+import {
+ Box, FormControlLabel, FormGroup, FormLabel, Radio, RadioGroup, useTheme,
+} from '@mui/material';
+import React from 'react';
+import Scrollbars from 'react-custom-scrollbars-2';
+
+export default function MapZoneSelectorComponent({
+ currentZone,
+ handleZoneSelectorUpdate,
+}) {
+ const theme = useTheme();
+ return (
+
+
+
+ handleZoneSelectorUpdate(e.target.value)}
+ >
+ Occult Crescent
+ } label="South Horn" />
+ Bozja
+ } label="Zadnor" />
+ } label="The Bozjan Southern Front" />
+ Eureka
+ } label="Eureka Hydatos" />
+ } label="Eureka Pyros" />
+ } label="Eureka Pagos" />
+ } label="Eureka Anemos" />
+
+
+
+
+ );
+}
diff --git a/src/map/TooltipBaseComponent.css b/src/map/TooltipBaseComponent.css
new file mode 100644
index 0000000..2d6fed2
--- /dev/null
+++ b/src/map/TooltipBaseComponent.css
@@ -0,0 +1,18 @@
+.tooltip-base-component {
+ width: 520px;
+ min-height: 50px;
+}
+
+.tooltip-no-margin p {
+ margin: 0 !important;
+}
+
+/* Stronger specificity to override .leaflet-popup-content p */
+.leaflet-popup-content .tooltip-no-margin p {
+ margin: 0 !important;
+}
+
+/* Further increase specificity */
+.leaflet-popup-content :where(.tooltip-no-margin) p {
+ margin: 0 !important;
+}
\ No newline at end of file
diff --git a/src/map/TooltipBaseComponent.jsx b/src/map/TooltipBaseComponent.jsx
new file mode 100644
index 0000000..e628ed9
--- /dev/null
+++ b/src/map/TooltipBaseComponent.jsx
@@ -0,0 +1,55 @@
+import {
+ Box,
+ Divider,
+ Stack,
+ Typography,
+} from '@mui/material';
+import React from 'react';
+import EurekaNMTooltipComponent from './tooltipcomponents/EurekaNMTooltipComponent';
+import AetheryteTooltipComponent from './tooltipcomponents/AetheryteTooltipComponent';
+import BozjaCETooltipComponent from './tooltipcomponents/BozjaCETooltipComponent';
+
+export default function TooltipBaseComponent(
+ {
+ markerData,
+ icon,
+ type,
+ },
+) {
+ const renderContent = () => {
+ switch (type) {
+ case 'nms':
+ return ;
+ case 'aetherytes':
+ return ;
+ case 'criticalEngagements':
+ return ;
+ default:
+ return null;
+ }
+ };
+
+ return (
+
+
+
+
+
+
+
+ {markerData.popupTitle ?? markerData.name}
+
+
+
+
+
+ {renderContent(markerData.type, markerData, icon)}
+
+
+ );
+}
diff --git a/src/map/lib/poi/bsf.json b/src/map/lib/poi/bsf.json
new file mode 100644
index 0000000..1ff0bde
--- /dev/null
+++ b/src/map/lib/poi/bsf.json
@@ -0,0 +1,900 @@
+{
+ "name": "The Bozjan Southern Front",
+ "categories": [
+ {
+ "name": "General",
+ "enabled": true,
+ "layers": [
+ {
+ "id": "namedLocations",
+ "name": "Named Locations",
+ "enabled": true
+ },
+ {
+ "id": "aetherytes",
+ "name": "Aetherytes",
+ "enabled": true
+ },
+ {
+ "id": "lostFindCaches",
+ "name": "Lost Find Caches",
+ "enabled": true
+ },
+ {
+ "id": "traders",
+ "name": "Traders",
+ "enabled": true
+ },
+ {
+ "id": "shops",
+ "name": "Shops",
+ "enabled": true
+ },
+ {
+ "id": "repair",
+ "name": "Repair",
+ "enabled": true
+ }
+ ]
+ },
+ {
+ "name": "Encounters",
+ "enabled": true,
+ "layers": [
+ {
+ "id": "skirmishes",
+ "name": "Skirmishes",
+ "enabled": true
+ },
+ {
+ "id": "criticalEngagements",
+ "name": "Critical Engagements",
+ "enabled": true
+ },
+ {
+ "id": "duels",
+ "name": "Duels",
+ "enabled": true
+ }
+ ]
+ },
+ {
+ "name": "Enemies",
+ "enabled": false,
+ "layers": [
+ {
+ "id": "starMobs",
+ "name": "Star Mobs",
+ "enabled": false
+ },
+ {
+ "id": "magitekMobs",
+ "name": "Magitek Mobs",
+ "enabled": false
+ },
+ {
+ "id": "fragmentMobs",
+ "name": "Fragment Mobs",
+ "enabled": false
+ }
+ ]
+ },
+ {
+ "name": "Progression",
+ "enabled": false,
+ "layers": [
+ {
+ "id": "quests",
+ "name": "Quests",
+ "enabled": false
+ },
+ {
+ "id": "fieldNotes",
+ "name": "Field Notes",
+ "enabled": false
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "id": "aetherytes"
+ },
+ {
+ "id": "lostFindCaches"
+ },
+ {
+ "id": "criticalEngagements"
+ }
+ ],
+ "parameters": {
+ "imageUrl": "bsf.png",
+ "bounds": {
+ "min": {
+ "lat": -1,
+ "lon": 1
+ },
+ "max": {
+ "lat": -42,
+ "lon": 42
+ }
+ },
+ "center": {
+ "lat": -21,
+ "lon": 21
+ },
+ "zoom": {
+ "default": 5,
+ "min": 3,
+ "max": 8,
+ "delta": 0.5,
+ "snap": 0.5,
+ "scrollPx": 200
+ }
+ },
+ "mapData": {
+ "namedLocations": {
+ "markerIcon": "poi-pin.png",
+ "waymarks": [
+ {
+ "name": "Orphaned Ruins",
+ "@id": "bsf_orphanedruins",
+ "position": {
+ "x": 16,
+ "y": -17.7
+ }
+ },
+ {
+ "name": "Firelight's Coffin",
+ "@id": "bsf_firelightscoffin",
+ "position": {
+ "x": 25.2,
+ "y": -35.4
+ }
+ },
+ {
+ "name": "Castrum Lacus Litore",
+ "@id": "bsf_castrumlacuslitore",
+ "position": {
+ "x": 16.9,
+ "y": -9.4
+ }
+ },
+ {
+ "name": "The Alermuc Climb",
+ "@id": "bsf_thealermucclimb",
+ "position": {
+ "x": 19.9,
+ "y": -14.8
+ }
+ },
+ {
+ "name": "Old Bozja",
+ "@id": "bsf_oldbozja",
+ "position": {
+ "x": 20.7,
+ "y": -22.1
+ }
+ },
+ {
+ "name": "Southern Entrenchment",
+ "@id": "bsf_southernentrenchment",
+ "position": {
+ "x": 20.1,
+ "y": -28.2
+ }
+ }
+ ]
+ },
+ "lostFindCaches": {
+ "markerIcon": "lostfindscache.png",
+ "waymarks": [
+ {
+ "name": "",
+ "popupTitle": "Lost Finds Cache",
+ "@id": "bsf_lostfindcache1",
+ "position": {
+ "x": 15.1,
+ "y": -30.2
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Lost Finds Cache",
+ "@id": "bsf_lostfindcache2",
+ "position": {
+ "x": 28.9,
+ "y": -23.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Lost Finds Cache",
+ "@id": "bsf_lostfindcache3",
+ "position": {
+ "x": 13.6,
+ "y": -23.8
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Lost Finds Cache",
+ "@id": "bsf_lostfindcache4",
+ "position": {
+ "x": 22.1,
+ "y": -16.6
+ }
+ }
+ ]
+ },
+ "repair": {
+ "markerIcon": "repair.png",
+ "waymarks": [
+ {
+ "name": "",
+ "@id": "bsf_repair1",
+ "position": {
+ "x": 14.6,
+ "y": -30.6
+ }
+ }
+ ]
+ },
+ "traders": {
+ "markerIcon": "trader.png",
+ "waymarks": [
+ {
+ "name": "Resistance Quartermaster",
+ "@id": "bsf_trader01",
+ "position": {
+ "x": 14.2,
+ "y": -29.7
+ }
+ },
+ {
+ "name": "Resistance Historian",
+ "@id": "bsf_trader02",
+ "position": {
+ "x": 15.0,
+ "y": -29.1
+ }
+ },
+ {
+ "name": "Resistance Supplier",
+ "@id": "bsf_trader03",
+ "position": {
+ "x": 15.3,
+ "y": -29.9
+ }
+ },
+ {
+ "name": "Resistance Appraiser",
+ "@id": "bsf_trader04",
+ "position": {
+ "x": 15.2,
+ "y": -30.1
+ }
+ },
+ {
+ "name": "Resistance Appraiser",
+ "@id": "bsf_trader05",
+ "position": {
+ "x": 28.9,
+ "y": -23.6
+ }
+ },
+ {
+ "name": "Resistance Appraiser",
+ "@id": "bsf_trader06",
+ "position": {
+ "x": 13.8,
+ "y": -23.8
+ }
+ },
+ {
+ "name": "Resistance Appraiser",
+ "@id": "bsf_trader07",
+ "position": {
+ "x": 22.2,
+ "y": -16.6
+ }
+ }
+ ]
+ },
+ "shops": {
+ "markerIcon": "shop.png",
+ "waymarks": [
+ {
+ "name": "Resistance Locksmith",
+ "@id": "bsf_shop01",
+ "position": {
+ "x": 15.1,
+ "y": -29.7
+ }
+ }
+ ]
+ },
+ "aetherytes": {
+ "markerIcon": "aetheryte.png",
+ "waymarks": [
+ {
+ "name": "Utya's Aegis",
+ "@id": "bsf_utyasaegis",
+ "position": {
+ "x": 14.9,
+ "y": -29.9
+ }
+ },
+ {
+ "name": "Olana's Stand",
+ "@id": "bsf_olanastand",
+ "position": {
+ "x": 28.6,
+ "y": -23.6
+ }
+ },
+ {
+ "name": "Lunya's Stand",
+ "@id": "bsf_lunyasstand",
+ "position": {
+ "x": 13.7,
+ "y": -23.6
+ }
+ },
+ {
+ "name": "Camp Steva",
+ "@id": "bsf_campsteva",
+ "position": {
+ "x": 22.3,
+ "y": -16.8
+ }
+ }
+ ]
+ },
+ "skirmishes": {
+ "markerIcon": "fate-enemies.png",
+ "circle": {
+ "radius": 0.9,
+ "color": "#03c2fc"
+ },
+ "waymarks": [
+ {
+ "name": "All Pets Are Off",
+ "@id": "bsf_skirmish01",
+ "position": {
+ "x": 17,
+ "y": -26.8
+ }
+ },
+ {
+ "name": "Brought to Heal",
+ "@id": "bsf_skirmish02",
+ "position": {
+ "x": 28.6,
+ "y": -26.1
+ }
+ },
+ {
+ "name": "Can Carnivorous Plants Bloom Even on a Battlefield?",
+ "@id": "bsf_skirmish03",
+ "position": {
+ "x": 33.7,
+ "y": -29.5
+ }
+ },
+ {
+ "name": "Conflicting with the First Law",
+ "@id": "bsf_skirmish04",
+ "position": {
+ "x": 33.5,
+ "y": -29.3
+ },
+ "iconOverride": "fate-boss.png"
+ },
+ {
+ "name": "More Machine Now than Man",
+ "@id": "bsf_skirmish05",
+ "position": {
+ "x": 27.9,
+ "y": -28.9
+ }
+ },
+ {
+ "name": "None of Them Knew They Were Robots",
+ "@id": "bsf_skirmish06",
+ "position": {
+ "x": 24.5,
+ "y": -27.8
+ }
+ },
+ {
+ "name": "Seeq and Destroy",
+ "@id": "bsf_skirmish07",
+ "position": {
+ "x": 28.8,
+ "y": -26.3
+ }
+ },
+ {
+ "name": "Sneak and Spell",
+ "@id": "bsf_skirmish08",
+ "position": {
+ "x": 20.2,
+ "y": -26.9
+ }
+ },
+ {
+ "name": "The Beasts Must Die",
+ "@id": "bsf_skirmish09",
+ "position": {
+ "x": 20.4,
+ "y": -27.1
+ }
+ },
+ {
+ "name": "Unrest for the Wicked",
+ "@id": "bsf_skirmish10",
+ "position": {
+ "x": 24.3,
+ "y": -27.6
+ }
+ },
+ {
+ "name": "Heavy Boots of Lead",
+ "@id": "bsf_skirmish11",
+ "position": {
+ "x": 30.6,
+ "y": -22
+ },
+ "iconOverride": "fate-boss.png"
+ },
+ {
+ "name": "Help Wanted",
+ "@id": "bsf_skirmish12",
+ "position": {
+ "x": 18.2,
+ "y": -20.9
+ },
+ "iconOverride": "fate-defense.png"
+ },
+ {
+ "name": "No Camping Allowed",
+ "@id": "bsf_skirmish13",
+ "position": {
+ "x": 17.8,
+ "y": -23.5
+ }
+ },
+ {
+ "name": "Parts and Recreation",
+ "@id": "bsf_skirmish14",
+ "position": {
+ "x": 25.4,
+ "y": -21.9
+ },
+ "iconOverride": "fate-gather.png"
+ },
+ {
+ "name": "Pyromancer Supreme",
+ "@id": "bsf_skirmish15",
+ "position": {
+ "x": 18.2,
+ "y": -20.9
+ },
+ "iconOverride": "fate-boss.png"
+ },
+ {
+ "name": "Red (Chocobo) Alert",
+ "@id": "bsf_skirmish16",
+ "position": {
+ "x": 27,
+ "y": -18.5
+ }
+ },
+ {
+ "name": "Scavengers of Man's Sorrow",
+ "@id": "bsf_skirmish17",
+ "position": {
+ "x": 25.2,
+ "y": -22.8
+ }
+ },
+ {
+ "name": "The Element of Supplies",
+ "@id": "bsf_skirmish18",
+ "position": {
+ "x": 17.6,
+ "y": -23.3
+ }
+ },
+ {
+ "name": "The Monster Mash",
+ "@id": "bsf_skirmish19",
+ "position": {
+ "x": 30.8,
+ "y": -22.2
+ }
+ },
+ {
+ "name": "Unicorn Flakes",
+ "@id": "bsf_skirmish20",
+ "position": {
+ "x": 31.6,
+ "y": -17.3
+ },
+ "iconOverride": "fate-defense.png"
+ },
+ {
+ "name": "Demonstrably Demonic",
+ "@id": "bsf_skirmish21",
+ "position": {
+ "x": 11.3,
+ "y": -20.4
+ }
+ },
+ {
+ "name": "Desperately Seeking Something",
+ "@id": "bsf_skirmish22",
+ "position": {
+ "x": 24.5,
+ "y": -17.3
+ }
+ },
+ {
+ "name": "For Absent Friends",
+ "@id": "bsf_skirmish23",
+ "position": {
+ "x": 14.3,
+ "y": -18.2
+ }
+ },
+ {
+ "name": "I'm a Mechanical Man",
+ "@id": "bsf_skirmish24",
+ "position": {
+ "x": 20.8,
+ "y": -18
+ },
+ "iconOverride": "fate-boss.png"
+ },
+ {
+ "name": "Let Slip the Dogs of War",
+ "@id": "bsf_skirmish25",
+ "position": {
+ "x": 14.1,
+ "y": -15.7
+ }
+ },
+ {
+ "name": "Murder Death Kill",
+ "@id": "bsf_skirmish26",
+ "position": {
+ "x": 14.3,
+ "y": -15.9
+ },
+ "iconOverride": "fate-boss.png"
+ },
+ {
+ "name": "My Family and Other Animals",
+ "@id": "bsf_skirmish27",
+ "position": {
+ "x": 11.3,
+ "y": -15.2
+ }
+ },
+ {
+ "name": "Of Steel and Flame",
+ "@id": "bsf_skirmish28",
+ "position": {
+ "x": 14.5,
+ "y": -18.4
+ },
+ "iconOverride": "fate-boss.png"
+ },
+ {
+ "name": "Supplies Party",
+ "@id": "bsf_skirmish29",
+ "position": {
+ "x": 20.9,
+ "y": -14.7
+ },
+ "iconOverride": "fate-gather.png"
+ },
+ {
+ "name": "The War Against the Machines",
+ "@id": "bsf_skirmish30",
+ "position": {
+ "x": 11.5,
+ "y": -20.6
+ }
+ },
+ {
+ "name": "The Wild Bunch",
+ "@id": "bsf_skirmish31",
+ "position": {
+ "x": 21.1,
+ "y": -14.9
+ }
+ },
+ {
+ "name": "Waste the Rainbow",
+ "@id": "bsf_skirmish32",
+ "position": {
+ "x": 24.7,
+ "y": -17.5
+ },
+ "iconOverride": "fate-boss.png"
+ }
+ ]
+ },
+ "criticalEngagements": {
+ "markerIcon": "fate-nm.png",
+ "circle": {
+ "radius": 0.9,
+ "color": "#e0635a"
+ },
+ "waymarks": [
+ {
+ "name": "Kill It with Fire",
+ "@id": "bsf_ce01",
+ "position": {
+ "x": 17.2,
+ "y": -27
+ },
+ "metadata": {
+ "boss": "Peerifool",
+ "spawnedBy": {
+ "type": "skirmish",
+ "name": "All Pets Are Off"
+ },
+ "rewards": [
+ {
+ "name": "Field Notes on Clarricie",
+ "type": "fieldNote"
+ },
+ {
+ "name": "Garlean Synthetic Fabric",
+ "type": "garleanFabric"
+ }
+ ]
+ }
+ },
+ {
+ "name": "The Baying of the Hound(s)",
+ "@id": "bsf_ce02",
+ "position": {
+ "x": 22,
+ "y": -29
+ },
+ "metadata": {
+ "boss": "Canus Dirus",
+ "spawnedBy": {
+ "type": "mob",
+ "name": "4th Legion Death Claw"
+ }
+ }
+ },
+ {
+ "name": "The Shadow of Death's Hand",
+ "@id": "bsf_ce03",
+ "position": {
+ "x": 35.4,
+ "y": -26.2
+ },
+ "metadata": {
+ "boss": "Akbaba",
+ "spawnedBy": {
+ "type": "mob",
+ "name": "4th Legion Roader"
+ },
+ "rewards": [
+ {
+ "name": "Field Notes on Xeven",
+ "type": "fieldNote"
+ }
+ ]
+ }
+ },
+ {
+ "name": "Vigil for the Lost",
+ "@id": "bsf_ce04",
+ "position": {
+ "x": 27.9,
+ "y": -30
+ },
+ "metadata": {
+ "boss": "Vigilia",
+ "spawnedBy": {
+ "type": "skirmish",
+ "name": "More Machine Now than Man"
+ }
+ }
+ },
+ {
+ "name": "Patriot Games",
+ "@id": "bsf_ce05",
+ "position": {
+ "x": 14.2,
+ "y": -21.3
+ },
+ "metadata": {
+ "boss": "Patriot",
+ "spawnedBy": {
+ "type": "skirmish",
+ "name": "The Fires of War"
+ }
+ }
+ },
+ {
+ "name": "The Final Furlong",
+ "@id": "bsf_ce06",
+ "position": {
+ "x": 31.9,
+ "y": -17.6
+ },
+ "metadata": {
+ "boss": "Spartoi",
+ "spawnedBy": {
+ "type": "skirmish",
+ "name": "Unicorn Flakes"
+ },
+ "rewards": [
+ {
+ "name": "Field Notes on Llofii",
+ "type": "fieldNote"
+ }
+ ]
+ }
+ },
+ {
+ "name": "The Fires of War",
+ "@id": "bsf_ce07",
+ "position": {
+ "x": 20.6,
+ "y": -24.3
+ },
+ "iconOverride": "fate-wave.png",
+ "metadata": {
+ "boss": "Pyrobolus Mater",
+ "spawnedBy": {
+ "type": "mob",
+ "name": "4th Legion Gunship"
+ }
+ }
+ },
+ {
+ "name": "The Hunt for Red Choctober",
+ "@id": "bsf_ce08",
+ "position": {
+ "x": 26.8,
+ "y": -18.4
+ },
+ "metadata": {
+ "boss": "Red Comet",
+ "spawnedBy": {
+ "type": "skirmish",
+ "name": "Red (Chocobo) Alert"
+ }
+ }
+ },
+ {
+ "name": "Metal Fox Chaos",
+ "@id": "bsf_ce09",
+ "position": {
+ "x": 14.3,
+ "y": -18.3
+ },
+ "metadata": {
+ "boss": "Dáinsleif",
+ "spawnedBy": {
+ "type": "skirmish",
+ "name": "Of Steel and Flame"
+ },
+ "rewards": [
+ {
+ "name": "10x Forgotten Fragment of Resolve",
+ "type": "bsfFragment"
+ }
+ ]
+ }
+ },
+ {
+ "name": "Rise of the Robots",
+ "@id": "bsf_ce10",
+ "position": {
+ "x": 21,
+ "y": -17.8
+ },
+ "metadata": {
+ "boss": "Mark XIII-X Magitek Laborer",
+ "spawnedBy": {
+ "type": "skirmish",
+ "name": "I'm a Mechanical Man"
+ },
+ "rewards": [
+ {
+ "name": "Field Notes on Sicinius",
+ "type": "fieldNote"
+ }
+ ]
+ }
+ },
+ {
+ "name": "Trampled under Hoof",
+ "@id": "bsf_ce11",
+ "position": {
+ "x": 9.9,
+ "y": -18.3
+ },
+ "metadata": {
+ "boss": "Eale",
+ "spawnedBy": {
+ "type": "mob",
+ "name": "4th Legion Armored Weapon"
+ },
+ "rewards": [
+ {
+ "name": "10x Forgotten Fragment of Resolve",
+ "type": "bsfFragment"
+ }
+ ]
+ }
+ },
+ {
+ "name": "Where Strode the Behemoth",
+ "@id": "bsf_ce12",
+ "position": {
+ "x": 23.6,
+ "y": -14.9
+ },
+ "metadata": {
+ "boss": "Chlevnik",
+ "spawnedBy": {
+ "type": "skirmish",
+ "name": "Trampled under Hoof"
+ },
+ "rewards": [
+ {
+ "name": "10x Forgotten Fragment of Resolve",
+ "type": "bsfFragment"
+ }
+ ]
+ }
+ }
+ ]
+ },
+ "duels": {
+ "markerIcon": "fate-duel.png",
+ "circle": {
+ "radius": 0.5,
+ "color": "#e0635a"
+ },
+ "waymarks": [
+ {
+ "name": "Aces High",
+ "@id": "bsf_duel1",
+ "position": {
+ "x": 31.6,
+ "y": -26.8
+ }
+ },
+ {
+ "name": "Beast of Man",
+ "@id": "bsf_duel2",
+ "position": {
+ "x": 23.2,
+ "y": -20.6
+ }
+ },
+ {
+ "name": "And the Flames Went Higher",
+ "@id": "bsf_duel3",
+ "position": {
+ "x": 18.7,
+ "y": -15.9
+ }
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/map/lib/poi/hydatos.json b/src/map/lib/poi/hydatos.json
new file mode 100644
index 0000000..183dea4
--- /dev/null
+++ b/src/map/lib/poi/hydatos.json
@@ -0,0 +1,1654 @@
+{
+ "name": "Eureka Hydatos",
+ "id": "hydatos",
+ "region": "Eureka",
+ "categories": [
+ {
+ "name": "General",
+ "enabled": true,
+ "layers": [
+ {
+ "id": "namedLocations",
+ "name": "Named Locations",
+ "enabled": true
+ },
+ {
+ "id": "aetherytes",
+ "name": "Aetherytes",
+ "enabled": true
+ },
+ {
+ "id": "elementals",
+ "name": "Elementals",
+ "enabled": true
+ },
+ {
+ "id": "logosManipulators",
+ "name": "Logos Manipulators",
+ "enabled": true
+ },
+ {
+ "id": "magiaMelders",
+ "name": "Magia Melders",
+ "enabled": true
+ },
+ {
+ "id": "traders",
+ "name": "Traders",
+ "enabled": true
+ },
+ {
+ "id": "shops",
+ "name": "Shops",
+ "enabled": true
+ },
+ {
+ "id": "repair",
+ "name": "Repair",
+ "enabled": true
+ }
+ ]
+ },
+ {
+ "name": "Encounters",
+ "enabled": true,
+ "layers": [
+ {
+ "id": "nms",
+ "name": "NMs",
+ "enabled": true
+ },
+ {
+ "id": "bunnyCoffers",
+ "name": "Bunny Coffers",
+ "enabled": true
+ },
+ {
+ "id": "baPortals",
+ "name": "BA Portals",
+ "enabled": true
+ }
+ ]
+ },
+ {
+ "name": "Enemies",
+ "enabled": false,
+ "layers": [
+ {
+ "id": "mobs",
+ "name": "Mobs",
+ "enabled": false
+ },
+ {
+ "id": "nmPrepMobs",
+ "name": "NM Prep Mobs",
+ "enabled": false
+ },
+ {
+ "id": "sprites",
+ "name": "Sprites",
+ "enabled": false
+ }
+ ]
+ },
+ {
+ "name": "Progression",
+ "enabled": false,
+ "layers": [
+ {
+ "id": "quests",
+ "name": "Quests",
+ "enabled": false
+ }
+ ]
+ }
+ ],
+ "layers": [
+ {
+ "id": "namedLocations"
+ },
+ {
+ "id": "aetherytes"
+ },
+ {
+ "id": "nms"
+ }
+ ],
+ "parameters": {
+ "imageUrl": "hydatos.png",
+ "bounds": {
+ "min": {
+ "lat": -1,
+ "lon": 1
+ },
+ "max": {
+ "lat": -42,
+ "lon": 42
+ }
+ },
+ "center": {
+ "lat": -21,
+ "lon": 21
+ },
+ "zoom": {
+ "default": 5,
+ "min": 3,
+ "max": 8,
+ "delta": 0.5,
+ "snap": 0.5,
+ "scrollPx": 200
+ }
+ },
+ "mapData": {
+ "namedLocations": {
+ "markerIcon": "poi-pin.png",
+ "waymarks": [
+ {
+ "name": "The Aetherbridge Foundation",
+ "@id": "hyd_aetherbridgefoundation",
+ "position": {
+ "x": 24.0,
+ "y": -31.0
+ }
+ },
+ {
+ "name": "The Western Columns",
+ "@id": "hyd_westerncolumns",
+ "position": {
+ "x": 10.7,
+ "y": -29.7
+ }
+ },
+ {
+ "name": "The Central Columns",
+ "@id": "hyd_centralcolumns",
+ "position": {
+ "x": 20.3,
+ "y": -24.9
+ }
+ },
+ {
+ "name": "The Eastern Columns",
+ "@id": "hyd_easterncolumns",
+ "position": {
+ "x": 31.2,
+ "y": -27.3
+ }
+ },
+ {
+ "name": "The Crystal Dragon's Bloom",
+ "@id": "hyd_crystaldragonsbloom",
+ "position": {
+ "x": 32.8,
+ "y": -19.0
+ }
+ }
+ ]
+ },
+ "aetherytes": {
+ "markerIcon": "aetheryte.png",
+ "waymarks": [
+ {
+ "name": "Central Point",
+ "@id": "hyd_centralpoint",
+ "position": {
+ "x": 20.4,
+ "y": -13.6
+ },
+ "metadata": {
+ "unlock": 50
+ }
+ },
+ {
+ "name": "Unverified Research",
+ "@id": "hyd_unverifiedresearch",
+ "position": {
+ "x": 9.9,
+ "y": -28.0
+ },
+ "metadata": {
+ "unlock": 51
+ }
+ },
+ {
+ "name": "The Dormitory",
+ "@id": "hyd_dormitory",
+ "position": {
+ "x": 37.0,
+ "y": -22.6
+ },
+ "metadata": {
+ "unlock": 55
+ }
+ }
+ ]
+ },
+ "elementals": {
+ "markerIcon": "elemental.png",
+ "waymarks": [
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental01",
+ "position": {
+ "x": 5.1,
+ "y": -16.1
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental02",
+ "position": {
+ "x": 10.0,
+ "y": -14.3
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental03",
+ "position": {
+ "x": 16.0,
+ "y": -13.3
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental04",
+ "position": {
+ "x": 14.6,
+ "y": -16.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental05",
+ "position": {
+ "x": 13.0,
+ "y": -17.8
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental06",
+ "position": {
+ "x": 7.8,
+ "y": -19.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental07",
+ "position": {
+ "x": 4.4,
+ "y": -23.9
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental08",
+ "position": {
+ "x": 7.1,
+ "y": -23.9
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental09",
+ "position": {
+ "x": 5.5,
+ "y": -30.0
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental10",
+ "position": {
+ "x": 9.2,
+ "y": -29.7
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental11",
+ "position": {
+ "x": 9.8,
+ "y": -27.0
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental12",
+ "position": {
+ "x": 14.2,
+ "y": -29.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental13",
+ "position": {
+ "x": 18.2,
+ "y": -20.4
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental14",
+ "position": {
+ "x": 19.2,
+ "y": -24.3
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental15",
+ "position": {
+ "x": 22.1,
+ "y": -30.1
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental16",
+ "position": {
+ "x": 23.7,
+ "y": -27.1
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental17",
+ "position": {
+ "x": 28.6,
+ "y": -21.1
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental18",
+ "position": {
+ "x": 30.8,
+ "y": -26.1
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental19",
+ "position": {
+ "x": 29.5,
+ "y": -30.3
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental20",
+ "position": {
+ "x": 34.2,
+ "y": -16.7
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental21",
+ "position": {
+ "x": 38.2,
+ "y": -17.2
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental22",
+ "position": {
+ "x": 33.9,
+ "y": -21.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental23",
+ "position": {
+ "x": 37.9,
+ "y": -22.9
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental24",
+ "position": {
+ "x": 35.6,
+ "y": -26.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Elemental Spawn Location",
+ "@id": "hyd_elemental25",
+ "position": {
+ "x": 36.9,
+ "y": -29.7
+ }
+ }
+ ]
+ },
+ "logosManipulators": {
+ "markerIcon": "logos-manipulator.png",
+ "waymarks": [
+ {
+ "name": "Logos Manipulator",
+ "@id": "hyd_logosmanipulator1",
+ "position": {
+ "x": 21.4,
+ "y": -13.5
+ }
+ }
+ ]
+ },
+ "magiaMelders": {
+ "markerIcon": "magia-melder.png",
+ "waymarks": [
+ {
+ "name": "Magia Melder",
+ "@id": "hyd_magiamelder1",
+ "position": {
+ "x": 19.7,
+ "y": -13.8
+ }
+ }
+ ]
+ },
+ "traders": {
+ "markerIcon": "trader.png",
+ "waymarks": [
+ {
+ "name": "Trader",
+ "@id": "hyd_trader1",
+ "position": {
+ "x": 20.5,
+ "y": -13.3
+ }
+ }
+ ]
+ },
+ "shops": {
+ "markerIcon": "shop.png",
+ "waymarks": [
+ {
+ "name": "Expedition Engineer",
+ "@id": "hyd_shop1",
+ "position": {
+ "x": 19.7,
+ "y": -13.2
+ }
+ },
+ {
+ "name": "Shop",
+ "@id": "hyd_shop2",
+ "position": {
+ "x": 19.8,
+ "y": -14.0
+ }
+ },
+ {
+ "name": "Shop",
+ "@id": "hyd_shop3",
+ "position": {
+ "x": 20.0,
+ "y": -14.1
+ }
+ },
+ {
+ "name": "Expedition Alchemist",
+ "@id": "hyd_shop4",
+ "position": {
+ "x": 20.9,
+ "y": -13.3
+ }
+ },
+ {
+ "name": "Drake",
+ "@id": "hyd_shop5",
+ "position": {
+ "x": 21.3,
+ "y": -13.3
+ }
+ },
+ {
+ "name": "Eureka Weapons & Gear",
+ "@id": "hyd_shop1",
+ "position": {
+ "x": 21.4,
+ "y": -13.7
+ }
+ }
+ ]
+ },
+ "repair": {
+ "markerIcon": "repair.png",
+ "waymarks": [
+ {
+ "name": "Repair",
+ "@id": "hyd_repair1",
+ "position": {
+ "x": 19.7,
+ "y": -13.5
+ }
+ }
+ ]
+ },
+ "baPortals": {
+ "markerIcon": "baportal.png",
+ "waymarks": [
+ {
+ "name": "1-1",
+ "@id": "ba-1-1",
+ "position": {
+ "x": 4.2,
+ "y": -15.1
+ }
+ },
+ {
+ "name": "1-2",
+ "@id": "ba-1-2",
+ "position": {
+ "x": 7.2,
+ "y": -14.9
+ }
+ },
+ {
+ "name": "1-3",
+ "@id": "ba-1-3",
+ "position": {
+ "x": 10.6,
+ "y": -14.6
+ }
+ },
+ {
+ "name": "1-4",
+ "@id": "ba-1-4",
+ "position": {
+ "x": 6.9,
+ "y": -18.7
+ }
+ },
+ {
+ "name": "1-5",
+ "@id": "ba-1-5",
+ "position": {
+ "x": 10.1,
+ "y": -17.8
+ }
+ },
+ {
+ "name": "1-6",
+ "@id": "ba-1-6",
+ "position": {
+ "x": 6.2,
+ "y": -21.8
+ }
+ },
+ {
+ "name": "1-7",
+ "@id": "ba-1-7",
+ "position": {
+ "x": 4.3,
+ "y": -26.1
+ }
+ },
+ {
+ "name": "1-8",
+ "@id": "ba-1-8",
+ "position": {
+ "x": 7.2,
+ "y": -29.3
+ }
+ },
+ {
+ "name": "2-1",
+ "@id": "ba-2-1",
+ "position": {
+ "x": 15.9,
+ "y": -14.6
+ }
+ },
+ {
+ "name": "2-2",
+ "@id": "ba-2-2",
+ "position": {
+ "x": 16.4,
+ "y": -18.7
+ }
+ },
+ {
+ "name": "2-3",
+ "@id": "ba-2-3",
+ "position": {
+ "x": 10.7,
+ "y": -21.9
+ }
+ },
+ {
+ "name": "2-4",
+ "@id": "ba-2-4",
+ "position": {
+ "x": 15.9,
+ "y": -22.2
+ }
+ },
+ {
+ "name": "2-5",
+ "@id": "ba-2-5",
+ "position": {
+ "x": 13.7,
+ "y": -24.3
+ }
+ },
+ {
+ "name": "2-6",
+ "@id": "ba-2-6",
+ "position": {
+ "x": 10.6,
+ "y": -25.8
+ }
+ },
+ {
+ "name": "2-7",
+ "@id": "ba-2-7",
+ "position": {
+ "x": 14.8,
+ "y": -26.8
+ }
+ },
+ {
+ "name": "2-8",
+ "@id": "ba-2-8",
+ "position": {
+ "x": 16.7,
+ "y": -29.2
+ }
+ },
+ {
+ "name": "3-1",
+ "@id": "ba-3-1",
+ "position": {
+ "x": 21.2,
+ "y": -17.2
+ }
+ },
+ {
+ "name": "3-2",
+ "@id": "ba-3-2",
+ "position": {
+ "x": 19.4,
+ "y": -20.1
+ }
+ },
+ {
+ "name": "3-3",
+ "@id": "ba-3-3",
+ "position": {
+ "x": 23.8,
+ "y": -21.9
+ }
+ },
+ {
+ "name": "3-4",
+ "@id": "ba-3-4",
+ "position": {
+ "x": 19.2,
+ "y": -26.8
+ }
+ },
+ {
+ "name": "3-5",
+ "@id": "ba-3-5",
+ "position": {
+ "x": 23.0,
+ "y": -27.0
+ }
+ },
+ {
+ "name": "3-6",
+ "@id": "ba-3-6",
+ "position": {
+ "x": 19.2,
+ "y": -28.6
+ }
+ },
+ {
+ "name": "3-7",
+ "@id": "ba-3-7",
+ "position": {
+ "x": 20.8,
+ "y": -29.0
+ }
+ },
+ {
+ "name": "3-8",
+ "@id": "ba-3-8",
+ "position": {
+ "x": 22.9,
+ "y": -29.1
+ }
+ },
+ {
+ "name": "4-1",
+ "@id": "ba-4-1",
+ "position": {
+ "x": 25.0,
+ "y": -16.4
+ }
+ },
+ {
+ "name": "4-2",
+ "@id": "ba-4-2",
+ "position": {
+ "x": 26.6,
+ "y": -19.1
+ }
+ },
+ {
+ "name": "4-3",
+ "@id": "ba-4-3",
+ "position": {
+ "x": 28.7,
+ "y": -23.4
+ }
+ },
+ {
+ "name": "4-4",
+ "@id": "ba-4-4",
+ "position": {
+ "x": 25.2,
+ "y": -25.3
+ }
+ },
+ {
+ "name": "4-5",
+ "@id": "ba-4-5",
+ "position": {
+ "x": 26.9,
+ "y": -26.7
+ }
+ },
+ {
+ "name": "4-6",
+ "@id": "ba-4-6",
+ "position": {
+ "x": 26.0,
+ "y": -27.9
+ }
+ },
+ {
+ "name": "4-7",
+ "@id": "ba-4-7",
+ "position": {
+ "x": 25.4,
+ "y": -28.9
+ }
+ },
+ {
+ "name": "4-8",
+ "@id": "ba-4-8",
+ "position": {
+ "x": 25.7,
+ "y": -30.1
+ }
+ },
+ {
+ "name": "5-1",
+ "@id": "ba-5-1",
+ "position": {
+ "x": 28.9,
+ "y": -15.3
+ }
+ },
+ {
+ "name": "5-2",
+ "@id": "ba-5-2",
+ "position": {
+ "x": 33.3,
+ "y": -17.0
+ }
+ },
+ {
+ "name": "5-3",
+ "@id": "ba-5-3",
+ "position": {
+ "x": 29.9,
+ "y": -19.6
+ }
+ },
+ {
+ "name": "5-4",
+ "@id": "ba-5-4",
+ "position": {
+ "x": 32.3,
+ "y": -24.8
+ }
+ },
+ {
+ "name": "5-5",
+ "@id": "ba-5-5",
+ "position": {
+ "x": 29.6,
+ "y": -26.3
+ }
+ },
+ {
+ "name": "5-6",
+ "@id": "ba-5-6",
+ "position": {
+ "x": 30.8,
+ "y": -28.6
+ }
+ },
+ {
+ "name": "5-7",
+ "@id": "ba-5-7",
+ "position": {
+ "x": 30.3,
+ "y": -30.1
+ }
+ },
+ {
+ "name": "5-8",
+ "@id": "ba-5-8",
+ "position": {
+ "x": 31.7,
+ "y": -29.9
+ }
+ },
+ {
+ "name": "6-1",
+ "@id": "ba-6-1",
+ "position": {
+ "x": 35.2,
+ "y": -13.9
+ }
+ },
+ {
+ "name": "6-2",
+ "@id": "ba-6-2",
+ "position": {
+ "x": 37.9,
+ "y": -15.0
+ }
+ },
+ {
+ "name": "6-3",
+ "@id": "ba-6-3",
+ "position": {
+ "x": 36.0,
+ "y": -19.0
+ }
+ },
+ {
+ "name": "6-4",
+ "@id": "ba-6-4",
+ "position": {
+ "x": 37.3,
+ "y": -23.7
+ }
+ },
+ {
+ "name": "6-5",
+ "@id": "ba-6-5",
+ "position": {
+ "x": 33.0,
+ "y": -27.8
+ }
+ },
+ {
+ "name": "6-6",
+ "@id": "ba-6-6",
+ "position": {
+ "x": 37.2,
+ "y": -27.9
+ }
+ },
+ {
+ "name": "6-7",
+ "@id": "ba-6-7",
+ "position": {
+ "x": 32.7,
+ "y": -29.0
+ }
+ },
+ {
+ "name": "6-8",
+ "@id": "ba-6-8",
+ "position": {
+ "x": 35.8,
+ "y": -29.9
+ }
+ }
+ ],
+ "annotations": [
+ {
+ "@id": "ba-line-12",
+ "type": "polyline",
+ "path": [
+ {
+ "x": 11.0,
+ "y": -14.0
+ },
+ {
+ "x": 11.0,
+ "y": -19.0
+ },
+ {
+ "x": 9.0,
+ "y": -21.5
+ },
+ {
+ "x": 9.0,
+ "y": -30
+ }
+ ]
+ },
+ {
+ "@id": "ba-line-23",
+ "type": "polyline",
+ "path": [
+ {
+ "x": 17.8,
+ "y": -12.5
+ },
+ {
+ "x": 17.8,
+ "y": -30.1
+ }
+ ]
+ },
+ {
+ "@id": "ba-line-34",
+ "type": "polyline",
+ "path": [
+ {
+ "x": 24.1,
+ "y": -14.8
+ },
+ {
+ "x": 24.1,
+ "y": -31.2
+ }
+ ]
+ },
+ {
+ "@id": "ba-line-45",
+ "type": "polyline",
+ "path": [
+ {
+ "x": 28.0,
+ "y": -14.6
+ },
+ {
+ "x": 28.0,
+ "y": -17.0
+ },
+ {
+ "x": 29.0,
+ "y": -19.0
+ },
+ {
+ "x": 29.0,
+ "y": -30.7
+ }
+ ]
+ },
+ {
+ "@id": "ba-line-56",
+ "type": "polyline",
+ "path": [
+ {
+ "x": 34.0,
+ "y": -13.2
+ },
+ {
+ "x": 34.0,
+ "y": -25.0
+ },
+ {
+ "x": 32.3,
+ "y": -26.5
+ },
+ {
+ "x": 32.3,
+ "y": -30.6
+ }
+ ]
+ },
+ {
+ "@id": "ba-1-header",
+ "type": "largeText",
+ "text": "1",
+ "position": {
+ "x": 7.0,
+ "y": -8.0
+ }
+ },
+ {
+ "@id": "ba-2-header",
+ "type": "largeText",
+ "text": "2",
+ "position": {
+ "x": 14.0,
+ "y": -8.0
+ }
+ },
+ {
+ "@id": "ba-3-header",
+ "type": "largeText",
+ "text": "3",
+ "position": {
+ "x": 20.5,
+ "y": -8.0
+ }
+ },
+ {
+ "@id": "ba-4-header",
+ "type": "largeText",
+ "text": "4",
+ "position": {
+ "x": 25.5,
+ "y": -8.0
+ }
+ },
+ {
+ "@id": "ba-5-header",
+ "type": "largeText",
+ "text": "5",
+ "position": {
+ "x": 31.0,
+ "y": -8.0
+ }
+ },
+ {
+ "@id": "ba-6-header",
+ "type": "largeText",
+ "text": "6",
+ "position": {
+ "x": 36.5,
+ "y": -8.0
+ }
+ }
+ ]
+ },
+ "bunnyCoffers": {
+ "markerIcon": "coffer.png",
+ "waymarks": [
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer01",
+ "position": {
+ "x": 5.4,
+ "y": -12.2
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer02",
+ "position": {
+ "x": 9.4,
+ "y": -13.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer03",
+ "position": {
+ "x": 3.0,
+ "y": -16.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer04",
+ "position": {
+ "x": 8.4,
+ "y": -19.4
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer05",
+ "position": {
+ "x": 4.1,
+ "y": -22.1
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer06",
+ "position": {
+ "x": 7.8,
+ "y": -23.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer07",
+ "position": {
+ "x": 3.1,
+ "y": -27.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer08",
+ "position": {
+ "x": 5.5,
+ "y": -28.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer09",
+ "position": {
+ "x": 10.2,
+ "y": -30.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer10",
+ "position": {
+ "x": 14.4,
+ "y": -28.7
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer11",
+ "position": {
+ "x": 18.1,
+ "y": -30.0
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer12",
+ "position": {
+ "x": 17.7,
+ "y": -19.1
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer13",
+ "position": {
+ "x": 12.7,
+ "y": -17.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer14",
+ "position": {
+ "x": 15.1,
+ "y": -16.4
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer15",
+ "position": {
+ "x": 19.1,
+ "y": -13.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer16",
+ "position": {
+ "x": 23.9,
+ "y": -18.7
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer17",
+ "position": {
+ "x": 21.6,
+ "y": -23.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer18",
+ "position": {
+ "x": 24.7,
+ "y": -24.0
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer19",
+ "position": {
+ "x": 24.2,
+ "y": -31.2
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer20",
+ "position": {
+ "x": 26.1,
+ "y": -13.3
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer21",
+ "position": {
+ "x": 27.2,
+ "y": -20.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer22",
+ "position": {
+ "x": 28.9,
+ "y": -22.9
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer23",
+ "position": {
+ "x": 28.3,
+ "y": -26.6
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer24",
+ "position": {
+ "x": 30.8,
+ "y": -16.8
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer25",
+ "position": {
+ "x": 38.4,
+ "y": -13.3
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer26",
+ "position": {
+ "x": 34.6,
+ "y": -16.4
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer27",
+ "position": {
+ "x": 38.2,
+ "y": -20.5
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer28",
+ "position": {
+ "x": 34.8,
+ "y": -23.9
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer29",
+ "position": {
+ "x": 38.9,
+ "y": -26.1
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer30",
+ "position": {
+ "x": 38.4,
+ "y": -30.7
+ }
+ },
+ {
+ "name": "",
+ "popupTitle": "Bunny Coffer Spawn Location",
+ "@id": "hydatoscoffer31",
+ "position": {
+ "x": 33.1,
+ "y": -29.3
+ }
+ }
+ ]
+ },
+ "nms": {
+ "markerIcon": "fate-nm.png",
+ "circle": {
+ "radius": 0.9,
+ "color": "#03c2fc"
+ },
+ "waymarks": [
+ {
+ "name": "Lv50 Khalamari",
+ "popupTitle": "I Ink, Therefore I Am",
+ "@id": "hyd_nm1",
+ "position": {
+ "x": 10.8,
+ "y": -25.6
+ },
+ "metadata": {
+ "boss": "Khalamari",
+ "level": 50,
+ "element": "Water",
+ "spawnedBy": {
+ "name": "Xzomit",
+ "element": "Water",
+ "level": 55
+ }
+ }
+ },
+ {
+ "name": "Bunny",
+ "popupTitle": "Drink Me",
+ "@id": "hyd_nmbunny",
+ "position": {
+ "x": 13.8,
+ "y": -21.6
+ },
+ "metadata": {
+ "boss": "Bunny FATE",
+ "level": 50,
+ "element": "Water"
+ }
+ },
+ {
+ "name": "Lv51 Stegodon",
+ "popupTitle": "From Tusk till Dawn",
+ "@id": "hyd_nm2",
+ "position": {
+ "x": 10.4,
+ "y": -17.2
+ },
+ "metadata": {
+ "boss": "Stegodon",
+ "level": 51,
+ "element": "Earth",
+ "spawnedBy": {
+ "name": "Hydatos Primelephas",
+ "element": "Earth",
+ "level": 56
+ }
+ }
+ },
+ {
+ "name": "Lv52 Molech",
+ "popupTitle": "Bullheaded Berserker",
+ "@id": "hyd_nm3",
+ "position": {
+ "x": 8.1,
+ "y": -21.8
+ },
+ "metadata": {
+ "boss": "Molech",
+ "level": 52,
+ "element": "Ice",
+ "spawnedBy": {
+ "name": "Val Nullchu",
+ "element": "Earth",
+ "level": 57
+ }
+ }
+ },
+ {
+ "name": "Lv 53 Piasa",
+ "popupTitle": "Mad, Bad, and Fabulous to Know",
+ "@id": "hyd_nm4",
+ "position": {
+ "x": 7.1,
+ "y": -14.5
+ },
+ "metadata": {
+ "boss": "Piasa",
+ "level": 53,
+ "element": "Wind",
+ "spawnedBy": {
+ "name": "Vivid Gastornis",
+ "element": "Wind",
+ "level": 58
+ }
+ }
+ },
+ {
+ "name": "Lv54 Frostmane",
+ "popupTitle": "Fearful Symmetry",
+ "@id": "hyd_nm5",
+ "position": {
+ "x": 8.2,
+ "y": -26.1
+ },
+ "metadata": {
+ "boss": "Frostmane",
+ "level": 54,
+ "element": "Fire",
+ "spawnedBy": {
+ "name": "Northern Tiger",
+ "element": "Earth",
+ "level": 59
+ }
+ }
+ },
+ {
+ "name": "Lv55 Daphne",
+ "popupTitle": "Crawling Chaos",
+ "@id": "hyd_nm6",
+ "position": {
+ "x": 24.2,
+ "y": -19.0
+ },
+ "metadata": {
+ "boss": "Daphne",
+ "level": 55,
+ "element": "Water",
+ "spawnedBy": {
+ "name": "Dark Void Monk",
+ "element": "Water",
+ "level": 60
+ }
+ }
+ },
+ {
+ "name": "Lv56 King Goldemar",
+ "popupTitle": "Duty-free",
+ "@id": "hyd_nm7",
+ "position": {
+ "x": 29.1,
+ "y": -23.0
+ },
+ "metadata": {
+ "boss": "King Goldemar",
+ "level": 56,
+ "element": "Lightning",
+ "spawnedBy": {
+ "name": "Hydatos Wraith",
+ "element": "Fire",
+ "level": 61,
+ "spawnCondition": {
+ "time": "Night"
+ }
+ }
+ }
+ },
+ {
+ "name": "Lv57 Leuke",
+ "popupTitle": "Leukewarm Reception",
+ "@id": "hyd_nm8",
+ "position": {
+ "x": 37.4,
+ "y": -26.4
+ },
+ "metadata": {
+ "boss": "Leuke",
+ "level": 57,
+ "element": "Earth",
+ "spawnedBy": {
+ "name": "Tigerhawk",
+ "element": "Wind",
+ "level": 62
+ }
+ }
+ },
+ {
+ "name": "Lv58 Barong",
+ "popupTitle": "Robber Barong",
+ "@id": "hyd_nm9",
+ "position": {
+ "x": 32.2,
+ "y": -25.3
+ },
+ "metadata": {
+ "boss": "Barong",
+ "level": 58,
+ "element": "Fire",
+ "spawnedBy": {
+ "name": "Laboratory Lion",
+ "element": "Earth",
+ "level": 63
+ }
+ }
+ },
+ {
+ "name": "Lv59 Ceto",
+ "popupTitle": "Stone-cold Killer",
+ "@id": "hyd_nm10",
+ "position": {
+ "x": 36.3,
+ "y": -14.0
+ },
+ "metadata": {
+ "boss": "Ceto",
+ "level": 59,
+ "element": "Water",
+ "spawnedBy": {
+ "name": "Hydatos Delphine",
+ "element": "Fire",
+ "level": 64
+ }
+ }
+ },
+ {
+ "name": "Lv60 Provenance Watcher",
+ "popupTitle": "Crystalline Provenance",
+ "@id": "hyd_nm11",
+ "position": {
+ "x": 32.8,
+ "y": -19.6
+ },
+ "metadata": {
+ "boss": "Provenance Watcher",
+ "level": 60,
+ "element": "Fire",
+ "spawnedBy": {
+ "name": "Crystal Claw",
+ "element": "Fire",
+ "level": 65
+ }
+ }
+ },
+ {
+ "name": "Ovni",
+ "popupTitle": "I Don't Want to Believe",
+ "@id": "hyd_nm12",
+ "position": {
+ "x": 26.3,
+ "y": -29.2
+ },
+ "metadata": {
+ "boss": "Ovni",
+ "level": 60,
+ "element": "Ice",
+ "spawnCondition": {
+ "weather": "Umbral Turbulence"
+ }
+ }
+ },
+ {
+ "name": "Support",
+ "popupTitle": "The Baldesion Arsenal: Expedition Support",
+ "@id": "hyd_nm13",
+ "position": {
+ "x": 18.7,
+ "y": -28.4
+ },
+ "metadata": {
+ "boss": "Tristitia",
+ "level": 60,
+ "element": "Wind",
+ "spawnCondition": {
+ "duty": "The Baldesion Arsenal"
+ }
+ }
+ }
+ ]
+ }
+ }
+}
\ No newline at end of file
diff --git a/src/map/tooltipcomponents/AetheryteTooltipComponent.jsx b/src/map/tooltipcomponents/AetheryteTooltipComponent.jsx
new file mode 100644
index 0000000..fd68288
--- /dev/null
+++ b/src/map/tooltipcomponents/AetheryteTooltipComponent.jsx
@@ -0,0 +1,15 @@
+import { Stack, Typography } from '@mui/material';
+import React from 'react';
+
+export default function AetheryteTooltipComponent({ markerData }) {
+ if (markerData.metadata?.unlock) {
+ return (
+
+ Unlocks at
+ {`Level ${markerData.metadata.unlock}`}
+
+ );
+ }
+
+ return null;
+}
diff --git a/src/map/tooltipcomponents/BozjaCETooltipComponent.jsx b/src/map/tooltipcomponents/BozjaCETooltipComponent.jsx
new file mode 100644
index 0000000..8e48206
--- /dev/null
+++ b/src/map/tooltipcomponents/BozjaCETooltipComponent.jsx
@@ -0,0 +1,54 @@
+import { Box, Stack, Typography } from '@mui/material';
+import React from 'react';
+
+export default function BozjaCETooltipComponent({ markerData }) {
+ const renderSpawnedBy = () => {
+ if (markerData.metadata.spawnedBy) {
+ return (
+
+
+
+
+ {markerData.metadata.spawnedBy.name}
+
+ );
+ }
+ return null;
+ };
+
+ const renderRewards = () => {
+ if (markerData.metadata.rewards) {
+ return markerData.metadata.rewards.map((reward) => (
+
+
+
+
+ {reward.name}
+
+ ));
+ }
+ return null;
+ };
+
+ return (
+
+ Critical Engagement
+ Boss
+ {markerData.metadata.boss}
+ {markerData.metadata.spawnedBy ? Spawned by : null}
+ {renderSpawnedBy()}
+ {markerData.metadata.rewards ? Rewards : null}
+ {renderRewards()}
+
+ );
+}
diff --git a/src/map/tooltipcomponents/EurekaNMTooltipComponent.jsx b/src/map/tooltipcomponents/EurekaNMTooltipComponent.jsx
new file mode 100644
index 0000000..029b97d
--- /dev/null
+++ b/src/map/tooltipcomponents/EurekaNMTooltipComponent.jsx
@@ -0,0 +1,42 @@
+import { Box, Stack, Typography } from '@mui/material';
+import React from 'react';
+
+export default function EurekaNMTooltipComponent({ markerData }) {
+ const renderSpawnedBy = () => {
+ if (markerData.metadata.spawnedBy) {
+ return (
+
+
+
+
+ {`Lv${markerData.metadata.spawnedBy.level} ${markerData.metadata.spawnedBy.name}`}
+
+ );
+ }
+ return null;
+ };
+
+ return (
+
+ Notorious Monster
+
+
+
+
+ {markerData.name}
+
+ {markerData.metadata.spawnedBy ? Spawned by : null}
+ {renderSpawnedBy()}
+
+ );
+}