mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
This property affects safe compat because the ID is what gets preserved in the save, but it's unfortunately also used as the display name, which means changing the display name breaks save compat. It also prevents us from changing display names without breaking faction definitions. This is the first step in fixing that. The next is adding a separate display_name property that can be updated without breaking either of those.
193 lines
6.1 KiB
Python
193 lines
6.1 KiB
Python
from abc import abstractmethod
|
|
from typing import TypeVar, Generic, Any
|
|
|
|
from game import Game
|
|
from game.coalition import Coalition
|
|
from game.dcs.aircrafttype import AircraftType
|
|
from game.dcs.groundunittype import GroundUnitType
|
|
from game.dcs.unittype import UnitType
|
|
from game.squadrons import Squadron
|
|
from game.theater import ControlPoint, ParkingType
|
|
|
|
ItemType = TypeVar("ItemType")
|
|
|
|
|
|
class TransactionError(RuntimeError):
|
|
def __init__(self, message: str) -> None:
|
|
super().__init__(message)
|
|
|
|
|
|
class PurchaseAdapter(Generic[ItemType]):
|
|
def __init__(self, coalition: Coalition) -> None:
|
|
self.coalition = coalition
|
|
|
|
def buy(self, item: ItemType, quantity: int) -> None:
|
|
for _ in range(quantity):
|
|
if self.has_pending_sales(item):
|
|
self.do_cancel_sale(item)
|
|
elif self.can_buy(item):
|
|
self.do_purchase(item)
|
|
else:
|
|
raise TransactionError(f"Cannot buy more {item}")
|
|
self.coalition.adjust_budget(-self.price_of(item))
|
|
|
|
def sell(self, item: ItemType, quantity: int) -> None:
|
|
for _ in range(quantity):
|
|
if self.has_pending_orders(item):
|
|
self.do_cancel_purchase(item)
|
|
elif self.can_sell(item):
|
|
self.do_sale(item)
|
|
else:
|
|
raise TransactionError(f"Cannot sell more {item}")
|
|
self.coalition.adjust_budget(self.price_of(item))
|
|
|
|
def has_pending_orders(self, item: ItemType) -> bool:
|
|
return self.pending_delivery_quantity(item) > 0
|
|
|
|
def has_pending_sales(self, item: ItemType) -> bool:
|
|
return self.pending_delivery_quantity(item) < 0
|
|
|
|
@abstractmethod
|
|
def current_quantity_of(self, item: ItemType) -> int:
|
|
...
|
|
|
|
@abstractmethod
|
|
def pending_delivery_quantity(self, item: ItemType) -> int:
|
|
...
|
|
|
|
def expected_quantity_next_turn(self, item: ItemType) -> int:
|
|
return self.current_quantity_of(item) + self.pending_delivery_quantity(item)
|
|
|
|
def can_buy(self, item: ItemType) -> bool:
|
|
return self.coalition.budget >= self.price_of(item)
|
|
|
|
def can_sell_or_cancel(self, item: ItemType) -> bool:
|
|
return self.can_sell(item) or self.has_pending_orders(item)
|
|
|
|
@abstractmethod
|
|
def can_sell(self, item: ItemType) -> bool:
|
|
...
|
|
|
|
@abstractmethod
|
|
def do_purchase(self, item: ItemType) -> None:
|
|
...
|
|
|
|
@abstractmethod
|
|
def do_cancel_purchase(self, item: ItemType) -> None:
|
|
...
|
|
|
|
@abstractmethod
|
|
def do_sale(self, item: ItemType) -> None:
|
|
...
|
|
|
|
@abstractmethod
|
|
def do_cancel_sale(self, item: ItemType) -> None:
|
|
...
|
|
|
|
@abstractmethod
|
|
def price_of(self, item: ItemType) -> int:
|
|
...
|
|
|
|
@abstractmethod
|
|
def name_of(self, item: ItemType, multiline: bool = False) -> str:
|
|
...
|
|
|
|
@abstractmethod
|
|
def unit_type_of(self, item: ItemType) -> UnitType[Any]:
|
|
...
|
|
|
|
|
|
class AircraftPurchaseAdapter(PurchaseAdapter[Squadron]):
|
|
def __init__(self, control_point: ControlPoint) -> None:
|
|
super().__init__(control_point.coalition)
|
|
self.control_point = control_point
|
|
|
|
def pending_delivery_quantity(self, item: Squadron) -> int:
|
|
return item.pending_deliveries
|
|
|
|
def current_quantity_of(self, item: Squadron) -> int:
|
|
return item.owned_aircraft
|
|
|
|
def can_buy(self, item: Squadron) -> bool:
|
|
parking_type = ParkingType().from_squadron(item)
|
|
unclaimed_parking = self.control_point.unclaimed_parking(parking_type)
|
|
return (
|
|
super().can_buy(item)
|
|
and unclaimed_parking > 0
|
|
and item.has_aircraft_capacity_for(1)
|
|
)
|
|
|
|
def can_sell(self, item: Squadron) -> bool:
|
|
return item.untasked_aircraft > 0
|
|
|
|
def do_purchase(self, item: Squadron) -> None:
|
|
item.pending_deliveries += 1
|
|
|
|
def do_cancel_purchase(self, item: Squadron) -> None:
|
|
item.pending_deliveries -= 1
|
|
|
|
def do_sale(self, item: Squadron) -> None:
|
|
item.untasked_aircraft -= 1
|
|
item.pending_deliveries -= 1
|
|
|
|
def do_cancel_sale(self, item: Squadron) -> None:
|
|
item.untasked_aircraft += 1
|
|
item.pending_deliveries += 1
|
|
|
|
def price_of(self, item: Squadron) -> int:
|
|
return item.aircraft.price
|
|
|
|
def name_of(self, item: Squadron, multiline: bool = False) -> str:
|
|
if multiline:
|
|
separator = "<br />"
|
|
else:
|
|
separator = " "
|
|
return separator.join([item.aircraft.variant_id, str(item)])
|
|
|
|
def unit_type_of(self, item: Squadron) -> AircraftType:
|
|
return item.aircraft
|
|
|
|
|
|
class GroundUnitPurchaseAdapter(PurchaseAdapter[GroundUnitType]):
|
|
def __init__(
|
|
self, control_point: ControlPoint, coalition: Coalition, game: Game
|
|
) -> None:
|
|
super().__init__(coalition)
|
|
self.control_point = control_point
|
|
self.game = game
|
|
|
|
def pending_delivery_quantity(self, item: GroundUnitType) -> int:
|
|
return self.control_point.ground_unit_orders.pending_orders(item)
|
|
|
|
def current_quantity_of(self, item: GroundUnitType) -> int:
|
|
return self.control_point.base.total_units_of_type(item)
|
|
|
|
def can_buy(self, item: GroundUnitType) -> bool:
|
|
return super().can_buy(item) and self.control_point.has_ground_unit_source(
|
|
self.game
|
|
)
|
|
|
|
def can_sell(self, item: GroundUnitType) -> bool:
|
|
return False
|
|
|
|
def do_purchase(self, item: GroundUnitType) -> None:
|
|
self.control_point.ground_unit_orders.order({item: 1})
|
|
|
|
def do_cancel_purchase(self, item: GroundUnitType) -> None:
|
|
self.control_point.ground_unit_orders.sell({item: 1})
|
|
|
|
def do_sale(self, item: GroundUnitType) -> None:
|
|
raise TransactionError("Sale of ground units not allowed")
|
|
|
|
def do_cancel_sale(self, item: GroundUnitType) -> None:
|
|
raise TransactionError("Sale of ground units not allowed")
|
|
|
|
def price_of(self, item: GroundUnitType) -> int:
|
|
return item.price
|
|
|
|
def name_of(self, item: GroundUnitType, multiline: bool = False) -> str:
|
|
return f"{item}"
|
|
|
|
def unit_type_of(self, item: GroundUnitType) -> GroundUnitType:
|
|
return item
|