mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Draw frozen combat in the new UI.
https://github.com/dcs-liberation/dcs_liberation/issues/2039
This commit is contained in:
parent
88cd9e19c5
commit
59f734dd07
8
client/src/api/combat.ts
Normal file
8
client/src/api/combat.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
import { LatLng } from "leaflet";
|
||||||
|
|
||||||
|
export default interface Combat {
|
||||||
|
id: string;
|
||||||
|
flight_position: LatLng | null;
|
||||||
|
target_positions: LatLng[] | null;
|
||||||
|
footprint: LatLng[][] | null;
|
||||||
|
}
|
||||||
42
client/src/api/combatSlice.ts
Normal file
42
client/src/api/combatSlice.ts
Normal file
@ -0,0 +1,42 @@
|
|||||||
|
import { RootState } from "../app/store";
|
||||||
|
import Combat from "./combat";
|
||||||
|
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
|
||||||
|
|
||||||
|
interface CombatState {
|
||||||
|
combat: { [key: string]: Combat };
|
||||||
|
}
|
||||||
|
|
||||||
|
const initialState: CombatState = {
|
||||||
|
combat: {},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const combatSlice = createSlice({
|
||||||
|
name: "combat",
|
||||||
|
initialState,
|
||||||
|
reducers: {
|
||||||
|
setCombat: (state, action: PayloadAction<Combat[]>) => {
|
||||||
|
state.combat = {};
|
||||||
|
for (const combat of action.payload) {
|
||||||
|
state.combat[combat.id] = combat;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
newCombat: (state, action: PayloadAction<Combat>) => {
|
||||||
|
const combat = action.payload;
|
||||||
|
state.combat[combat.id] = combat;
|
||||||
|
},
|
||||||
|
updateCombat: (state, action: PayloadAction<Combat>) => {
|
||||||
|
const combat = action.payload;
|
||||||
|
state.combat[combat.id] = combat;
|
||||||
|
},
|
||||||
|
endCombat: (state, action: PayloadAction<string>) => {
|
||||||
|
delete state.combat[action.payload];
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
export const { setCombat, newCombat, updateCombat, endCombat } =
|
||||||
|
combatSlice.actions;
|
||||||
|
|
||||||
|
export const selectCombat = (state: RootState) => state.combat;
|
||||||
|
|
||||||
|
export default combatSlice.reducer;
|
||||||
@ -1,5 +1,7 @@
|
|||||||
import { AppDispatch } from "../app/store";
|
import { AppDispatch } from "../app/store";
|
||||||
import backend from "./backend";
|
import backend from "./backend";
|
||||||
|
import Combat from "./combat";
|
||||||
|
import { endCombat, newCombat, updateCombat } from "./combatSlice";
|
||||||
import { updateControlPoint } from "./controlPointsSlice";
|
import { updateControlPoint } from "./controlPointsSlice";
|
||||||
import { ControlPoint } from "./controlpoint";
|
import { ControlPoint } from "./controlpoint";
|
||||||
import { Flight } from "./flight";
|
import { Flight } from "./flight";
|
||||||
@ -21,14 +23,10 @@ import Tgo from "./tgo";
|
|||||||
import { updateTgo } from "./tgosSlice";
|
import { updateTgo } from "./tgosSlice";
|
||||||
import { LatLng } from "leaflet";
|
import { LatLng } from "leaflet";
|
||||||
|
|
||||||
// Placeholder. We don't use this yet. This is just here so we can flesh out the
|
|
||||||
// update events model.
|
|
||||||
interface FrozenCombat {}
|
|
||||||
|
|
||||||
interface GameUpdateEvents {
|
interface GameUpdateEvents {
|
||||||
updated_flight_positions: { [id: string]: LatLng };
|
updated_flight_positions: { [id: string]: LatLng };
|
||||||
new_combats: FrozenCombat[];
|
new_combats: Combat[];
|
||||||
updated_combats: FrozenCombat[];
|
updated_combats: Combat[];
|
||||||
ended_combats: string[];
|
ended_combats: string[];
|
||||||
navmesh_updates: boolean[];
|
navmesh_updates: boolean[];
|
||||||
unculled_zones_updated: boolean;
|
unculled_zones_updated: boolean;
|
||||||
@ -55,6 +53,18 @@ export const handleStreamedEvents = (
|
|||||||
dispatch(updateFlightPosition([id, position]));
|
dispatch(updateFlightPosition([id, position]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
for (const combat of events.new_combats) {
|
||||||
|
dispatch(newCombat(combat));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const combat of events.updated_combats) {
|
||||||
|
dispatch(updateCombat(combat));
|
||||||
|
}
|
||||||
|
|
||||||
|
for (const id of events.ended_combats) {
|
||||||
|
dispatch(endCombat(id));
|
||||||
|
}
|
||||||
|
|
||||||
for (const flight of events.new_flights) {
|
for (const flight of events.new_flights) {
|
||||||
dispatch(registerFlight(flight));
|
dispatch(registerFlight(flight));
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,3 +1,4 @@
|
|||||||
|
import combatReducer from "../api/combatSlice";
|
||||||
import controlPointsReducer from "../api/controlPointsSlice";
|
import controlPointsReducer from "../api/controlPointsSlice";
|
||||||
import flightsReducer from "../api/flightsSlice";
|
import flightsReducer from "../api/flightsSlice";
|
||||||
import frontLinesReducer from "../api/frontLinesSlice";
|
import frontLinesReducer from "../api/frontLinesSlice";
|
||||||
@ -8,14 +9,15 @@ import logger from "redux-logger";
|
|||||||
|
|
||||||
export const store = configureStore({
|
export const store = configureStore({
|
||||||
reducer: {
|
reducer: {
|
||||||
|
combat: combatReducer,
|
||||||
|
controlPoints: controlPointsReducer,
|
||||||
flights: flightsReducer,
|
flights: flightsReducer,
|
||||||
frontLines: frontLinesReducer,
|
frontLines: frontLinesReducer,
|
||||||
controlPoints: controlPointsReducer,
|
|
||||||
supplyRoutes: supplyRoutesReducer,
|
supplyRoutes: supplyRoutesReducer,
|
||||||
tgos: tgosReducer,
|
tgos: tgosReducer,
|
||||||
},
|
},
|
||||||
// The logger middleware must be last or it won't log actions.
|
// The logger middleware must be last or it won't log actions.
|
||||||
//middleware: [logger],
|
middleware: [logger],
|
||||||
});
|
});
|
||||||
|
|
||||||
export type AppDispatch = typeof store.dispatch;
|
export type AppDispatch = typeof store.dispatch;
|
||||||
|
|||||||
52
client/src/components/combat/Combat.tsx
Normal file
52
client/src/components/combat/Combat.tsx
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
import CombatModel from "../../api/combat";
|
||||||
|
import { LatLng } from "leaflet";
|
||||||
|
import { Polygon, Polyline } from "react-leaflet";
|
||||||
|
|
||||||
|
interface CombatProps {
|
||||||
|
combat: CombatModel;
|
||||||
|
}
|
||||||
|
|
||||||
|
function CombatFootprint(props: CombatProps) {
|
||||||
|
if (!props.combat.footprint) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Polygon
|
||||||
|
positions={props.combat.footprint}
|
||||||
|
color="#c85050"
|
||||||
|
interactive={false}
|
||||||
|
fillOpacity={0.2}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function CombatLines(props: CombatProps) {
|
||||||
|
if (!props.combat.flight_position || !props.combat.target_positions) {
|
||||||
|
return <></>;
|
||||||
|
}
|
||||||
|
|
||||||
|
const flightPosition: LatLng = props.combat.flight_position;
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{props.combat.target_positions.map((position) => {
|
||||||
|
return (
|
||||||
|
<Polyline
|
||||||
|
positions={[flightPosition, position]}
|
||||||
|
color="#c85050"
|
||||||
|
interactive={false}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default function Combat(props: CombatProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<CombatFootprint {...props} />
|
||||||
|
<CombatLines {...props} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
client/src/components/combat/index.ts
Normal file
1
client/src/components/combat/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./Combat";
|
||||||
16
client/src/components/combatlayer/CombatLayer.tsx
Normal file
16
client/src/components/combatlayer/CombatLayer.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { selectCombat } from "../../api/combatSlice";
|
||||||
|
import { useAppSelector } from "../../app/hooks";
|
||||||
|
import Combat from "../combat/Combat";
|
||||||
|
import { LayerGroup } from "react-leaflet";
|
||||||
|
|
||||||
|
export default function CombatLayer() {
|
||||||
|
const combats = useAppSelector(selectCombat);
|
||||||
|
return (
|
||||||
|
<LayerGroup>
|
||||||
|
{Object.values(combats.combat).map((combat) => {
|
||||||
|
return <Combat key={combat.id} combat={combat} />;
|
||||||
|
})}
|
||||||
|
(
|
||||||
|
</LayerGroup>
|
||||||
|
);
|
||||||
|
}
|
||||||
1
client/src/components/combatlayer/index.ts
Normal file
1
client/src/components/combatlayer/index.ts
Normal file
@ -0,0 +1 @@
|
|||||||
|
export { default } from "./CombatLayer";
|
||||||
@ -1,5 +1,6 @@
|
|||||||
import AircraftLayer from "../aircraftlayer";
|
import AircraftLayer from "../aircraftlayer";
|
||||||
import AirDefenseRangeLayer from "../airdefenserangelayer";
|
import AirDefenseRangeLayer from "../airdefenserangelayer";
|
||||||
|
import CombatLayer from "../combatlayer";
|
||||||
import ControlPointsLayer from "../controlpointslayer";
|
import ControlPointsLayer from "../controlpointslayer";
|
||||||
import FlightPlansLayer from "../flightplanslayer";
|
import FlightPlansLayer from "../flightplanslayer";
|
||||||
import FrontLinesLayer from "../frontlineslayer";
|
import FrontLinesLayer from "../frontlineslayer";
|
||||||
@ -34,6 +35,9 @@ export default function LiberationMap(props: GameProps) {
|
|||||||
<LayersControl.Overlay name="Aircraft" checked>
|
<LayersControl.Overlay name="Aircraft" checked>
|
||||||
<AircraftLayer />
|
<AircraftLayer />
|
||||||
</LayersControl.Overlay>
|
</LayersControl.Overlay>
|
||||||
|
<LayersControl.Overlay name="Active combat" checked>
|
||||||
|
<CombatLayer />
|
||||||
|
</LayersControl.Overlay>
|
||||||
<LayersControl.Overlay name="Air defenses" checked>
|
<LayersControl.Overlay name="Air defenses" checked>
|
||||||
<TgosLayer categories={["aa"]} />
|
<TgosLayer categories={["aa"]} />
|
||||||
</LayersControl.Overlay>
|
</LayersControl.Overlay>
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user