Add culling exclusion zones display to the new map.

https://github.com/dcs-liberation/dcs_liberation/issues/2158
This commit is contained in:
Raffson 2022-06-15 03:57:04 +02:00 committed by GitHub
parent c5ff8777be
commit ad7032064d
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
12 changed files with 123 additions and 11 deletions

View File

@ -479,6 +479,7 @@ export type Game = {
threat_zones: ThreatZoneContainer;
navmeshes: NavMeshes;
map_center?: LatLng;
unculled_zones: UnculledZone[];
};
export type MapZones = {
inclusion: LatLng[][];

View File

@ -28,6 +28,7 @@ import {
import { navMeshUpdated } from "./navMeshSlice";
import { updateTgo } from "./tgosSlice";
import { threatZonesUpdated } from "./threatZonesSlice";
import { unculledZonesUpdated } from "./unculledZonesSlice";
import { LatLng } from "leaflet";
import { updateIadsConnection } from "./iadsNetworkSlice";
import { IadsConnection } from "./_liberationApi";
@ -87,6 +88,16 @@ export const handleStreamedEvents = (
});
}
if (events.unculled_zones_updated) {
backend.get(`/map-zones/unculled`).then(
(result) => {
if (result.data) {
dispatch(unculledZonesUpdated(result.data));
}
}
);
}
if (events.threat_zones_updated) {
dispatch(liberationApi.endpoints.getThreatZones.initiate()).then(
(result) => {

View File

@ -0,0 +1,36 @@
import { RootState } from "../app/store";
import { gameLoaded, gameUnloaded } from "./actions";
import { UnculledZone } from "./liberationApi";
import { PayloadAction, createSlice } from "@reduxjs/toolkit";
interface UnculledZonesState {
zones: UnculledZone[];
}
const initialState: UnculledZonesState = {
zones: [],
};
export const unculledZonesSlice = createSlice({
name: "unculledZonesState",
initialState,
reducers: {
updated: (state, action: PayloadAction<UnculledZone[]>) => {
state.zones = action.payload;
},
},
extraReducers: (builder) => {
builder.addCase(gameLoaded, (state, action) => {
state.zones = action.payload.unculled_zones;
});
builder.addCase(gameUnloaded, (state) => {
state.zones = initialState.zones;
});
},
});
export const { updated: unculledZonesUpdated } = unculledZonesSlice.actions;
export const selectUnculledZones = (state: RootState) => state.unculledZones;
export default unculledZonesSlice.reducer;

View File

@ -9,6 +9,7 @@ import supplyRoutesReducer from "../api/supplyRoutesSlice";
import tgosReducer from "../api/tgosSlice";
import iadsNetworkReducer from "../api/iadsNetworkSlice";
import threatZonesReducer from "../api/threatZonesSlice";
import unculledZonesReducer from "../api/unculledZonesSlice";
import { Action, ThunkAction, configureStore } from "@reduxjs/toolkit";
export const store = configureStore({
@ -24,6 +25,7 @@ export const store = configureStore({
tgos: tgosReducer,
threatZones: threatZonesReducer,
[baseApi.reducerPath]: baseApi.reducer,
unculledZones: unculledZonesReducer,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware().concat(baseApi.middleware),

View File

@ -0,0 +1,47 @@
import { UnculledZone } from "../../api/liberationApi";
import { selectUnculledZones } from "../../api/unculledZonesSlice";
import { useAppSelector } from "../../app/hooks";
import { LayerGroup, LayersControl, Circle } from "react-leaflet";
interface CullingExclusionCirclesProps {
zones: UnculledZone[];
}
const CullingExclusionCircles = (props: CullingExclusionCirclesProps) => {
return (
<>
<LayerGroup>
{props.zones.map((zone, idx) => {
return (
<Circle
key={idx}
center={zone.position}
radius={zone.radius}
color="#b4ff8c"
fill={false}
interactive={false}
/>
);
})}
</LayerGroup>
</>
);
};
export default function CullingExclusionZones() {
const data = useAppSelector(selectUnculledZones).zones;
var cez = <></>;
if (!data) {
console.log("Empty response when loading culling exclusion zones");
} else {
cez = (
<CullingExclusionCircles zones={data}></CullingExclusionCircles>
);
}
return (
<LayersControl.Overlay name="Culling exclusion zones">
{cez}
</LayersControl.Overlay>
);
}

View File

@ -0,0 +1 @@
export { default } from "./CullingExclusionZones";

View File

@ -18,6 +18,7 @@ import { useEffect, useRef } from "react";
import { BasemapLayer } from "react-esri-leaflet";
import { LayersControl, MapContainer, ScaleControl } from "react-leaflet";
import Iadsnetworklayer from "../iadsnetworklayer";
import CullingExclusionZones from "../cullingexclusionzones/CullingExclusionZones"
import LeafletRuler from "../ruler/Ruler";
export default function LiberationMap() {
@ -109,6 +110,7 @@ export default function LiberationMap() {
<NavMeshLayer blue={false} />
</LayersControl.Overlay>
<TerrainZonesLayers />
<CullingExclusionZones />
<WaypointDebugZonesControls />
</LayersControl>
</MapContainer>

View File

@ -217,7 +217,7 @@ class Game:
naming.namegen = self.name_generator
LuaPluginManager.load_settings(self.settings)
ObjectiveDistanceCache.set_theater(self.theater)
self.compute_unculled_zones()
self.compute_unculled_zones(GameUpdateEvents())
if not game_still_initializing:
# We don't need to push events that happen during load. The UI will fully
# reset when we're done.
@ -417,7 +417,7 @@ class Game:
# Update cull zones
with logged_duration("Computing culling positions"):
self.compute_unculled_zones()
self.compute_unculled_zones(events)
def message(self, title: str, text: str = "") -> None:
self.informations.append(Information(title, text, turn=self.turn))
@ -459,7 +459,7 @@ class Game:
def navmesh_for(self, player: bool) -> NavMesh:
return self.coalition_for(player).nav_mesh
def compute_unculled_zones(self) -> None:
def compute_unculled_zones(self, events: GameUpdateEvents) -> None:
"""
Compute the current conflict position(s) used for culling calculation
"""
@ -514,6 +514,7 @@ class Game:
zones.append(package.target.position)
self.__culling_zones = zones
events.update_unculled_zones()
def add_destroyed_units(self, data: dict[str, Union[float, str]]) -> None:
pos = Point(

View File

@ -8,7 +8,7 @@ from game.server.controlpoints.models import ControlPointJs
from game.server.flights.models import FlightJs
from game.server.frontlines.models import FrontLineJs
from game.server.leaflet import LeafletPoint
from game.server.mapzones.models import ThreatZoneContainerJs
from game.server.mapzones.models import ThreatZoneContainerJs, UnculledZoneJs
from game.server.navmesh.models import NavMeshesJs
from game.server.supplyroutes.models import SupplyRouteJs
from game.server.tgos.models import TgoJs
@ -28,6 +28,7 @@ class GameJs(BaseModel):
threat_zones: ThreatZoneContainerJs
navmeshes: NavMeshesJs
map_center: LeafletPoint | None
unculled_zones: list[UnculledZoneJs]
class Config:
title = "Game"
@ -44,4 +45,5 @@ class GameJs(BaseModel):
threat_zones=ThreatZoneContainerJs.for_game(game),
navmeshes=NavMeshesJs.from_game(game),
map_center=game.theater.terrain.map_view_default.position.latlng(),
unculled_zones=UnculledZoneJs.from_game(game),
)

View File

@ -28,6 +28,16 @@ class UnculledZoneJs(BaseModel):
class Config:
title = "UnculledZone"
@staticmethod
def from_game(game: Game) -> list[UnculledZoneJs]:
return [
UnculledZoneJs(
position=zone.latlng(),
radius=game.settings.perf_culling_distance * 1000,
)
for zone in game.get_culling_zones()
]
class ThreatZonesJs(BaseModel):
full: list[LeafletPoly]

View File

@ -27,12 +27,7 @@ def get_terrain(game: Game = Depends(GameContext.require)) -> MapZonesJs:
def get_unculled_zones(
game: Game = Depends(GameContext.require),
) -> list[UnculledZoneJs]:
return [
UnculledZoneJs(
position=zone.latlng(), radius=game.settings.perf_culling_distance * 1000
)
for zone in game.get_culling_zones()
]
return UnculledZoneJs.from_game(game)
@router.get(

View File

@ -22,6 +22,7 @@ from PySide2.QtWidgets import (
import qt_ui.uiconstants as CONST
from game.game import Game
from game.server import EventStream
from game.settings import (
BooleanOption,
BoundedFloatOption,
@ -31,6 +32,7 @@ from game.settings import (
OptionDescription,
Settings,
)
from game.sim import GameUpdateEvents
from qt_ui.widgets.QLabeledWidget import QLabeledWidget
from qt_ui.widgets.spinsliders import FloatSpinSlider, TimeInputs
from qt_ui.windows.GameUpdateSignal import GameUpdateSignal
@ -356,7 +358,9 @@ class QSettingsWindow(QDialog):
self.cheat_options.show_base_capture_cheat
)
self.game.compute_unculled_zones()
events = GameUpdateEvents()
self.game.compute_unculled_zones(events)
EventStream.put_nowait(events)
GameUpdateSignal.get_instance().updateGame(self.game)
def onSelectionChanged(self):