mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Draw supply routes on the react map.
https://github.com/dcs-liberation/dcs_liberation/issues/2039
This commit is contained in:
28
client/src/api/supplyRoutesSlice.ts
Normal file
28
client/src/api/supplyRoutesSlice.ts
Normal file
@@ -0,0 +1,28 @@
|
||||
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||
|
||||
import { RootState } from "../app/store";
|
||||
import SupplyRoute from "./supplyroute";
|
||||
|
||||
interface SupplyRoutesState {
|
||||
routes: SupplyRoute[];
|
||||
}
|
||||
|
||||
const initialState: SupplyRoutesState = {
|
||||
routes: [],
|
||||
};
|
||||
|
||||
export const supplyRoutesSlice = createSlice({
|
||||
name: "supplyRoutes",
|
||||
initialState,
|
||||
reducers: {
|
||||
setSupplyRoutes: (state, action: PayloadAction<SupplyRoute[]>) => {
|
||||
state.routes = action.payload;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const { setSupplyRoutes } = supplyRoutesSlice.actions;
|
||||
|
||||
export const selectSupplyRoutes = (state: RootState) => state.supplyRoutes;
|
||||
|
||||
export default supplyRoutesSlice.reducer;
|
||||
11
client/src/api/supplyroute.ts
Normal file
11
client/src/api/supplyroute.ts
Normal file
@@ -0,0 +1,11 @@
|
||||
import { LatLng } from "leaflet";
|
||||
|
||||
export interface SupplyRoute {
|
||||
points: LatLng[];
|
||||
front_active: boolean;
|
||||
is_sea: boolean;
|
||||
blue: boolean;
|
||||
active_transports: string[];
|
||||
}
|
||||
|
||||
export default SupplyRoute;
|
||||
@@ -2,12 +2,14 @@ import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit";
|
||||
|
||||
import controlPointsReducer from "../api/controlPointsSlice";
|
||||
import flightsReducer from "../api/flightsSlice";
|
||||
import supplyRoutesReducer from "../api/supplyRoutesSlice";
|
||||
import tgosReducer from "../api/tgosSlice";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
flights: flightsReducer,
|
||||
controlPoints: controlPointsReducer,
|
||||
supplyRoutes: supplyRoutesReducer,
|
||||
tgos: tgosReducer,
|
||||
},
|
||||
});
|
||||
|
||||
@@ -7,6 +7,7 @@ import { BasemapLayer } from "react-esri-leaflet";
|
||||
import ControlPointsLayer from "../controlpointslayer";
|
||||
import FlightPlansLayer from "../flightplanslayer";
|
||||
import { LatLng } from "leaflet";
|
||||
import SupplyRoutesLayer from "../supplyrouteslayer";
|
||||
import { TgoType } from "../../api/tgo";
|
||||
import TgosLayer from "../tgoslayer/TgosLayer";
|
||||
|
||||
@@ -38,6 +39,9 @@ export default function LiberationMap(props: GameProps) {
|
||||
</LayersControl.Overlay>
|
||||
);
|
||||
})}
|
||||
<LayersControl.Overlay name="Supply routes" checked>
|
||||
<SupplyRoutesLayer />
|
||||
</LayersControl.Overlay>
|
||||
<LayersControl.Overlay name="Enemy SAM threat range" checked>
|
||||
<AirDefenseRangeLayer blue={false} />
|
||||
</LayersControl.Overlay>
|
||||
|
||||
20
client/src/components/splitlines/SplitLines.tsx
Normal file
20
client/src/components/splitlines/SplitLines.tsx
Normal file
@@ -0,0 +1,20 @@
|
||||
interface SplitLinesProps {
|
||||
items: string[];
|
||||
}
|
||||
|
||||
const SplitLines = (props: SplitLinesProps) => {
|
||||
return (
|
||||
<>
|
||||
{props.items.map((text) => {
|
||||
return (
|
||||
<>
|
||||
{text}
|
||||
<br />
|
||||
</>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default SplitLines;
|
||||
68
client/src/components/supplyroute/SupplyRoute.tsx
Normal file
68
client/src/components/supplyroute/SupplyRoute.tsx
Normal file
@@ -0,0 +1,68 @@
|
||||
import { Polyline, Tooltip } from "react-leaflet";
|
||||
import { useEffect, useRef } from "react";
|
||||
|
||||
import { Polyline as LPolyline } from "leaflet";
|
||||
import SplitLines from "../splitlines/SplitLines";
|
||||
import { SupplyRoute as SupplyRouteModel } from "../../api/supplyroute";
|
||||
|
||||
interface SupplyRouteProps {
|
||||
route: SupplyRouteModel;
|
||||
}
|
||||
|
||||
function SupplyRouteTooltip(props: SupplyRouteProps) {
|
||||
if (!props.route.active_transports.length) {
|
||||
return <Tooltip>This supply route is inactive.</Tooltip>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Tooltip>
|
||||
<SplitLines items={props.route.active_transports} />
|
||||
</Tooltip>
|
||||
);
|
||||
}
|
||||
|
||||
function ActiveSupplyRouteHighlight(props: SupplyRouteProps) {
|
||||
if (!props.route.active_transports.length) {
|
||||
return <></>;
|
||||
}
|
||||
|
||||
return (
|
||||
<Polyline positions={props.route.points} color={"#ffffff"} weight={2} />
|
||||
);
|
||||
}
|
||||
|
||||
function colorFor(route: SupplyRouteModel) {
|
||||
if (route.front_active) {
|
||||
return "#c85050";
|
||||
}
|
||||
if (route.blue) {
|
||||
return "#2d3e50";
|
||||
}
|
||||
return "#8c1414";
|
||||
}
|
||||
|
||||
export default function SupplyRoute(props: SupplyRouteProps) {
|
||||
const color = colorFor(props.route);
|
||||
const weight = props.route.is_sea ? 4 : 6;
|
||||
|
||||
const path = useRef<LPolyline | null>();
|
||||
|
||||
useEffect(() => {
|
||||
// Ensure that the highlight line draws on top of this. We have to bring
|
||||
// this to the back rather than bringing the highlight to the front because
|
||||
// the highlight won't necessarily be drawn yet.
|
||||
path.current?.bringToBack();
|
||||
});
|
||||
|
||||
return (
|
||||
<Polyline
|
||||
positions={props.route.points}
|
||||
color={color}
|
||||
weight={weight}
|
||||
ref={(ref) => (path.current = ref)}
|
||||
>
|
||||
<SupplyRouteTooltip {...props} />
|
||||
<ActiveSupplyRouteHighlight {...props} />
|
||||
</Polyline>
|
||||
);
|
||||
}
|
||||
1
client/src/components/supplyroute/index.ts
Normal file
1
client/src/components/supplyroute/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./SupplyRoute";
|
||||
@@ -0,0 +1,15 @@
|
||||
import { LayerGroup } from "react-leaflet";
|
||||
import SupplyRoute from "../supplyroute/SupplyRoute";
|
||||
import { selectSupplyRoutes } from "../../api/supplyRoutesSlice";
|
||||
import { useAppSelector } from "../../app/hooks";
|
||||
|
||||
export default function SupplyRoutesLayer() {
|
||||
const routes = useAppSelector(selectSupplyRoutes).routes;
|
||||
return (
|
||||
<LayerGroup>
|
||||
{routes.map((route, idx) => {
|
||||
return <SupplyRoute key={idx} route={route} />;
|
||||
})}
|
||||
</LayerGroup>
|
||||
);
|
||||
}
|
||||
1
client/src/components/supplyrouteslayer/index.ts
Normal file
1
client/src/components/supplyrouteslayer/index.ts
Normal file
@@ -0,0 +1 @@
|
||||
export { default } from "./SupplyRoutesLayer";
|
||||
@@ -11,7 +11,6 @@ interface TgosLayerProps {
|
||||
export default function TgosLayer(props: TgosLayerProps) {
|
||||
const allTgos = useAppSelector(selectTgos);
|
||||
const tgos = allTgos.tgosByType[props.type];
|
||||
console.dir(Object.entries(TgoType));
|
||||
return (
|
||||
<LayerGroup>
|
||||
{tgos.map((tgo) => {
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { ControlPoint } from "../api/controlpoint";
|
||||
import { Flight } from "../api/flight";
|
||||
import SupplyRoute from "../api/supplyroute";
|
||||
import Tgo from "../api/tgo";
|
||||
import backend from "../api/backend";
|
||||
import { registerFlight } from "../api/flightsSlice";
|
||||
import { setControlPoints } from "../api/controlPointsSlice";
|
||||
import { setSupplyRoutes } from "../api/supplyRoutesSlice";
|
||||
import { setTgos } from "../api/tgosSlice";
|
||||
import { useAppDispatch } from "../app/hooks";
|
||||
import { useEffect } from "react";
|
||||
@@ -31,6 +33,14 @@ export const useInitialGameState = () => {
|
||||
dispatch(setTgos(response.data as Tgo[]));
|
||||
}
|
||||
});
|
||||
backend
|
||||
.get("/supply-routes")
|
||||
.catch((error) => console.log(`Error fetching supply routes: ${error}`))
|
||||
.then((response) => {
|
||||
if (response != null) {
|
||||
dispatch(setSupplyRoutes(response.data as SupplyRoute[]));
|
||||
}
|
||||
});
|
||||
backend
|
||||
.get("/flights?with_waypoints=true")
|
||||
.catch((error) => console.log(`Error fetching flights: ${error}`))
|
||||
|
||||
Reference in New Issue
Block a user