From 59f734dd07235afbc634c8ce473eaa618d14940a Mon Sep 17 00:00:00 2001 From: Dan Albert Date: Fri, 4 Mar 2022 19:41:33 -0800 Subject: [PATCH] Draw frozen combat in the new UI. https://github.com/dcs-liberation/dcs_liberation/issues/2039 --- client/src/api/combat.ts | 8 +++ client/src/api/combatSlice.ts | 42 +++++++++++++++ client/src/api/eventstream.tsx | 22 +++++--- client/src/app/store.ts | 6 ++- client/src/components/combat/Combat.tsx | 52 +++++++++++++++++++ client/src/components/combat/index.ts | 1 + .../components/combatlayer/CombatLayer.tsx | 16 ++++++ client/src/components/combatlayer/index.ts | 1 + .../liberationmap/LiberationMap.tsx | 4 ++ 9 files changed, 144 insertions(+), 8 deletions(-) create mode 100644 client/src/api/combat.ts create mode 100644 client/src/api/combatSlice.ts create mode 100644 client/src/components/combat/Combat.tsx create mode 100644 client/src/components/combat/index.ts create mode 100644 client/src/components/combatlayer/CombatLayer.tsx create mode 100644 client/src/components/combatlayer/index.ts diff --git a/client/src/api/combat.ts b/client/src/api/combat.ts new file mode 100644 index 00000000..23a1d482 --- /dev/null +++ b/client/src/api/combat.ts @@ -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; +} diff --git a/client/src/api/combatSlice.ts b/client/src/api/combatSlice.ts new file mode 100644 index 00000000..2e1f4c90 --- /dev/null +++ b/client/src/api/combatSlice.ts @@ -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) => { + state.combat = {}; + for (const combat of action.payload) { + state.combat[combat.id] = combat; + } + }, + newCombat: (state, action: PayloadAction) => { + const combat = action.payload; + state.combat[combat.id] = combat; + }, + updateCombat: (state, action: PayloadAction) => { + const combat = action.payload; + state.combat[combat.id] = combat; + }, + endCombat: (state, action: PayloadAction) => { + delete state.combat[action.payload]; + }, + }, +}); + +export const { setCombat, newCombat, updateCombat, endCombat } = + combatSlice.actions; + +export const selectCombat = (state: RootState) => state.combat; + +export default combatSlice.reducer; diff --git a/client/src/api/eventstream.tsx b/client/src/api/eventstream.tsx index e2a1b3c0..ef246983 100644 --- a/client/src/api/eventstream.tsx +++ b/client/src/api/eventstream.tsx @@ -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)); } diff --git a/client/src/app/store.ts b/client/src/app/store.ts index c91a140c..1715aa0a 100644 --- a/client/src/app/store.ts +++ b/client/src/app/store.ts @@ -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; diff --git a/client/src/components/combat/Combat.tsx b/client/src/components/combat/Combat.tsx new file mode 100644 index 00000000..d1112b70 --- /dev/null +++ b/client/src/components/combat/Combat.tsx @@ -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 ( + + ); +} + +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 ( + + ); + })} + + ); +} + +export default function Combat(props: CombatProps) { + return ( + <> + + + + ); +} diff --git a/client/src/components/combat/index.ts b/client/src/components/combat/index.ts new file mode 100644 index 00000000..c7d266da --- /dev/null +++ b/client/src/components/combat/index.ts @@ -0,0 +1 @@ +export { default } from "./Combat"; diff --git a/client/src/components/combatlayer/CombatLayer.tsx b/client/src/components/combatlayer/CombatLayer.tsx new file mode 100644 index 00000000..0a5de15d --- /dev/null +++ b/client/src/components/combatlayer/CombatLayer.tsx @@ -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 ( + + {Object.values(combats.combat).map((combat) => { + return ; + })} + ( + + ); +} diff --git a/client/src/components/combatlayer/index.ts b/client/src/components/combatlayer/index.ts new file mode 100644 index 00000000..98aaaa74 --- /dev/null +++ b/client/src/components/combatlayer/index.ts @@ -0,0 +1 @@ +export { default } from "./CombatLayer"; diff --git a/client/src/components/liberationmap/LiberationMap.tsx b/client/src/components/liberationmap/LiberationMap.tsx index 8ed893a3..47f52a54 100644 --- a/client/src/components/liberationmap/LiberationMap.tsx +++ b/client/src/components/liberationmap/LiberationMap.tsx @@ -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) { + + +