mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Move the AI to the normal procurement system.
The procurement AI now uses the same system as the players. Orders are placed and take a turn to fulfill. This has a few advantages: * We no longer need special case purchase logic for the turn 0 population of opfor airbases. * Players using auto-purchase can cancel orders they don't like.
This commit is contained in:
parent
7226359e64
commit
d519aa1dad
@ -374,7 +374,11 @@ class UnitsDeliveryEvent(Event):
|
||||
|
||||
def skip(self) -> None:
|
||||
for k, v in self.units.items():
|
||||
info = Information("Ally Reinforcement", str(k.id) + " x " + str(v) + " at " + self.to_cp.name, self.game.turn)
|
||||
self.game.informations.append(info)
|
||||
if self.to_cp.captured:
|
||||
name = "Ally "
|
||||
else:
|
||||
name = "Enemy "
|
||||
self.game.message(
|
||||
f"{name} reinforcements: {k.id} x {v} at {self.to_cp.name}")
|
||||
|
||||
self.to_cp.base.commision_units(self.units)
|
||||
|
||||
43
game/game.py
43
game/game.py
@ -91,9 +91,7 @@ class Game:
|
||||
self.__destroyed_units: List[str] = []
|
||||
self.savepath = ""
|
||||
self.budget = PLAYER_BUDGET_INITIAL
|
||||
# The enemy currently doesn't buy anything on turn zero; they get
|
||||
# pre-populated airbases that are generated by the new game generator.
|
||||
self.enemy_budget = 0
|
||||
self.enemy_budget = PLAYER_BUDGET_INITIAL
|
||||
self.current_unit_id = 0
|
||||
self.current_group_id = 0
|
||||
|
||||
@ -110,6 +108,8 @@ class Game:
|
||||
cp.pending_unit_deliveries = self.units_delivery_event(cp)
|
||||
|
||||
self.sanitize_sides()
|
||||
# Turn 0 procurement.
|
||||
self.plan_procurement()
|
||||
self.on_load()
|
||||
|
||||
def generate_conditions(self) -> Conditions:
|
||||
@ -229,24 +229,8 @@ class Game:
|
||||
control_point.process_turn()
|
||||
|
||||
self.process_enemy_income()
|
||||
self.enemy_budget = ProcurementAi(
|
||||
self,
|
||||
for_player=False,
|
||||
faction=self.enemy_faction,
|
||||
manage_runways=True,
|
||||
manage_front_line=True,
|
||||
manage_aircraft=True
|
||||
).spend_budget(self.enemy_budget)
|
||||
|
||||
self.process_player_income()
|
||||
self.budget = ProcurementAi(
|
||||
self,
|
||||
for_player=True,
|
||||
faction=self.player_faction,
|
||||
manage_runways=self.settings.automate_runway_repair,
|
||||
manage_front_line=self.settings.automate_front_line_reinforcements,
|
||||
manage_aircraft=self.settings.automate_aircraft_reinforcements
|
||||
).spend_budget(self.budget)
|
||||
|
||||
if not no_action and self.turn > 1:
|
||||
for cp in self.theater.player_points():
|
||||
@ -288,6 +272,27 @@ class Game:
|
||||
gplanner.plan_groundwar()
|
||||
self.ground_planners[cp.id] = gplanner
|
||||
|
||||
self.plan_procurement()
|
||||
|
||||
def plan_procurement(self) -> None:
|
||||
self.budget = ProcurementAi(
|
||||
self,
|
||||
for_player=True,
|
||||
faction=self.player_faction,
|
||||
manage_runways=self.settings.automate_runway_repair,
|
||||
manage_front_line=self.settings.automate_front_line_reinforcements,
|
||||
manage_aircraft=self.settings.automate_aircraft_reinforcements
|
||||
).spend_budget(self.budget)
|
||||
|
||||
self.enemy_budget = ProcurementAi(
|
||||
self,
|
||||
for_player=False,
|
||||
faction=self.enemy_faction,
|
||||
manage_runways=True,
|
||||
manage_front_line=True,
|
||||
manage_aircraft=True
|
||||
).spend_budget(self.enemy_budget)
|
||||
|
||||
def message(self, text: str) -> None:
|
||||
self.informations.append(Information(text, turn=self.turn))
|
||||
|
||||
|
||||
@ -86,8 +86,8 @@ class ProcurementAi:
|
||||
break
|
||||
|
||||
budget -= db.PRICES[unit]
|
||||
cp.base.armor[unit] = cp.base.armor.get(unit, 0) + 1
|
||||
self.reinforcement_message(unit, cp, group_size=1)
|
||||
assert cp.pending_unit_deliveries is not None
|
||||
cp.pending_unit_deliveries.deliver({unit: 1})
|
||||
|
||||
if cp.base.total_armor >= armor_limit:
|
||||
candidates.remove(cp)
|
||||
@ -127,8 +127,8 @@ class ProcurementAi:
|
||||
break
|
||||
|
||||
budget -= db.PRICES[unit] * group_size
|
||||
cp.base.aircraft[unit] = cp.base.aircraft.get(unit, 0) + group_size
|
||||
self.reinforcement_message(unit, cp, group_size)
|
||||
assert cp.pending_unit_deliveries is not None
|
||||
cp.pending_unit_deliveries.deliver({unit: group_size})
|
||||
|
||||
if cp.base.total_aircraft >= aircraft_limit:
|
||||
candidates.remove(cp)
|
||||
@ -137,15 +137,6 @@ class ProcurementAi:
|
||||
|
||||
return budget
|
||||
|
||||
def reinforcement_message(self, unit_type: Type[UnitType],
|
||||
control_point: ControlPoint,
|
||||
group_size: int) -> None:
|
||||
description = f"{unit_type.id} x {group_size} at {control_point.name}"
|
||||
if self.is_player:
|
||||
self.game.message(f"Our reinforcements: {description}")
|
||||
else:
|
||||
self.game.message(f"OPFOR reinforcements: {description}")
|
||||
|
||||
@property
|
||||
def owned_points(self) -> List[ControlPoint]:
|
||||
if self.is_player:
|
||||
|
||||
@ -91,7 +91,6 @@ class GameGenerator:
|
||||
# Reset name generator
|
||||
namegen.reset()
|
||||
self.prepare_theater()
|
||||
self.populate_red_airbases()
|
||||
game = Game(player_name=self.player,
|
||||
enemy_name=self.enemy,
|
||||
theater=self.theater,
|
||||
@ -134,51 +133,6 @@ class GameGenerator:
|
||||
else:
|
||||
cp.captured = True
|
||||
|
||||
def populate_red_airbases(self) -> None:
|
||||
for control_point in self.theater.enemy_points():
|
||||
if control_point.captured:
|
||||
continue
|
||||
self.populate_red_airbase(control_point)
|
||||
|
||||
def populate_red_airbase(self, control_point: ControlPoint) -> None:
|
||||
# Force reset cp on generation
|
||||
control_point.base.aircraft = {}
|
||||
control_point.base.armor = {}
|
||||
control_point.base.aa = {}
|
||||
control_point.base.commision_points = {}
|
||||
control_point.base.strength = 1
|
||||
|
||||
# The tasks here are confusing. PinpointStrike for some reason means
|
||||
# ground units.
|
||||
for task in [PinpointStrike, CAP, CAS, AirDefence]:
|
||||
if isinstance(control_point, OffMapSpawn):
|
||||
# Off-map spawn locations start with no aircraft.
|
||||
continue
|
||||
|
||||
if IMPORTANCE_HIGH <= control_point.importance <= IMPORTANCE_LOW:
|
||||
raise ValueError(
|
||||
f"CP importance must be between {IMPORTANCE_LOW} and "
|
||||
f"{IMPORTANCE_HIGH}, is {control_point.importance}")
|
||||
|
||||
importance_factor = ((control_point.importance - IMPORTANCE_LOW) /
|
||||
(IMPORTANCE_HIGH - IMPORTANCE_LOW))
|
||||
# noinspection PyTypeChecker
|
||||
unit_types = db.choose_units(task, importance_factor, UNIT_VARIETY,
|
||||
self.enemy)
|
||||
if not unit_types:
|
||||
continue
|
||||
|
||||
count_log = math.log(control_point.importance + 0.01,
|
||||
UNIT_COUNT_IMPORTANCE_LOG)
|
||||
count = max((COUNT_BY_TASK[task] *
|
||||
self.generator_settings.multiplier *
|
||||
(count_log + 1)),
|
||||
1)
|
||||
|
||||
count_per_type = max(int(float(count) / len(unit_types)), 1)
|
||||
for unit_type in unit_types:
|
||||
control_point.base.commision_units({unit_type: count_per_type})
|
||||
|
||||
|
||||
class LocationFinder:
|
||||
def __init__(self, game: Game, control_point: ControlPoint) -> None:
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user