mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +00:00
Compare commits
5 Commits
4.1.1
...
develop-4.
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40b147148b | ||
|
|
2f56bae3e5 | ||
|
|
6febf546a8 | ||
|
|
7754e5fd4d | ||
|
|
81058d9e25 |
14
changelog.md
14
changelog.md
@@ -1,3 +1,17 @@
|
|||||||
|
# 4.1.2
|
||||||
|
|
||||||
|
Saves from 4.1.1 are compatible with 4.1.2.
|
||||||
|
|
||||||
|
## Features/Improvements
|
||||||
|
|
||||||
|
* **[Mission Generation]** EWRs are now also headed towards the center of the conflict
|
||||||
|
* **[UI]** Sell Button for aircraft will be disabled if there are no units available to be sold or all are already assigned to a mission
|
||||||
|
|
||||||
|
## Fixes
|
||||||
|
|
||||||
|
* **[UI]** Selling of Units is now visible again in the UI dialog and shows the correct amount of sold units
|
||||||
|
* **[Mission Generation]** Mission results and other files will now be opened with enforced utf-8 encoding to prevent an issue where destroyed ground units were untracked because of special characters in their names.
|
||||||
|
|
||||||
# 4.1.1
|
# 4.1.1
|
||||||
|
|
||||||
Saves from 4.1.0 are compatible with 4.1.1.
|
Saves from 4.1.0 are compatible with 4.1.1.
|
||||||
|
|||||||
@@ -288,7 +288,7 @@ class AircraftType(UnitType[Type[FlyingType]]):
|
|||||||
logging.warning(f"No data for {aircraft.id}; it will not be available")
|
logging.warning(f"No data for {aircraft.id}; it will not be available")
|
||||||
return
|
return
|
||||||
|
|
||||||
with data_path.open() as data_file:
|
with data_path.open(encoding="utf-8") as data_file:
|
||||||
data = yaml.safe_load(data_file)
|
data = yaml.safe_load(data_file)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ class GroundUnitType(UnitType[Type[VehicleType]]):
|
|||||||
logging.warning(f"No data for {vehicle.id}; it will not be available")
|
logging.warning(f"No data for {vehicle.id}; it will not be available")
|
||||||
return
|
return
|
||||||
|
|
||||||
with data_path.open() as data_file:
|
with data_path.open(encoding="utf-8") as data_file:
|
||||||
data = yaml.safe_load(data_file)
|
data = yaml.safe_load(data_file)
|
||||||
|
|
||||||
try:
|
try:
|
||||||
|
|||||||
@@ -389,7 +389,7 @@ class PollDebriefingFileThread(threading.Thread):
|
|||||||
os.path.isfile("state.json")
|
os.path.isfile("state.json")
|
||||||
and os.path.getmtime("state.json") > last_modified
|
and os.path.getmtime("state.json") > last_modified
|
||||||
):
|
):
|
||||||
with open("state.json", "r") as json_file:
|
with open("state.json", "r", encoding="utf-8") as json_file:
|
||||||
json_data = json.load(json_file)
|
json_data = json.load(json_file)
|
||||||
debriefing = Debriefing(json_data, self.game, self.unit_map)
|
debriefing = Debriefing(json_data, self.game, self.unit_map)
|
||||||
self.callback(debriefing)
|
self.callback(debriefing)
|
||||||
|
|||||||
@@ -63,7 +63,7 @@ class Operation:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def prepare(cls, game: Game) -> None:
|
def prepare(cls, game: Game) -> None:
|
||||||
with open("resources/default_options.lua", "r") as f:
|
with open("resources/default_options.lua", "r", encoding="utf-8") as f:
|
||||||
options_dict = loads(f.read())["options"]
|
options_dict = loads(f.read())["options"]
|
||||||
cls._set_mission(Mission(game.theater.terrain))
|
cls._set_mission(Mission(game.theater.terrain))
|
||||||
cls.game = game
|
cls.game = game
|
||||||
|
|||||||
@@ -39,9 +39,8 @@ class PendingUnitDeliveries:
|
|||||||
|
|
||||||
def sell(self, units: dict[UnitType[Any], int]) -> None:
|
def sell(self, units: dict[UnitType[Any], int]) -> None:
|
||||||
for k, v in units.items():
|
for k, v in units.items():
|
||||||
if self.units[k] > v:
|
self.units[k] -= v
|
||||||
self.units[k] -= v
|
if self.units[k] == 0:
|
||||||
else:
|
|
||||||
del self.units[k]
|
del self.units[k]
|
||||||
|
|
||||||
def refund_all(self, game: Game) -> None:
|
def refund_all(self, game: Game) -> None:
|
||||||
|
|||||||
@@ -3,7 +3,7 @@ from pathlib import Path
|
|||||||
|
|
||||||
MAJOR_VERSION = 4
|
MAJOR_VERSION = 4
|
||||||
MINOR_VERSION = 1
|
MINOR_VERSION = 1
|
||||||
MICRO_VERSION = 1
|
MICRO_VERSION = 2
|
||||||
|
|
||||||
|
|
||||||
def _build_version_string() -> str:
|
def _build_version_string() -> str:
|
||||||
@@ -12,7 +12,7 @@ def _build_version_string() -> str:
|
|||||||
]
|
]
|
||||||
build_number_path = Path("resources/buildnumber")
|
build_number_path = Path("resources/buildnumber")
|
||||||
if build_number_path.exists():
|
if build_number_path.exists():
|
||||||
with build_number_path.open("r") as build_number_file:
|
with build_number_path.open("r", encoding="utf-8") as build_number_file:
|
||||||
components.append(build_number_file.readline())
|
components.append(build_number_file.readline())
|
||||||
|
|
||||||
if not Path("resources/final").exists():
|
if not Path("resources/final").exists():
|
||||||
|
|||||||
@@ -16,7 +16,11 @@ class EwrGenerator(VehicleGroupGenerator[EwrGroundObject]):
|
|||||||
|
|
||||||
def generate(self) -> None:
|
def generate(self) -> None:
|
||||||
self.add_unit(
|
self.add_unit(
|
||||||
self.unit_type, "EWR", self.position.x, self.position.y, self.heading
|
self.unit_type,
|
||||||
|
"EWR",
|
||||||
|
self.position.x,
|
||||||
|
self.position.y,
|
||||||
|
self.heading_to_conflict(),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -68,7 +68,8 @@ def run_ui(game: Optional[Game]) -> None:
|
|||||||
# init the theme and load the stylesheet based on the theme index
|
# init the theme and load the stylesheet based on the theme index
|
||||||
liberation_theme.init()
|
liberation_theme.init()
|
||||||
with open(
|
with open(
|
||||||
"./resources/stylesheets/" + liberation_theme.get_theme_css_file()
|
"./resources/stylesheets/" + liberation_theme.get_theme_css_file(),
|
||||||
|
encoding="utf-8",
|
||||||
) as stylesheet:
|
) as stylesheet:
|
||||||
logging.info("Loading stylesheet: %s", liberation_theme.get_theme_css_file())
|
logging.info("Loading stylesheet: %s", liberation_theme.get_theme_css_file())
|
||||||
app.setStyleSheet(stylesheet.read())
|
app.setStyleSheet(stylesheet.read())
|
||||||
|
|||||||
@@ -228,7 +228,7 @@ class QWaitingForMissionResultWindow(QDialog):
|
|||||||
)
|
)
|
||||||
print(file)
|
print(file)
|
||||||
try:
|
try:
|
||||||
with open(file[0], "r") as json_file:
|
with open(file[0], "r", encoding="utf-8") as json_file:
|
||||||
json_data = json.load(json_file)
|
json_data = json.load(json_file)
|
||||||
json_data["mission_ended"] = True
|
json_data["mission_ended"] = True
|
||||||
debriefing = Debriefing(json_data, self.game, self.unit_map)
|
debriefing = Debriefing(json_data, self.game, self.unit_map)
|
||||||
|
|||||||
@@ -80,7 +80,13 @@ class PurchaseGroup(QGroupBox):
|
|||||||
|
|
||||||
def update_state(self) -> None:
|
def update_state(self) -> None:
|
||||||
self.buy_button.setEnabled(self.recruiter.enable_purchase(self.unit_type))
|
self.buy_button.setEnabled(self.recruiter.enable_purchase(self.unit_type))
|
||||||
|
self.buy_button.setToolTip(
|
||||||
|
self.recruiter.purchase_tooltip(self.buy_button.isEnabled())
|
||||||
|
)
|
||||||
self.sell_button.setEnabled(self.recruiter.enable_sale(self.unit_type))
|
self.sell_button.setEnabled(self.recruiter.enable_sale(self.unit_type))
|
||||||
|
self.sell_button.setToolTip(
|
||||||
|
self.recruiter.sell_tooltip(self.sell_button.isEnabled())
|
||||||
|
)
|
||||||
self.amount_bought.setText(f"<b>{self.pending_units}</b>")
|
self.amount_bought.setText(f"<b>{self.pending_units}</b>")
|
||||||
|
|
||||||
|
|
||||||
@@ -223,6 +229,18 @@ class QRecruitBehaviour:
|
|||||||
def enable_sale(self, unit_type: UnitType) -> bool:
|
def enable_sale(self, unit_type: UnitType) -> bool:
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def purchase_tooltip(self, is_enabled: bool) -> str:
|
||||||
|
if is_enabled:
|
||||||
|
return "Buy unit. Use Shift or Ctrl key to buy multiple units at once."
|
||||||
|
else:
|
||||||
|
return "Unit can not be bought."
|
||||||
|
|
||||||
|
def sell_tooltip(self, is_enabled: bool) -> str:
|
||||||
|
if is_enabled:
|
||||||
|
return "Sell unit. Use Shift or Ctrl key to buy multiple units at once."
|
||||||
|
else:
|
||||||
|
return "Unit can not be sold."
|
||||||
|
|
||||||
def info(self, unit_type: UnitType) -> None:
|
def info(self, unit_type: UnitType) -> None:
|
||||||
self.info_window = QUnitInfoWindow(self.game_model.game, unit_type)
|
self.info_window = QUnitInfoWindow(self.game_model.game, unit_type)
|
||||||
self.info_window.show()
|
self.info_window.show()
|
||||||
|
|||||||
@@ -85,9 +85,13 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
|
|||||||
return True
|
return True
|
||||||
|
|
||||||
def enable_sale(self, unit_type: AircraftType) -> bool:
|
def enable_sale(self, unit_type: AircraftType) -> bool:
|
||||||
if not self.cp.can_operate(unit_type):
|
return self.can_be_sold(unit_type)
|
||||||
return False
|
|
||||||
return True
|
def sell_tooltip(self, is_enabled: bool) -> str:
|
||||||
|
if is_enabled:
|
||||||
|
return "Sell unit. Use Shift or Ctrl key to sell multiple units at once."
|
||||||
|
else:
|
||||||
|
return "Can not be sold because either no aircraft are available or are already assigned to a mission."
|
||||||
|
|
||||||
def buy(self, unit_type: AircraftType) -> bool:
|
def buy(self, unit_type: AircraftType) -> bool:
|
||||||
if self.maximum_units > 0:
|
if self.maximum_units > 0:
|
||||||
@@ -112,24 +116,32 @@ class QAircraftRecruitmentMenu(QFrame, QRecruitBehaviour):
|
|||||||
self.hangar_status.update_label()
|
self.hangar_status.update_label()
|
||||||
return True
|
return True
|
||||||
|
|
||||||
|
def can_be_sold(self, unit_type: AircraftType) -> bool:
|
||||||
|
inventory = self.game_model.game.aircraft_inventory.for_control_point(self.cp)
|
||||||
|
pending_deliveries = self.pending_deliveries.units.get(unit_type, 0)
|
||||||
|
return self.cp.can_operate(unit_type) and (
|
||||||
|
pending_deliveries > 0 or inventory.available(unit_type) > 0
|
||||||
|
)
|
||||||
|
|
||||||
def sell(self, unit_type: AircraftType) -> bool:
|
def sell(self, unit_type: AircraftType) -> bool:
|
||||||
# Don't need to remove aircraft from the inventory if we're canceling
|
# Don't need to remove aircraft from the inventory if we're canceling
|
||||||
# orders.
|
# orders.
|
||||||
if self.pending_deliveries.units.get(unit_type, 0) <= 0:
|
if not self.can_be_sold(unit_type):
|
||||||
global_inventory = self.game_model.game.aircraft_inventory
|
QMessageBox.critical(
|
||||||
inventory = global_inventory.for_control_point(self.cp)
|
self,
|
||||||
try:
|
"Could not sell aircraft",
|
||||||
inventory.remove_aircraft(unit_type, 1)
|
f"Attempted to sell one {unit_type} at {self.cp.name} "
|
||||||
except ValueError:
|
"but none are available. Are all aircraft currently "
|
||||||
QMessageBox.critical(
|
"assigned to a mission?",
|
||||||
self,
|
QMessageBox.Ok,
|
||||||
"Could not sell aircraft",
|
)
|
||||||
f"Attempted to sell one {unit_type} at {self.cp.name} "
|
return False
|
||||||
"but none are available. Are all aircraft currently "
|
|
||||||
"assigned to a mission?",
|
inventory = self.game_model.game.aircraft_inventory.for_control_point(self.cp)
|
||||||
QMessageBox.Ok,
|
pending_deliveries = self.pending_deliveries.units.get(unit_type, 0)
|
||||||
)
|
if pending_deliveries <= 0 < inventory.available(unit_type):
|
||||||
return False
|
inventory.remove_aircraft(unit_type, 1)
|
||||||
|
|
||||||
super().sell(unit_type)
|
super().sell(unit_type)
|
||||||
self.hangar_status.update_label()
|
self.hangar_status.update_label()
|
||||||
|
|
||||||
|
|||||||
@@ -42,7 +42,7 @@ class Campaign:
|
|||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
def from_json(cls, path: Path) -> Campaign:
|
def from_json(cls, path: Path) -> Campaign:
|
||||||
with path.open() as campaign_file:
|
with path.open(encoding="utf-8") as campaign_file:
|
||||||
data = json.load(campaign_file)
|
data = json.load(campaign_file)
|
||||||
|
|
||||||
sanitized_theater = data["theater"].replace(" ", "")
|
sanitized_theater = data["theater"].replace(" ", "")
|
||||||
|
|||||||
Reference in New Issue
Block a user