mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
* Rename pydcs_extensions/f4/__init__.py (from pydcs_extensions/f4b/__init__.py) Rename pydcs_extensions/f4/f4b.py (from pydcs_extensions/f4b/f4b.py) * Rename pydcs_extensions/f4/f4.py (from pydcs_extensions/f4/f4b.py) * Support for the updated F-4B/C mod Updated the F4.py pydcs extension to match the updated F-4B/C mod and reworked the standard payloads to add "clean" to new F-4B pylons 11 and 12. This includes a workaround to allow Liberation to use the new VSN F-4B weapons with combined 2x Aim-9js on pylons 3 and 9 underslung with bombs on ters on new pylons 11 and 12. In mission editor the combined weapons are selected in pylons 3 and 9 and their under-slung counterparts are forced onto 11 and 12 using "required" arguments in the mod's lua. All other pylon 3 and 9 weapons use "required clean" arguments. Liberation doesn't have a way to force these linkages onto pylons 11 and 12 and without them, even without clean, no weapons will load on 3 and 9 or 11 and 12. The workaround for normal weapons on the F-4B is to set the standard load-outs to "clean" on pylons 11 and 12. This allows all normal weapons to work on pylons 3 and 9 so long as pylons 11 and 12 are left as Clean. It also allows Clean into the Liberation dropdown so it can be selected later if necessary. The workaround for the 4 new weapons that combines pylons 3 with 11 and 9 with 12 is that the user has to use the matching pair on each set of pylons. For example - if F4B_LAU105_AIM9J_2_BRU42A_MK82_3 is selected for pylon 3, BRU 42A MK823 LAU105 AIM9J2 must be selected for pylon 11. Failure to do this correctly doesn't crash liberation or DCS, the result will just be either no weapons at all on either pylon or the underslung weapons on 11 and 12 floating without a pylon attaching it to the plane. When updating f4.py in the future, note that running the pydcs database export doesn't pull any data for Pylons 11 and 12. Those matching weapons / classes have to be manually defined in those pylons for the F-4B. This is noted in f4.py. * F-4 mod support update to 2.8.7.201 * Updated QGeneratorSettings.py and the changelog. * Updated F-4B and F-4C loadouts. * Added the Phantom to some additional factions. * Changelog updated: Updated support for F-4B/C Phantom mod to 2.8.7.204 The pydcs export for the latest patch was identical, which means it's also supported. * Migrator change for renamed F-4B/C folder --------- Co-authored-by: Nosajthedevil <78634843+Nosajthedevil@users.noreply.github.com> Co-authored-by: Raffson <Raffson@users.noreply.github.com>
157 lines
4.3 KiB
Python
157 lines
4.3 KiB
Python
from __future__ import annotations
|
|
|
|
import logging
|
|
import pickle
|
|
import shutil
|
|
from pathlib import Path
|
|
from typing import Optional, TYPE_CHECKING, Any
|
|
|
|
import dcs.terrain.falklands.airports
|
|
|
|
import pydcs_extensions
|
|
from game.profiling import logged_duration
|
|
|
|
if TYPE_CHECKING:
|
|
from game import Game
|
|
|
|
_dcs_saved_game_folder: Optional[str] = None
|
|
|
|
|
|
# fmt: off
|
|
class DummyObject:
|
|
def __setstate__(self, state: dict[str, Any]) -> None:
|
|
self.__dict__.update(state)
|
|
|
|
|
|
class MigrationUnpickler(pickle.Unpickler):
|
|
"""Custom unpickler to migrate campaign save-files for when components have been moved"""
|
|
def find_class(self, module: Any, name: str) -> Any:
|
|
if name == "NightMissions":
|
|
from game.settings import NightMissions
|
|
return NightMissions
|
|
if name == "Conditions":
|
|
from game.weather.conditions import Conditions
|
|
return Conditions
|
|
if name == "AtmosphericConditions":
|
|
from game.weather.atmosphericconditions import AtmosphericConditions
|
|
return AtmosphericConditions
|
|
if name == "WindConditions":
|
|
from game.weather.wind import WindConditions
|
|
return WindConditions
|
|
if name == "Clouds":
|
|
from game.weather.clouds import Clouds
|
|
return Clouds
|
|
if name == "Fog":
|
|
from game.weather.fog import Fog
|
|
return Fog
|
|
if name == "ClearSkies":
|
|
from game.weather.weather import ClearSkies
|
|
return ClearSkies
|
|
if name == "Cloudy":
|
|
from game.weather.weather import Cloudy
|
|
return Cloudy
|
|
if name == "Raining":
|
|
from game.weather.weather import Raining
|
|
return Raining
|
|
if name == "Thunderstorm":
|
|
from game.weather.weather import Thunderstorm
|
|
return Thunderstorm
|
|
if name == "Hipico":
|
|
return dcs.terrain.falklands.airports.Hipico_Flying_Club
|
|
if name in ["SaveManager", "SaveGameBundle"]:
|
|
return DummyObject
|
|
if module == "pydcs_extensions.f4b.f4b":
|
|
return pydcs_extensions.f4
|
|
return super().find_class(module, name)
|
|
# fmt: on
|
|
|
|
|
|
def setup(user_folder: str) -> None:
|
|
global _dcs_saved_game_folder
|
|
_dcs_saved_game_folder = user_folder
|
|
if not save_dir().exists():
|
|
save_dir().mkdir(parents=True)
|
|
|
|
|
|
def base_path() -> Path:
|
|
global _dcs_saved_game_folder
|
|
assert _dcs_saved_game_folder
|
|
return Path(_dcs_saved_game_folder)
|
|
|
|
|
|
def debug_dir() -> Path:
|
|
return base_path() / "Retribution" / "Debug"
|
|
|
|
|
|
def waypoint_debug_directory() -> Path:
|
|
return debug_dir() / "Waypoints"
|
|
|
|
|
|
def settings_dir() -> Path:
|
|
return base_path() / "Retribution" / "Settings"
|
|
|
|
|
|
def payloads_dir(backup: bool = False) -> Path:
|
|
payloads = base_path() / "MissionEditor" / "UnitPayloads"
|
|
if backup:
|
|
return payloads / "_retribution_backups"
|
|
return payloads
|
|
|
|
|
|
def user_custom_weapon_injections_dir() -> Path:
|
|
return base_path() / "Retribution" / "WeaponInjections"
|
|
|
|
|
|
def save_dir() -> Path:
|
|
return base_path() / "Retribution" / "Saves"
|
|
|
|
|
|
def _temporary_save_file() -> str:
|
|
return str(save_dir() / "tmpsave.retribution")
|
|
|
|
|
|
def _autosave_path() -> str:
|
|
return str(save_dir() / "autosave.retribution")
|
|
|
|
|
|
def mission_path_for(name: str) -> Path:
|
|
return base_path() / "Missions" / name
|
|
|
|
|
|
def load_game(path: str) -> Optional[Game]:
|
|
with open(path, "rb") as f:
|
|
try:
|
|
save = MigrationUnpickler(f).load()
|
|
save.savepath = path
|
|
return save
|
|
except Exception:
|
|
logging.exception("Invalid Save game")
|
|
return None
|
|
|
|
|
|
def save_game(game: Game) -> bool:
|
|
with logged_duration("Saving game"):
|
|
try:
|
|
with open(_temporary_save_file(), "wb") as f:
|
|
pickle.dump(game, f)
|
|
shutil.copy(_temporary_save_file(), game.savepath)
|
|
return True
|
|
except Exception:
|
|
logging.exception("Could not save game")
|
|
return False
|
|
|
|
|
|
def autosave(game: Game) -> bool:
|
|
"""
|
|
Autosave to the autosave location
|
|
:param game: Game to save
|
|
:return: True if saved successfully
|
|
"""
|
|
try:
|
|
with open(_autosave_path(), "wb") as f:
|
|
pickle.dump(game, f)
|
|
return True
|
|
except Exception:
|
|
logging.exception("Could not save game")
|
|
return False
|