mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
integration tests for operation generation; adjusted waypoint altitude for AI to not fly too low; removed C101 from list of generated AI aircrafts
This commit is contained in:
17
game/db.py
17
game/db.py
@@ -39,7 +39,6 @@ and prioritization for the enemy (i.e. less important bases will receive units w
|
||||
"""
|
||||
PRICES = {
|
||||
# fighter
|
||||
C_101CC: 8,
|
||||
MiG_23MLD: 18,
|
||||
Su_27: 20,
|
||||
Su_33: 22,
|
||||
@@ -85,14 +84,14 @@ PRICES = {
|
||||
C_130: 8,
|
||||
|
||||
# armor
|
||||
Armor.APC_BTR_80: 12,
|
||||
Armor.MBT_T_55: 14,
|
||||
Armor.MBT_T_80U: 18,
|
||||
Armor.MBT_T_90: 20,
|
||||
Armor.APC_BTR_80: 16,
|
||||
Armor.MBT_T_55: 22,
|
||||
Armor.MBT_T_80U: 28,
|
||||
Armor.MBT_T_90: 35,
|
||||
|
||||
Armor.ATGM_M1134_Stryker: 12,
|
||||
Armor.MBT_M60A3_Patton: 14,
|
||||
Armor.MBT_M1A2_Abrams: 18,
|
||||
Armor.ATGM_M1134_Stryker: 18,
|
||||
Armor.MBT_M60A3_Patton: 24,
|
||||
Armor.MBT_M1A2_Abrams: 35,
|
||||
|
||||
Unarmed.Transport_UAZ_469: 3,
|
||||
Unarmed.Transport_Ural_375: 3,
|
||||
@@ -137,7 +136,6 @@ Following tasks are present:
|
||||
"""
|
||||
UNIT_BY_TASK = {
|
||||
CAP: [
|
||||
C_101CC,
|
||||
F_5E_3,
|
||||
MiG_23MLD,
|
||||
Su_27,
|
||||
@@ -258,7 +256,6 @@ Be advised that putting unit to the country that have not access to the unit in
|
||||
"""
|
||||
UNIT_BY_COUNTRY = {
|
||||
"Russia": [
|
||||
C_101CC,
|
||||
AJS37,
|
||||
MiG_23MLD,
|
||||
F_5E_3,
|
||||
|
||||
@@ -25,8 +25,8 @@ class BaseAttackEvent(Event):
|
||||
return "Ground attack"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_attackers = sum([v for k, v in debriefing.alive_units.get(self.attacker_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_defenders = sum([v for k, v in debriefing.alive_units.get(self.defender_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||
attackers_success = alive_attackers >= alive_defenders
|
||||
if self.departure_cp.captured:
|
||||
return attackers_success
|
||||
|
||||
@@ -53,7 +53,7 @@ class ConvoyStrikeEvent(Event):
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
killed_units = sum([v for k, v in debriefing.destroyed_units[self.defender_name].items() if db.unit_task(k) in [PinpointStrike, Reconnaissance]])
|
||||
killed_units = sum([v for k, v in debriefing.destroyed_units.get(self.defender_name, {}).items() if db.unit_task(k) in [PinpointStrike, Reconnaissance]])
|
||||
all_units = sum(self.targets.values())
|
||||
attackers_success = (float(killed_units) / (all_units + 0.01)) > self.SUCCESS_FACTOR
|
||||
if self.from_cp.captured:
|
||||
|
||||
@@ -16,6 +16,7 @@ from userdata.debriefing import Debriefing
|
||||
from userdata import persistency
|
||||
|
||||
DIFFICULTY_LOG_BASE = 1.1
|
||||
EVENT_DEPARTURE_MAX_DISTANCE = 340000
|
||||
|
||||
|
||||
class Event:
|
||||
@@ -74,6 +75,18 @@ class Event:
|
||||
def global_cp_available(self) -> bool:
|
||||
return False
|
||||
|
||||
def is_departure_available_from(self, cp: ControlPoint) -> bool:
|
||||
if not cp.captured:
|
||||
return False
|
||||
|
||||
if self.location.distance_to_point(cp.position) > EVENT_DEPARTURE_MAX_DISTANCE:
|
||||
return False
|
||||
|
||||
if cp.is_global and not self.global_cp_available:
|
||||
return False
|
||||
|
||||
return True
|
||||
|
||||
def bonus(self) -> int:
|
||||
return int(math.log(self.to_cp.importance + 1, DIFFICULTY_LOG_BASE) * self.BONUS_BASE)
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ class FrontlineAttackEvent(Event):
|
||||
@property
|
||||
def tasks(self) -> typing.Collection[typing.Type[Task]]:
|
||||
if self.is_player_attacking:
|
||||
return [CAS]
|
||||
return [CAS, CAP]
|
||||
else:
|
||||
return [CAP]
|
||||
|
||||
@@ -38,8 +38,8 @@ class FrontlineAttackEvent(Event):
|
||||
return "Frontline attack"
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
alive_attackers = sum([v for k, v in debriefing.alive_units[self.attacker_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_defenders = sum([v for k, v in debriefing.alive_units[self.defender_name].items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_attackers = sum([v for k, v in debriefing.alive_units.get(self.attacker_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||
alive_defenders = sum([v for k, v in debriefing.alive_units.get(self.defender_name, {}).items() if db.unit_task(k) == PinpointStrike])
|
||||
attackers_success = (float(alive_attackers) / (alive_defenders + 0.01)) > self.SUCCESS_FACTOR
|
||||
if self.from_cp.captured:
|
||||
return attackers_success
|
||||
@@ -65,8 +65,7 @@ class FrontlineAttackEvent(Event):
|
||||
self.to_cp.base.affect_strength(-0.1)
|
||||
|
||||
def player_attacking(self, flights: db.TaskForceDict):
|
||||
assert CAS in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
assert CAS in flights and CAP in flights and len(flights) == 2, "Invalid flights"
|
||||
|
||||
op = FrontlineAttackOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
@@ -76,10 +75,36 @@ class FrontlineAttackEvent(Event):
|
||||
to_cp=self.to_cp)
|
||||
|
||||
defenders = self.to_cp.base.assemble_attack()
|
||||
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), sum(defenders.values()))
|
||||
op.setup(target=defenders,
|
||||
max_attackers = int(math.ceil(sum(defenders.values()) * self.ATTACKER_DEFENDER_FACTOR))
|
||||
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
|
||||
op.setup(defenders=defenders,
|
||||
attackers=attackers,
|
||||
strikegroup=flights[CAS])
|
||||
strikegroup=flights[CAS],
|
||||
escort=flights[CAP],
|
||||
interceptors=assigned_units_from(self.to_cp.base.scramble_interceptors(1)))
|
||||
|
||||
self.operation = op
|
||||
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
op = FrontlineAttackOperation(game=self.game,
|
||||
attacker_name=self.attacker_name,
|
||||
defender_name=self.defender_name,
|
||||
from_cp=self.from_cp,
|
||||
departure_cp=self.departure_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
defenders = self.to_cp.base.assemble_attack()
|
||||
|
||||
max_attackers = int(math.ceil(sum(defenders.values())))
|
||||
attackers = db.unitdict_restrict_count(self.from_cp.base.assemble_attack(), max_attackers)
|
||||
|
||||
op.setup(defenders=defenders,
|
||||
attackers=attackers,
|
||||
strikegroup=assigned_units_from(self.from_cp.base.scramble_cas(1)),
|
||||
escort=assigned_units_from(self.from_cp.base.scramble_sweep(1)),
|
||||
interceptors=flights[CAP])
|
||||
|
||||
self.operation = op
|
||||
|
||||
|
||||
@@ -42,8 +42,8 @@ class InterceptEvent(Event):
|
||||
return True
|
||||
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
units_destroyed = debriefing.destroyed_units[self.defender_name].get(self.transport_unit, 0)
|
||||
if self.departure_cp.captured:
|
||||
units_destroyed = debriefing.destroyed_units.get(self.defender_name, {}).get(self.transport_unit, 0)
|
||||
if self.from_cp.captured:
|
||||
return units_destroyed > 0
|
||||
else:
|
||||
return units_destroyed == 0
|
||||
@@ -56,11 +56,11 @@ class InterceptEvent(Event):
|
||||
for _, cp in self.game.theater.conflicts(True):
|
||||
cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
# enemy attacking
|
||||
if self.is_successfull(debriefing):
|
||||
self.departure_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
self.from_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
else:
|
||||
self.to_cp.base.affect_strength(-self.STRENGTH_INFLUENCE)
|
||||
|
||||
@@ -95,7 +95,7 @@ class InterceptEvent(Event):
|
||||
def player_defending(self, flights: db.TaskForceDict):
|
||||
assert CAP in flights and len(flights) == 1, "Invalid flights"
|
||||
|
||||
interceptors = self.departure_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
||||
interceptors = self.from_cp.base.scramble_interceptors(self.game.settings.multiplier)
|
||||
|
||||
self.transport_unit = random.choice(db.find_unittype(Transport, self.defender_name))
|
||||
assert self.transport_unit is not None
|
||||
@@ -107,7 +107,8 @@ class InterceptEvent(Event):
|
||||
departure_cp=self.departure_cp,
|
||||
to_cp=self.to_cp)
|
||||
|
||||
op.setup(escort=flights[CAP],
|
||||
op.setup(location=self.location,
|
||||
escort=flights[CAP],
|
||||
transport={self.transport_unit: 1},
|
||||
interceptors=assigned_units_from(interceptors),
|
||||
airdefense={})
|
||||
|
||||
@@ -49,7 +49,7 @@ class NavalInterceptEvent(Event):
|
||||
def is_successfull(self, debriefing: Debriefing):
|
||||
total_targets = sum(self.targets.values())
|
||||
destroyed_targets = 0
|
||||
for unit, count in debriefing.destroyed_units[self.defender_name].items():
|
||||
for unit, count in debriefing.destroyed_units.get(self.defender_name, {}).items():
|
||||
if unit in self.targets:
|
||||
destroyed_targets += count
|
||||
|
||||
|
||||
14
game/game.py
14
game/game.py
@@ -43,19 +43,18 @@ Events:
|
||||
* BaseAttackEvent - capture base
|
||||
* InterceptEvent - air intercept
|
||||
* FrontlineAttackEvent - frontline attack
|
||||
* FrontlineCAPEvent - frontline attack
|
||||
* NavalInterceptEvent - naval intercept
|
||||
* StrikeEvent - strike event
|
||||
* InfantryTransportEvent - helicopter infantry transport
|
||||
"""
|
||||
EVENT_PROBABILITIES = {
|
||||
# events always present; only for the player
|
||||
FrontlineAttackEvent: [100, 0],
|
||||
FrontlineAttackEvent: [100, 9],
|
||||
#FrontlinePatrolEvent: [100, 0],
|
||||
StrikeEvent: [100, 0],
|
||||
|
||||
# events randomly present; only for the player
|
||||
InfantryTransportEvent: [25, 0],
|
||||
#InfantryTransportEvent: [25, 0],
|
||||
ConvoyStrikeEvent: [25, 0],
|
||||
|
||||
# events conditionally present; for both enemy and player
|
||||
@@ -163,6 +162,8 @@ class Game:
|
||||
self.events.append(event_class(self, enemy_cp, player_cp, player_cp.position, self.enemy, self.player))
|
||||
|
||||
def _generate_events(self):
|
||||
strikes_generated_for = set()
|
||||
|
||||
for player_cp, enemy_cp in self.theater.conflicts(True):
|
||||
for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items():
|
||||
if event_class in [FrontlineAttackEvent, FrontlinePatrolEvent, InfantryTransportEvent, ConvoyStrikeEvent]:
|
||||
@@ -170,8 +171,15 @@ class Game:
|
||||
if not Conflict.has_frontline_between(player_cp, enemy_cp):
|
||||
continue
|
||||
|
||||
if event_class in [StrikeEvent]:
|
||||
# don't generate multiple 100% strike events from each attack direction
|
||||
if enemy_cp in strikes_generated_for:
|
||||
continue
|
||||
|
||||
if player_probability == 100 or player_probability > 0 and self._roll(player_probability, player_cp.base.strength):
|
||||
self._generate_player_event(event_class, player_cp, enemy_cp)
|
||||
if event_class in [StrikeEvent]:
|
||||
strikes_generated_for.add(enemy_cp)
|
||||
|
||||
if enemy_probability == 100 or enemy_probability > 0 and self._roll(enemy_probability, enemy_cp.base.strength):
|
||||
self._generate_enemy_event(event_class, player_cp, enemy_cp)
|
||||
|
||||
@@ -7,16 +7,24 @@ MAX_DISTANCE_BETWEEN_GROUPS = 12000
|
||||
|
||||
|
||||
class FrontlineAttackOperation(Operation):
|
||||
interceptors = None # type: db.AssignedUnitsDict
|
||||
escort = None # type: db.AssignedUnitsDict
|
||||
strikegroup = None # type: db.AssignedUnitsDict
|
||||
|
||||
attackers = None # type: db.ArmorDict
|
||||
target = None # type: db.ArmorDict
|
||||
defenders = None # type: db.ArmorDict
|
||||
|
||||
def setup(self,
|
||||
target: db.ArmorDict,
|
||||
defenders: db.ArmorDict,
|
||||
attackers: db.ArmorDict,
|
||||
strikegroup: db.AssignedUnitsDict):
|
||||
strikegroup: db.AssignedUnitsDict,
|
||||
escort: db.AssignedUnitsDict,
|
||||
interceptors: db.AssignedUnitsDict):
|
||||
self.strikegroup = strikegroup
|
||||
self.target = target
|
||||
self.escort = escort
|
||||
self.interceptors = interceptors
|
||||
|
||||
self.defenders = defenders
|
||||
self.attackers = attackers
|
||||
|
||||
def prepare(self, terrain: Terrain, is_quick: bool):
|
||||
@@ -40,8 +48,10 @@ class FrontlineAttackOperation(Operation):
|
||||
if self.is_player_attack:
|
||||
self.prepare_carriers(db.unitdict_from(self.strikegroup))
|
||||
|
||||
self.armorgen.generate_vec(self.attackers, self.target)
|
||||
# ground units
|
||||
self.armorgen.generate_vec(self.attackers, self.defenders)
|
||||
|
||||
# strike group w/ heli support
|
||||
planes_flights = {k: v for k, v in self.strikegroup.items() if k in plane_map.values()}
|
||||
self.airgen.generate_cas_strikegroup(*assigned_units_split(planes_flights), at=self.attackers_starting_position)
|
||||
|
||||
@@ -54,6 +64,10 @@ class FrontlineAttackOperation(Operation):
|
||||
at=farp,
|
||||
escort=len(planes_flights) == 0)
|
||||
|
||||
self.airgen.generate_attackers_escort(*assigned_units_split(self.escort), at=self.attackers_starting_position)
|
||||
|
||||
self.airgen.generate_defense(*assigned_units_split(self.interceptors), at=self.defenders_starting_position)
|
||||
|
||||
self.briefinggen.title = "Frontline CAS"
|
||||
self.briefinggen.description = "Provide CAS for the ground forces attacking enemy lines. Operation will be considered successful if total number of enemy units will be lower than your own by a factor of 1.5 (i.e. with 12 units from both sides, enemy forces need to be reduced to at least 8), meaning that you (and, probably, your wingmans) should concentrate on destroying the enemy units. Target base strength will be lowered as a result. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu."
|
||||
self.briefinggen.append_waypoint("CAS AREA IP")
|
||||
|
||||
Reference in New Issue
Block a user