mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Simplfy fast forward settings, introduce ability to skip combat instead of resolving. (#3448)
This PR simplifies fast forward settings and introduces the ability to skip combat instead of resolving.
This commit is contained in:
@@ -8,6 +8,9 @@ from .atdeparture import AtDeparture
|
||||
from .taxi import Taxi
|
||||
from ..starttype import StartType
|
||||
|
||||
from game.settings.settings import FastForwardStopCondition
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.ato.flight import Flight
|
||||
from game.settings import Settings
|
||||
@@ -37,7 +40,8 @@ class StartUp(AtDeparture):
|
||||
def should_halt_sim(self) -> bool:
|
||||
if (
|
||||
self.flight.client_count > 0
|
||||
and self.settings.player_mission_interrupts_sim_at is StartType.COLD
|
||||
and self.settings.fast_forward_stop_condition
|
||||
== FastForwardStopCondition.PLAYER_STARTUP
|
||||
):
|
||||
logging.info(
|
||||
f"Interrupting simulation because {self.flight} has players and has "
|
||||
|
||||
@@ -9,6 +9,8 @@ from .navigating import Navigating
|
||||
from ..starttype import StartType
|
||||
from ...utils import LBS_TO_KG
|
||||
|
||||
from game.settings.settings import FastForwardStopCondition
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.ato.flight import Flight
|
||||
from game.settings import Settings
|
||||
@@ -45,7 +47,8 @@ class Takeoff(AtDeparture):
|
||||
def should_halt_sim(self) -> bool:
|
||||
if (
|
||||
self.flight.client_count > 0
|
||||
and self.settings.player_mission_interrupts_sim_at is StartType.RUNWAY
|
||||
and self.settings.fast_forward_stop_condition
|
||||
== FastForwardStopCondition.PLAYER_TAKEOFF
|
||||
):
|
||||
logging.info(
|
||||
f"Interrupting simulation because {self.flight} has players and has "
|
||||
|
||||
@@ -8,6 +8,8 @@ from .atdeparture import AtDeparture
|
||||
from .takeoff import Takeoff
|
||||
from ..starttype import StartType
|
||||
|
||||
from game.settings.settings import FastForwardStopCondition
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.ato.flight import Flight
|
||||
from game.settings import Settings
|
||||
@@ -37,7 +39,8 @@ class Taxi(AtDeparture):
|
||||
def should_halt_sim(self) -> bool:
|
||||
if (
|
||||
self.flight.client_count > 0
|
||||
and self.settings.player_mission_interrupts_sim_at is StartType.WARM
|
||||
and self.settings.fast_forward_stop_condition
|
||||
== FastForwardStopCondition.PLAYER_TAXI
|
||||
):
|
||||
logging.info(
|
||||
f"Interrupting simulation because {self.flight} has players and has "
|
||||
|
||||
@@ -28,6 +28,23 @@ class AutoAtoBehavior(Enum):
|
||||
Prefer = "Prefer player pilots"
|
||||
|
||||
|
||||
@unique
|
||||
class FastForwardStopCondition(Enum):
|
||||
DISABLED = "Fast forward disabled"
|
||||
FIRST_CONTACT = "First contact"
|
||||
PLAYER_TAKEOFF = "Player takeoff time"
|
||||
PLAYER_TAXI = "Player taxi time"
|
||||
PLAYER_STARTUP = "Player startup time"
|
||||
MANUAL = "Manual fast forward control"
|
||||
|
||||
|
||||
@unique
|
||||
class CombatResolutionMethod(Enum):
|
||||
PAUSE = "Pause simulation"
|
||||
RESOLVE = "Resolve combat"
|
||||
SKIP = "Skip combat"
|
||||
|
||||
|
||||
DIFFICULTY_PAGE = "Difficulty"
|
||||
|
||||
AI_DIFFICULTY_SECTION = "AI Difficulty"
|
||||
@@ -293,7 +310,7 @@ class Settings:
|
||||
"Tactical commander",
|
||||
page=MISSION_GENERATOR_PAGE,
|
||||
section=COMMANDERS_SECTION,
|
||||
default=1,
|
||||
default=0,
|
||||
min=0,
|
||||
max=100,
|
||||
)
|
||||
@@ -309,7 +326,7 @@ class Settings:
|
||||
"Observer",
|
||||
page=MISSION_GENERATOR_PAGE,
|
||||
section=COMMANDERS_SECTION,
|
||||
default=1,
|
||||
default=0,
|
||||
min=0,
|
||||
max=100,
|
||||
)
|
||||
@@ -327,19 +344,6 @@ class Settings:
|
||||
"run out of fuel when players would not."
|
||||
),
|
||||
)
|
||||
fast_forward_to_first_contact: bool = boolean_option(
|
||||
"Fast forward mission to first contact (WIP)",
|
||||
page=MISSION_GENERATOR_PAGE,
|
||||
section=GAMEPLAY_SECTION,
|
||||
default=False,
|
||||
detail=(
|
||||
"If enabled, the mission will be generated at the point of first contact. "
|
||||
"If you enable this option, you will not be able to create new flights "
|
||||
'after pressing "TAKE OFF". Doing so will create an error the next time '
|
||||
'you press "TAKE OFF". Save your game first if you want to make '
|
||||
"modifications."
|
||||
),
|
||||
)
|
||||
reload_pre_sim_checkpoint_on_abort: bool = boolean_option(
|
||||
"Reset mission to pre-take off conditions on abort",
|
||||
page=MISSION_GENERATOR_PAGE,
|
||||
@@ -351,37 +355,44 @@ class Settings:
|
||||
"your game after aborting take off."
|
||||
),
|
||||
)
|
||||
player_mission_interrupts_sim_at: Optional[StartType] = choices_option(
|
||||
"Player missions interrupt fast forward",
|
||||
fast_forward_stop_condition: FastForwardStopCondition = choices_option(
|
||||
"Fast forward until",
|
||||
page=MISSION_GENERATOR_PAGE,
|
||||
section=GAMEPLAY_SECTION,
|
||||
default=None,
|
||||
default=FastForwardStopCondition.DISABLED,
|
||||
choices={
|
||||
"Never": None,
|
||||
"At startup time": StartType.COLD,
|
||||
"At taxi time": StartType.WARM,
|
||||
"At takeoff time": StartType.RUNWAY,
|
||||
"No fast forward": FastForwardStopCondition.DISABLED,
|
||||
"Player startup time": FastForwardStopCondition.PLAYER_STARTUP,
|
||||
"Player taxi time": FastForwardStopCondition.PLAYER_TAXI,
|
||||
"Player takeoff time": FastForwardStopCondition.PLAYER_TAKEOFF,
|
||||
"First contact": FastForwardStopCondition.FIRST_CONTACT,
|
||||
"Manual": FastForwardStopCondition.MANUAL,
|
||||
},
|
||||
detail=(
|
||||
"Determines what player mission states will interrupt fast-forwarding to "
|
||||
"first contact, if enabled. If never is selected player missions will not "
|
||||
"impact simulation and player missions may be generated mid-flight. The "
|
||||
"other options will cause the mission to be generated as soon as a player "
|
||||
"mission reaches the set state or at first contact, whichever comes first."
|
||||
"Determines when fast forwarding stops: "
|
||||
"No fast forward: disables fast forward. "
|
||||
"Player startup time: fast forward until player startup time. "
|
||||
"Player taxi time: fast forward until player taxi time. "
|
||||
"Player takeoff time: fast forward until player takeoff time. "
|
||||
"First contact: fast forward until first contact between blue and red units. "
|
||||
"Manual: manually control fast forward. Show manual controls with --show-sim-speed-controls."
|
||||
),
|
||||
)
|
||||
auto_resolve_combat: bool = boolean_option(
|
||||
"Auto-resolve combat during fast-forward (WIP)",
|
||||
combat_resolution_method: CombatResolutionMethod = choices_option(
|
||||
"Resolve combat when fast forwarding by",
|
||||
page=MISSION_GENERATOR_PAGE,
|
||||
section=GAMEPLAY_SECTION,
|
||||
default=False,
|
||||
default=CombatResolutionMethod.PAUSE,
|
||||
choices={
|
||||
"Pause": CombatResolutionMethod.PAUSE,
|
||||
"Resolving combat (WIP)": CombatResolutionMethod.RESOLVE,
|
||||
"Skipping combat": CombatResolutionMethod.SKIP,
|
||||
},
|
||||
detail=(
|
||||
'Requires a "Player missions interrupt fast forward" setting other than '
|
||||
'"Never" If enabled, aircraft entering combat during fast forward will have'
|
||||
"their combat auto-resolved after a period of time. This allows the "
|
||||
"simulation to advance further into the mission before requiring mission "
|
||||
"generation, but simulation is currently very rudimentary so may result in "
|
||||
"huge losses."
|
||||
"Determines what happens when combat occurs when fast forwarding. "
|
||||
"Pause: pause fast forward and generate mission. Fast forwarding may stop before the condition specified in the above setting. "
|
||||
"Resolving combat (WIP): auto resolve combat. This method is very rudimentary and will result in large losses. "
|
||||
"Skipping combat: skip combat as if it did not occur."
|
||||
),
|
||||
)
|
||||
supercarrier: bool = boolean_option(
|
||||
|
||||
@@ -10,6 +10,7 @@ from typing_extensions import TYPE_CHECKING
|
||||
from game.ato.flightstate import (
|
||||
Uninitialized,
|
||||
)
|
||||
from game.settings.settings import FastForwardStopCondition, CombatResolutionMethod
|
||||
from .combat import CombatInitiator, FrozenCombat
|
||||
from .gameupdateevents import GameUpdateEvents
|
||||
from .simulationresults import SimulationResults
|
||||
@@ -32,7 +33,7 @@ class AircraftSimulation:
|
||||
def on_game_tick(
|
||||
self, events: GameUpdateEvents, time: datetime, duration: timedelta
|
||||
) -> None:
|
||||
if not self.game.settings.auto_resolve_combat and self.combats:
|
||||
if not self._auto_resolve_combat() and self.combats:
|
||||
logging.error(
|
||||
"Cannot resume simulation because aircraft are in combat and "
|
||||
"auto-resolve is disabled"
|
||||
@@ -42,7 +43,13 @@ class AircraftSimulation:
|
||||
|
||||
still_active = []
|
||||
for combat in self.combats:
|
||||
if combat.on_game_tick(time, duration, self.results, events):
|
||||
if combat.on_game_tick(
|
||||
time,
|
||||
duration,
|
||||
self.results,
|
||||
events,
|
||||
self.game.settings.combat_resolution_method,
|
||||
):
|
||||
events.end_combat(combat)
|
||||
else:
|
||||
still_active.append(combat)
|
||||
@@ -61,7 +68,7 @@ class AircraftSimulation:
|
||||
events.complete_simulation()
|
||||
return
|
||||
|
||||
if not self.game.settings.auto_resolve_combat and self.combats:
|
||||
if not self._auto_resolve_combat() and self.combats:
|
||||
events.complete_simulation()
|
||||
|
||||
def set_initial_flight_states(self) -> None:
|
||||
@@ -80,3 +87,11 @@ class AircraftSimulation:
|
||||
)
|
||||
for package in packages:
|
||||
yield from package.flights
|
||||
|
||||
def _auto_resolve_combat(self) -> bool:
|
||||
return (
|
||||
self.game.settings.fast_forward_stop_condition
|
||||
!= FastForwardStopCondition.DISABLED
|
||||
and self.game.settings.combat_resolution_method
|
||||
!= CombatResolutionMethod.PAUSE
|
||||
)
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING
|
||||
from shapely.ops import unary_union
|
||||
|
||||
from game.ato.flightstate import InCombat, InFlight
|
||||
from game.settings.settings import CombatResolutionMethod
|
||||
from game.utils import dcs_to_shapely_point
|
||||
from .joinablecombat import JoinableCombat
|
||||
from .. import GameUpdateEvents
|
||||
@@ -67,7 +68,15 @@ class AirCombat(JoinableCombat):
|
||||
events: GameUpdateEvents,
|
||||
time: datetime,
|
||||
elapsed_time: timedelta,
|
||||
resolution_method: CombatResolutionMethod,
|
||||
) -> None:
|
||||
|
||||
if resolution_method is CombatResolutionMethod.SKIP:
|
||||
for flight in self.flights:
|
||||
assert isinstance(flight.state, InCombat)
|
||||
flight.state.exit_combat(events, time, elapsed_time)
|
||||
return
|
||||
|
||||
blue = []
|
||||
red = []
|
||||
for flight in self.flights:
|
||||
|
||||
@@ -8,6 +8,7 @@ from typing import TYPE_CHECKING
|
||||
from .frozencombat import FrozenCombat
|
||||
from .. import GameUpdateEvents
|
||||
from ...ato.flightstate import InCombat
|
||||
from game.settings.settings import CombatResolutionMethod
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from game.ato import Flight
|
||||
@@ -34,6 +35,7 @@ class AtIp(FrozenCombat):
|
||||
events: GameUpdateEvents,
|
||||
time: datetime,
|
||||
elapsed_time: timedelta,
|
||||
resolution_method: CombatResolutionMethod,
|
||||
) -> None:
|
||||
logging.debug(
|
||||
f"{self.flight} attack on {self.flight.package.target} auto-resolved with "
|
||||
|
||||
@@ -7,6 +7,7 @@ from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from game.ato.flightstate import InCombat
|
||||
from game.settings.settings import CombatResolutionMethod
|
||||
from .frozencombat import FrozenCombat
|
||||
from .. import GameUpdateEvents
|
||||
|
||||
@@ -43,8 +44,14 @@ class DefendingSam(FrozenCombat):
|
||||
events: GameUpdateEvents,
|
||||
time: datetime,
|
||||
elapsed_time: timedelta,
|
||||
resolution_method: CombatResolutionMethod,
|
||||
) -> None:
|
||||
assert isinstance(self.flight.state, InCombat)
|
||||
|
||||
if resolution_method is CombatResolutionMethod.SKIP:
|
||||
self.flight.state.exit_combat(events, time, elapsed_time)
|
||||
return
|
||||
|
||||
if random.random() >= 0.5:
|
||||
logging.debug(f"Air defense combat auto-resolved with {self.flight} lost")
|
||||
self.flight.kill(results, events)
|
||||
|
||||
@@ -7,6 +7,7 @@ from datetime import datetime, timedelta
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from game.ato.flightstate import InCombat, InFlight
|
||||
from game.settings.settings import CombatResolutionMethod
|
||||
from .. import GameUpdateEvents
|
||||
|
||||
if TYPE_CHECKING:
|
||||
@@ -26,10 +27,11 @@ class FrozenCombat(ABC):
|
||||
duration: timedelta,
|
||||
results: SimulationResults,
|
||||
events: GameUpdateEvents,
|
||||
resolution_method: CombatResolutionMethod,
|
||||
) -> bool:
|
||||
self.elapsed_time += duration
|
||||
if self.elapsed_time >= self.freeze_duration:
|
||||
self.resolve(results, events, time, self.elapsed_time)
|
||||
self.resolve(results, events, time, self.elapsed_time, resolution_method)
|
||||
return True
|
||||
return False
|
||||
|
||||
@@ -40,6 +42,7 @@ class FrozenCombat(ABC):
|
||||
events: GameUpdateEvents,
|
||||
time: datetime,
|
||||
elapsed_time: timedelta,
|
||||
resolution_method: CombatResolutionMethod,
|
||||
) -> None: ...
|
||||
|
||||
@abstractmethod
|
||||
|
||||
@@ -65,7 +65,7 @@ class GameLoop:
|
||||
self.start()
|
||||
logging.info("Running sim to first contact")
|
||||
while not self.completed:
|
||||
self.tick(suppress_events=True)
|
||||
self.tick(suppress_events=False)
|
||||
|
||||
def pause_and_generate_miz(self, output: Path) -> None:
|
||||
self.pause()
|
||||
|
||||
Reference in New Issue
Block a user