def order_airlift_assets_at(self, control_point: ControlPoint) -> None: unclaimed_parking = control_point.unclaimed_parking(self.game) # Buy a maximum of unclaimed_parking only to prevent that aircraft procurement # take place at another base gap = min( [ self.desired_airlift_capacity(control_point) - self.current_airlift_capacity(control_point), unclaimed_parking, ] ) if gap <= 0: return if gap % 2: # Always buy in pairs since we're not trying to fill odd squadrons. Purely # aesthetic. gap += 1 if gap > unclaimed_parking: # Prevent to buy more aircraft than possible return self.game.procurement_requests_for(player=control_point.captured).append( AircraftProcurementRequest( control_point, nautical_miles(200), FlightType.TRANSPORT, gap ) )
def _affordable_aircraft_for_task( self, task: FlightType, airbase: ControlPoint, number: int, max_price: float, ) -> Optional[AircraftType]: best_choice: Optional[AircraftType] = None for unit in aircraft_for_task(task): if unit not in self.faction.aircrafts: continue if unit.price * number > max_price: continue if not airbase.can_operate(unit): continue for squadron in self.air_wing.squadrons_for(unit): if task in squadron.auto_assignable_mission_types: break else: continue # Affordable, compatible, and we have a squadron capable of the task. To # keep some variety, skip with a 50/50 chance. Might be a good idea to have # the chance to skip based on the price compared to the rest of the choices. best_choice = unit if random.choice([True, False]): break return best_choice
def operates_from(self, control_point: ControlPoint) -> bool: if not control_point.can_operate(self.aircraft): return False if control_point.is_carrier: return self.operating_bases.carrier elif control_point.is_lha: return self.operating_bases.lha else: return self.operating_bases.shore
def for_link(game: Game, a: ControlPoint, b: ControlPoint, points: list[Point], sea: bool) -> SupplyRouteJs: return SupplyRouteJs( points=[p.latlng() for p in points], front_active=not sea and a.front_is_active(b), is_sea=sea, blue=a.captured, active_transports=TransportFinder( game, a, b).describe_active_transports(sea), )
def plan_relocation(self, destination: ControlPoint, theater: ConflictTheater) -> None: if destination == self.location: logging.warning( f"Attempted to plan relocation of {self} to current location " f"{destination}. Ignoring.") return if destination == self.destination: logging.warning( f"Attempted to plan relocation of {self} to current destination " f"{destination}. Ignoring.") return if self.expected_size_next_turn > destination.unclaimed_parking(): raise RuntimeError( f"Not enough parking for {self} at {destination}.") if not destination.can_operate(self.aircraft): raise RuntimeError(f"{self} cannot operate at {destination}.") self.destination = destination self.replan_ferry_flights(theater)
def _affordable_aircraft_of_types( self, types: List[Type[FlyingType]], airbase: ControlPoint, number: int, max_price: int) -> Optional[Type[FlyingType]]: unit_pool = [u for u in self.faction.aircrafts if u in types] affordable_units = [ u for u in unit_pool if db.PRICES[u] * number <= max_price and airbase.can_operate(u) ] if not affordable_units: return None return random.choice(affordable_units)
def for_control_point(control_point: ControlPoint) -> ControlPointJs: destination = None if control_point.target_position is not None: destination = control_point.target_position.latlng() return ControlPointJs( id=control_point.id, name=control_point.name, blue=control_point.captured, position=control_point.position.latlng(), mobile=control_point.moveable and control_point.captured, destination=destination, sidc=str(control_point.sidc()), )
def cost_ratio_of_ground_unit(self, control_point: ControlPoint, unit_class: GroundUnitClass) -> float: allocations = control_point.allocated_ground_units(self.game.transfers) class_cost = 0 total_cost = 0 for unit_type, count in allocations.all.items(): cost = unit_type.price * count total_cost += cost if unit_type.unit_class is unit_class: class_cost += cost if not total_cost: return 0 return class_cost / total_cost
def _affordable_aircraft_of_types( self, types: List[Type[FlyingType]], airbase: ControlPoint, number: int, max_price: float) -> Optional[Type[FlyingType]]: best_choice: Optional[Type[FlyingType]] = None for unit in [u for u in self.faction.aircrafts if u in types]: if db.PRICES[unit] * number > max_price: continue if not airbase.can_operate(unit): continue # Affordable and compatible. To keep some variety, skip with a 50/50 # chance. Might be a good idea to have the chance to skip based on # the price compared to the rest of the choices. best_choice = unit if random.choice([True, False]): break return best_choice
def generate_for_task( self, task: FlightType, control_point: ControlPoint) -> Optional[SquadronDef]: aircraft_choice: Optional[AircraftType] = None for aircraft in aircraft_for_task(task): if aircraft not in self.faction.aircrafts: continue if not control_point.can_operate(aircraft): continue aircraft_choice = aircraft # 50/50 chance to keep looking for an aircraft that isn't as far up the # priority list to maintain some unit variety. if random.choice([True, False]): break if aircraft_choice is None: return None return self.generate_for_aircraft(aircraft_choice)