mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Allow deletion of certain types of waypoints
Resolves #60 NAV/REFUEL/DIVERT waypoints should have no effect on the timings.
This commit is contained in:
parent
8363a7e8fa
commit
198ff7d8a3
@ -16,6 +16,7 @@
|
|||||||
* **[Modding]** Support for Iron Dome v1.2 by IDF Mods Project
|
* **[Modding]** Support for Iron Dome v1.2 by IDF Mods Project
|
||||||
* **[New Game Wizard]** Re-organized generator options & show the regular settings menu instead of the limited "Difficulty & Automation" page.
|
* **[New Game Wizard]** Re-organized generator options & show the regular settings menu instead of the limited "Difficulty & Automation" page.
|
||||||
* **[Campaign Management]** Ability to operate harriers from FOBs/FARPs for <ins>__human pilots only__</ins>. Please note that the autoplanner won't generate flights for harriers at FOBs/FARPs, which means you need to plan your missions manually.
|
* **[Campaign Management]** Ability to operate harriers from FOBs/FARPs for <ins>__human pilots only__</ins>. Please note that the autoplanner won't generate flights for harriers at FOBs/FARPs, which means you need to plan your missions manually.
|
||||||
|
* **[Mission Planning]** Allow NAV/REFUEL/DIVERT waypoints to be deleted without degrading to a custom flight-plan, also warning the user before actually degrading the flight-plan.
|
||||||
|
|
||||||
## Fixes
|
## Fixes
|
||||||
* **[New Game Wizard]** Settings would not persist when going back to a previous page (obsolete due to overhaul).
|
* **[New Game Wizard]** Settings would not persist when going back to a previous page (obsolete due to overhaul).
|
||||||
|
|||||||
@ -4,11 +4,11 @@ from dataclasses import dataclass
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import Iterator, TYPE_CHECKING, Type
|
from typing import Iterator, TYPE_CHECKING, Type
|
||||||
|
|
||||||
from ._common_ctld import generate_random_ctld_point
|
|
||||||
from game.ato.flightplans.standard import StandardFlightPlan, StandardLayout
|
from game.ato.flightplans.standard import StandardFlightPlan, StandardLayout
|
||||||
from game.theater.controlpoint import ControlPointType
|
from game.theater.controlpoint import ControlPointType
|
||||||
from game.theater.missiontarget import MissionTarget
|
from game.theater.missiontarget import MissionTarget
|
||||||
from game.utils import Distance, feet, meters
|
from game.utils import Distance, feet, meters
|
||||||
|
from ._common_ctld import generate_random_ctld_point
|
||||||
from .ibuilder import IBuilder
|
from .ibuilder import IBuilder
|
||||||
from .planningerror import PlanningError
|
from .planningerror import PlanningError
|
||||||
from .uizonedisplay import UiZone, UiZoneDisplay
|
from .uizonedisplay import UiZone, UiZoneDisplay
|
||||||
@ -21,7 +21,7 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class AirAssaultLayout(StandardLayout):
|
class AirAssaultLayout(StandardLayout):
|
||||||
# The pickup point is optional because we don't always need to load the cargo. When
|
# The pickup point is optional because we don't always need to load the cargo. When
|
||||||
# departing from a carrier, LHA, or off-map spawn, the cargo is pre-loaded.
|
# departing from a carrier, LHA, or off-map spawn, the cargo is pre-loaded.
|
||||||
|
|||||||
@ -5,9 +5,9 @@ from dataclasses import dataclass
|
|||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from typing import TYPE_CHECKING, Type
|
from typing import TYPE_CHECKING, Type
|
||||||
|
|
||||||
from ._common_ctld import generate_random_ctld_point
|
|
||||||
from game.theater.missiontarget import MissionTarget
|
from game.theater.missiontarget import MissionTarget
|
||||||
from game.utils import feet
|
from game.utils import feet
|
||||||
|
from ._common_ctld import generate_random_ctld_point
|
||||||
from .ibuilder import IBuilder
|
from .ibuilder import IBuilder
|
||||||
from .planningerror import PlanningError
|
from .planningerror import PlanningError
|
||||||
from .standard import StandardFlightPlan, StandardLayout
|
from .standard import StandardFlightPlan, StandardLayout
|
||||||
@ -19,7 +19,7 @@ if TYPE_CHECKING:
|
|||||||
from dcs import Point
|
from dcs import Point
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class AirliftLayout(StandardLayout):
|
class AirliftLayout(StandardLayout):
|
||||||
nav_to_pickup: list[FlightWaypoint]
|
nav_to_pickup: list[FlightWaypoint]
|
||||||
# There will not be a pickup waypoint when the pickup airfield is the departure
|
# There will not be a pickup waypoint when the pickup airfield is the departure
|
||||||
@ -38,6 +38,20 @@ class AirliftLayout(StandardLayout):
|
|||||||
ctld_drop_off_zone: FlightWaypoint | None
|
ctld_drop_off_zone: FlightWaypoint | None
|
||||||
nav_to_home: list[FlightWaypoint]
|
nav_to_home: list[FlightWaypoint]
|
||||||
|
|
||||||
|
def delete_waypoint(self, waypoint: FlightWaypoint) -> bool:
|
||||||
|
if super().delete_waypoint(waypoint):
|
||||||
|
return True
|
||||||
|
if waypoint in self.nav_to_pickup:
|
||||||
|
self.nav_to_pickup.remove(waypoint)
|
||||||
|
return True
|
||||||
|
elif waypoint in self.nav_to_drop_off:
|
||||||
|
self.nav_to_drop_off.remove(waypoint)
|
||||||
|
return True
|
||||||
|
elif waypoint in self.nav_to_home:
|
||||||
|
self.nav_to_home.remove(waypoint)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.departure
|
yield self.departure
|
||||||
yield from self.nav_to_pickup
|
yield from self.nav_to_pickup
|
||||||
|
|||||||
@ -18,7 +18,7 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class CasLayout(PatrollingLayout):
|
class CasLayout(PatrollingLayout):
|
||||||
target: FlightWaypoint
|
target: FlightWaypoint
|
||||||
|
|
||||||
|
|||||||
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class CustomLayout(Layout):
|
class CustomLayout(Layout):
|
||||||
custom_waypoints: list[FlightWaypoint]
|
custom_waypoints: list[FlightWaypoint]
|
||||||
|
|
||||||
|
|||||||
@ -15,10 +15,18 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class FerryLayout(StandardLayout):
|
class FerryLayout(StandardLayout):
|
||||||
nav_to_destination: list[FlightWaypoint]
|
nav_to_destination: list[FlightWaypoint]
|
||||||
|
|
||||||
|
def delete_waypoint(self, waypoint: FlightWaypoint) -> bool:
|
||||||
|
if super().delete_waypoint(waypoint):
|
||||||
|
return True
|
||||||
|
if waypoint in self.nav_to_destination:
|
||||||
|
self.nav_to_destination.remove(waypoint)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.departure
|
yield self.departure
|
||||||
yield from self.nav_to_destination
|
yield from self.nav_to_destination
|
||||||
|
|||||||
@ -41,7 +41,7 @@ INGRESS_TYPES = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class Layout(ABC):
|
class Layout(ABC):
|
||||||
departure: FlightWaypoint
|
departure: FlightWaypoint
|
||||||
|
|
||||||
@ -50,6 +50,9 @@ class Layout(ABC):
|
|||||||
"""A list of all waypoints in the flight plan, in order."""
|
"""A list of all waypoints in the flight plan, in order."""
|
||||||
return list(self.iter_waypoints())
|
return list(self.iter_waypoints())
|
||||||
|
|
||||||
|
def delete_waypoint(self, waypoint: FlightWaypoint) -> bool:
|
||||||
|
return False
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
"""Iterates over all waypoints in the flight plan, in order."""
|
"""Iterates over all waypoints in the flight plan, in order."""
|
||||||
raise NotImplementedError
|
raise NotImplementedError
|
||||||
|
|||||||
@ -4,7 +4,7 @@ from abc import ABC, abstractmethod
|
|||||||
from dataclasses import dataclass
|
from dataclasses import dataclass
|
||||||
from datetime import timedelta
|
from datetime import timedelta
|
||||||
from functools import cached_property
|
from functools import cached_property
|
||||||
from typing import Any, TYPE_CHECKING, TypeGuard
|
from typing import Any, TYPE_CHECKING, TypeGuard, Optional
|
||||||
|
|
||||||
from game.typeguard import self_type_guard
|
from game.typeguard import self_type_guard
|
||||||
from game.utils import Speed
|
from game.utils import Speed
|
||||||
@ -16,14 +16,28 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class FormationLayout(LoiterLayout, ABC):
|
class FormationLayout(LoiterLayout, ABC):
|
||||||
nav_to: list[FlightWaypoint]
|
nav_to: list[FlightWaypoint]
|
||||||
join: FlightWaypoint
|
join: FlightWaypoint
|
||||||
split: FlightWaypoint
|
split: FlightWaypoint
|
||||||
refuel: FlightWaypoint
|
refuel: Optional[FlightWaypoint]
|
||||||
nav_from: list[FlightWaypoint]
|
nav_from: list[FlightWaypoint]
|
||||||
|
|
||||||
|
def delete_waypoint(self, waypoint: FlightWaypoint) -> bool:
|
||||||
|
if super().delete_waypoint(waypoint):
|
||||||
|
return True
|
||||||
|
if waypoint in self.nav_to:
|
||||||
|
self.nav_to.remove(waypoint)
|
||||||
|
return True
|
||||||
|
elif waypoint in self.nav_from:
|
||||||
|
self.nav_from.remove(waypoint)
|
||||||
|
return True
|
||||||
|
elif waypoint == self.refuel:
|
||||||
|
self.refuel = None
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
class FormationFlightPlan(LoiterFlightPlan, ABC):
|
class FormationFlightPlan(LoiterFlightPlan, ABC):
|
||||||
@property
|
@property
|
||||||
|
|||||||
@ -10,7 +10,7 @@ from dcs import Point
|
|||||||
|
|
||||||
from game.flightplan import HoldZoneGeometry
|
from game.flightplan import HoldZoneGeometry
|
||||||
from game.theater import MissionTarget
|
from game.theater import MissionTarget
|
||||||
from game.utils import Speed, meters, Distance
|
from game.utils import Speed, meters
|
||||||
from .flightplan import FlightPlan
|
from .flightplan import FlightPlan
|
||||||
from .formation import FormationFlightPlan, FormationLayout
|
from .formation import FormationFlightPlan, FormationLayout
|
||||||
from .ibuilder import IBuilder
|
from .ibuilder import IBuilder
|
||||||
@ -130,7 +130,7 @@ class FormationAttackFlightPlan(FormationFlightPlan, ABC):
|
|||||||
return super().tot_for_waypoint(waypoint)
|
return super().tot_for_waypoint(waypoint)
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class FormationAttackLayout(FormationLayout):
|
class FormationAttackLayout(FormationLayout):
|
||||||
ingress: FlightWaypoint
|
ingress: FlightWaypoint
|
||||||
targets: list[FlightWaypoint]
|
targets: list[FlightWaypoint]
|
||||||
|
|||||||
@ -13,7 +13,7 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class LoiterLayout(StandardLayout, ABC):
|
class LoiterLayout(StandardLayout, ABC):
|
||||||
hold: FlightWaypoint
|
hold: FlightWaypoint
|
||||||
|
|
||||||
|
|||||||
@ -16,13 +16,24 @@ if TYPE_CHECKING:
|
|||||||
from .flightplan import FlightPlan
|
from .flightplan import FlightPlan
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class PatrollingLayout(StandardLayout):
|
class PatrollingLayout(StandardLayout):
|
||||||
nav_to: list[FlightWaypoint]
|
nav_to: list[FlightWaypoint]
|
||||||
patrol_start: FlightWaypoint
|
patrol_start: FlightWaypoint
|
||||||
patrol_end: FlightWaypoint
|
patrol_end: FlightWaypoint
|
||||||
nav_from: list[FlightWaypoint]
|
nav_from: list[FlightWaypoint]
|
||||||
|
|
||||||
|
def delete_waypoint(self, waypoint: FlightWaypoint) -> bool:
|
||||||
|
if super().delete_waypoint(waypoint):
|
||||||
|
return True
|
||||||
|
if waypoint in self.nav_to:
|
||||||
|
self.nav_to.remove(waypoint)
|
||||||
|
return True
|
||||||
|
elif waypoint in self.nav_from:
|
||||||
|
self.nav_from.remove(waypoint)
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
def iter_waypoints(self) -> Iterator[FlightWaypoint]:
|
||||||
yield self.departure
|
yield self.departure
|
||||||
yield from self.nav_to
|
yield from self.nav_to
|
||||||
|
|||||||
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class RtbLayout(StandardLayout):
|
class RtbLayout(StandardLayout):
|
||||||
abort_location: FlightWaypoint
|
abort_location: FlightWaypoint
|
||||||
nav_to_destination: list[FlightWaypoint]
|
nav_to_destination: list[FlightWaypoint]
|
||||||
|
|||||||
@ -10,12 +10,18 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class StandardLayout(Layout, ABC):
|
class StandardLayout(Layout, ABC):
|
||||||
arrival: FlightWaypoint
|
arrival: FlightWaypoint
|
||||||
divert: FlightWaypoint | None
|
divert: FlightWaypoint | None
|
||||||
bullseye: FlightWaypoint
|
bullseye: FlightWaypoint
|
||||||
|
|
||||||
|
def delete_waypoint(self, waypoint: FlightWaypoint) -> bool:
|
||||||
|
if waypoint is self.divert:
|
||||||
|
self.divert = None
|
||||||
|
return True
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
LayoutT = TypeVar("LayoutT", bound=StandardLayout)
|
LayoutT = TypeVar("LayoutT", bound=StandardLayout)
|
||||||
|
|
||||||
|
|||||||
@ -17,7 +17,7 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class SweepLayout(LoiterLayout):
|
class SweepLayout(LoiterLayout):
|
||||||
nav_to: list[FlightWaypoint]
|
nav_to: list[FlightWaypoint]
|
||||||
sweep_start: FlightWaypoint
|
sweep_start: FlightWaypoint
|
||||||
|
|||||||
@ -15,7 +15,7 @@ if TYPE_CHECKING:
|
|||||||
from ..flightwaypoint import FlightWaypoint
|
from ..flightwaypoint import FlightWaypoint
|
||||||
|
|
||||||
|
|
||||||
@dataclass(frozen=True)
|
@dataclass
|
||||||
class TarCapLayout(PatrollingLayout):
|
class TarCapLayout(PatrollingLayout):
|
||||||
refuel: FlightWaypoint | None
|
refuel: FlightWaypoint | None
|
||||||
|
|
||||||
|
|||||||
@ -437,7 +437,7 @@ class WaypointBuilder:
|
|||||||
|
|
||||||
return FlightWaypoint(
|
return FlightWaypoint(
|
||||||
"SEAD Search",
|
"SEAD Search",
|
||||||
FlightWaypointType.CUSTOM,
|
FlightWaypointType.INGRESS_SEAD,
|
||||||
hold,
|
hold,
|
||||||
self.doctrine.ingress_altitude,
|
self.doctrine.ingress_altitude,
|
||||||
alt_type="BARO",
|
alt_type="BARO",
|
||||||
|
|||||||
@ -109,13 +109,28 @@ class QFlightWaypointTab(QFrame):
|
|||||||
# Need to degrade to a custom flight plan and remove the waypoint.
|
# Need to degrade to a custom flight plan and remove the waypoint.
|
||||||
# If the waypoint is a target waypoint and is not the last target
|
# If the waypoint is a target waypoint and is not the last target
|
||||||
# waypoint, we don't need to degrade.
|
# waypoint, we don't need to degrade.
|
||||||
if isinstance(self.flight.flight_plan, FormationAttackFlightPlan):
|
fp = self.flight.flight_plan
|
||||||
is_target = waypoint in self.flight.flight_plan.target_area_waypoint.targets
|
if isinstance(fp, FormationAttackFlightPlan):
|
||||||
count = len(self.flight.flight_plan.target_area_waypoint.targets)
|
is_target = waypoint in fp.target_area_waypoint.targets
|
||||||
|
count = len(fp.target_area_waypoint.targets)
|
||||||
if is_target and count > 1:
|
if is_target and count > 1:
|
||||||
self.flight.flight_plan.target_area_waypoint.targets.remove(waypoint)
|
fp.target_area_waypoint.targets.remove(waypoint)
|
||||||
return
|
return
|
||||||
|
|
||||||
|
if fp.layout.delete_waypoint(waypoint):
|
||||||
|
return
|
||||||
|
|
||||||
|
result = QMessageBox.warning(
|
||||||
|
self,
|
||||||
|
"Degrade flight-plan?",
|
||||||
|
"Deleting the selected waypoint(s) will require degradation to a custom flight-plan. "
|
||||||
|
"A custom flight-plan will no longer respect the TOTs of the package.<br><br>"
|
||||||
|
"<b>Are you sure you wish to continue?</b>",
|
||||||
|
QMessageBox.Yes,
|
||||||
|
QMessageBox.No,
|
||||||
|
)
|
||||||
|
if result == QMessageBox.No:
|
||||||
|
return
|
||||||
self.degrade_to_custom_flight_plan()
|
self.degrade_to_custom_flight_plan()
|
||||||
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
|
assert isinstance(self.flight.flight_plan, CustomFlightPlan)
|
||||||
self.flight.flight_plan.layout.custom_waypoints.remove(waypoint)
|
self.flight.flight_plan.layout.custom_waypoints.remove(waypoint)
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user