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 backend from "./backend";
|
||||
import Combat from "./combat";
|
||||
import { endCombat, newCombat, updateCombat } from "./combatSlice";
|
||||
import { updateControlPoint } from "./controlPointsSlice";
|
||||
import { ControlPoint } from "./controlpoint";
|
||||
import { Flight } from "./flight";
|
||||
@ -21,14 +23,10 @@ import Tgo from "./tgo";
|
||||
import { updateTgo } from "./tgosSlice";
|
||||
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 {
|
||||
updated_flight_positions: { [id: string]: LatLng };
|
||||
new_combats: FrozenCombat[];
|
||||
updated_combats: FrozenCombat[];
|
||||
new_combats: Combat[];
|
||||
updated_combats: Combat[];
|
||||
ended_combats: string[];
|
||||
navmesh_updates: boolean[];
|
||||
unculled_zones_updated: boolean;
|
||||
@ -55,6 +53,18 @@ export const handleStreamedEvents = (
|
||||
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) {
|
||||
dispatch(registerFlight(flight));
|
||||
}
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import combatReducer from "../api/combatSlice";
|
||||
import controlPointsReducer from "../api/controlPointsSlice";
|
||||
import flightsReducer from "../api/flightsSlice";
|
||||
import frontLinesReducer from "../api/frontLinesSlice";
|
||||
@ -8,14 +9,15 @@ import logger from "redux-logger";
|
||||
|
||||
export const store = configureStore({
|
||||
reducer: {
|
||||
combat: combatReducer,
|
||||
controlPoints: controlPointsReducer,
|
||||
flights: flightsReducer,
|
||||
frontLines: frontLinesReducer,
|
||||
controlPoints: controlPointsReducer,
|
||||
supplyRoutes: supplyRoutesReducer,
|
||||
tgos: tgosReducer,
|
||||
},
|
||||
// The logger middleware must be last or it won't log actions.
|
||||
//middleware: [logger],
|
||||
middleware: [logger],
|
||||
});
|
||||
|
||||
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 AirDefenseRangeLayer from "../airdefenserangelayer";
|
||||
import CombatLayer from "../combatlayer";
|
||||
import ControlPointsLayer from "../controlpointslayer";
|
||||
import FlightPlansLayer from "../flightplanslayer";
|
||||
import FrontLinesLayer from "../frontlineslayer";
|
||||
@ -34,6 +35,9 @@ export default function LiberationMap(props: GameProps) {
|
||||
<LayersControl.Overlay name="Aircraft" checked>
|
||||
<AircraftLayer />
|
||||
</LayersControl.Overlay>
|
||||
<LayersControl.Overlay name="Active combat" checked>
|
||||
<CombatLayer />
|
||||
</LayersControl.Overlay>
|
||||
<LayersControl.Overlay name="Air defenses" checked>
|
||||
<TgosLayer categories={["aa"]} />
|
||||
</LayersControl.Overlay>
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user