mirror of
https://github.com/dcs-liberation/dcs_liberation.git
synced 2025-11-10 14:22:26 +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:
parent
fbbe56f954
commit
f7e2c8921c
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")
|
||||
|
||||
@ -17,23 +17,23 @@ ESCORT_ENGAGEMENT_MAX_DIST = 100000
|
||||
WORKAROUND_WAYP_DIST = 1000
|
||||
|
||||
WARM_START_HELI_AIRSPEED = 120
|
||||
WARM_START_HELI_ALT = 1000
|
||||
WARM_START_HELI_ALT = 500
|
||||
|
||||
WARM_START_ALTITUDE = 3000
|
||||
WARM_START_AIRSPEED = 550
|
||||
|
||||
INTERCEPTION_ALT = 3000
|
||||
INTERCEPTION_AIRSPEED = 1000
|
||||
BARCAP_RACETRACK_DISTANCE = 20000
|
||||
|
||||
ATTACK_CIRCLE_ALT = 5000
|
||||
ATTACK_CIRCLE_ALT = 1000
|
||||
ATTACK_CIRCLE_DURATION = 15
|
||||
|
||||
CAS_ALTITUDE = 1000
|
||||
RTB_ALTITUDE = 3000
|
||||
HELI_ALT = 900
|
||||
CAS_ALTITUDE = 800
|
||||
RTB_ALTITUDE = 800
|
||||
RTB_DISTANCE = 5000
|
||||
HELI_ALT = 500
|
||||
|
||||
TRANSPORT_LANDING_ALT = 1000
|
||||
TRANSPORT_LANDING_ALT = 2000
|
||||
|
||||
DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000
|
||||
INTERCEPT_MAX_DISTANCE = 200000
|
||||
@ -149,7 +149,7 @@ class AircraftConflictGenerator:
|
||||
pos = Point(at.x + random.randint(100, 1000), at.y + random.randint(100, 1000))
|
||||
|
||||
logging.info("airgen: {} for {} at {} at {}".format(unit_type, side.id, alt, speed))
|
||||
return self.m.flight_group(
|
||||
group = self.m.flight_group(
|
||||
country=side,
|
||||
name=name,
|
||||
aircraft_type=unit_type,
|
||||
@ -161,6 +161,9 @@ class AircraftConflictGenerator:
|
||||
start_type=self._start_type(),
|
||||
group_size=count)
|
||||
|
||||
group.points[0].alt_type = "RADIO"
|
||||
return group
|
||||
|
||||
def _generate_at_group(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, at: typing.Union[ShipGroup, StaticGroup]) -> FlyingGroup:
|
||||
assert count > 0
|
||||
assert unit is not None
|
||||
@ -197,17 +200,26 @@ class AircraftConflictGenerator:
|
||||
else:
|
||||
assert False
|
||||
|
||||
def _add_radio_waypoint(self, group: FlyingGroup, position, altitude: int, airspeed: int = 600):
|
||||
point = group.add_waypoint(position, altitude, airspeed)
|
||||
point.alt_type = "RADIO"
|
||||
return point
|
||||
|
||||
def _rtb_for(self, group: FlyingGroup, cp: ControlPoint, at: db.StartingPosition = None):
|
||||
if not at:
|
||||
at = cp.at
|
||||
position = at if isinstance(at, Point) else at.position
|
||||
|
||||
if isinstance(at, Point):
|
||||
group.add_waypoint(at, RTB_ALTITUDE)
|
||||
elif isinstance(at, Group):
|
||||
group.add_waypoint(at.position, RTB_ALTITUDE)
|
||||
elif issubclass(at, Airport):
|
||||
group.add_waypoint(at.position, RTB_ALTITUDE)
|
||||
last_waypoint = group.points[-1]
|
||||
if last_waypoint is not None:
|
||||
heading = position.heading_between_point(last_waypoint.position)
|
||||
tod_location = position.point_from_heading(heading, RTB_DISTANCE)
|
||||
self._add_radio_waypoint(group, tod_location, last_waypoint.alt)
|
||||
|
||||
destination_waypoint = self._add_radio_waypoint(group, position, RTB_ALTITUDE)
|
||||
if isinstance(at, Airport):
|
||||
group.land_at(at)
|
||||
return destination_waypoint
|
||||
|
||||
def _at_position(self, at) -> Point:
|
||||
if isinstance(at, Point):
|
||||
@ -244,7 +256,7 @@ class AircraftConflictGenerator:
|
||||
orbit_task = ControlledTask(OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||
orbit_task.stop_after_duration(ATTACK_CIRCLE_DURATION * 60)
|
||||
|
||||
orbit_waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE)
|
||||
orbit_waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE)
|
||||
orbit_waypoint.tasks.append(orbit_task)
|
||||
orbit_waypoint.tasks.append(EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||
|
||||
@ -263,9 +275,9 @@ class AircraftConflictGenerator:
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
waypoint = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, client_count)
|
||||
@ -312,11 +324,11 @@ class AircraftConflictGenerator:
|
||||
|
||||
location = self._group_point(self.conflict.air_defenders_location)
|
||||
insertion_point = self.conflict.find_insertion_point(location)
|
||||
waypoint = group.add_waypoint(insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
waypoint = self._add_radio_waypoint(group, insertion_point, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
if self.conflict.is_vector:
|
||||
destination_tail = self.conflict.tail.distance_to_point(insertion_point) > self.conflict.position.distance_to_point(insertion_point)
|
||||
group.add_waypoint(destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
self._add_radio_waypoint(group, destination_tail and self.conflict.tail or self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
group.task = CAS.name
|
||||
self._setup_group(group, CAS, client_count)
|
||||
@ -336,7 +348,7 @@ class AircraftConflictGenerator:
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
for target_group in target_groups:
|
||||
wayp.tasks.append(AttackGroup(target_group.id))
|
||||
|
||||
@ -377,7 +389,7 @@ class AircraftConflictGenerator:
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
group.task = CAP.name
|
||||
wayp = group.add_waypoint(self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
wayp = self._add_radio_waypoint(group, self.conflict.position, CAS_ALTITUDE, WARM_START_AIRSPEED)
|
||||
wayp.tasks.append(dcs.task.EngageTargets(max_distance=DEFENCE_ENGAGEMENT_MAX_DISTANCE))
|
||||
wayp.tasks.append(dcs.task.OrbitAction(ATTACK_CIRCLE_ALT, pattern=OrbitAction.OrbitPattern.Circle))
|
||||
self._setup_group(group, CAP, client_count)
|
||||
@ -393,9 +405,9 @@ class AircraftConflictGenerator:
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location))
|
||||
|
||||
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
|
||||
group.task = CAP.name
|
||||
self._setup_group(group, CAP, client_count)
|
||||
@ -411,14 +423,14 @@ class AircraftConflictGenerator:
|
||||
client_count=client_count,
|
||||
at=at and at or self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
waypoint = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
waypoint = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
if self.conflict.is_vector:
|
||||
group.add_waypoint(self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
self._add_radio_waypoint(group, self.conflict.tail, WARM_START_ALTITUDE, WARM_START_AIRSPEED)
|
||||
else:
|
||||
heading = group.position.heading_between_point(self.conflict.position)
|
||||
waypoint = group.add_waypoint(self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
||||
WARM_START_ALTITUDE,
|
||||
WARM_START_AIRSPEED)
|
||||
waypoint = self._add_radio_waypoint(group, self.conflict.position.point_from_heading(heading, BARCAP_RACETRACK_DISTANCE),
|
||||
WARM_START_ALTITUDE,
|
||||
WARM_START_AIRSPEED)
|
||||
waypoint.tasks.append(OrbitAction(WARM_START_ALTITUDE, WARM_START_AIRSPEED))
|
||||
|
||||
group.task = CAP.name
|
||||
@ -437,9 +449,11 @@ class AircraftConflictGenerator:
|
||||
client_count=client_count,
|
||||
at=self._group_point(self.conflict.air_defenders_location))
|
||||
|
||||
waypoint = group.add_waypoint(destination.position.random_point_within(0, 0), TRANSPORT_LANDING_ALT)
|
||||
waypoint = self._rtb_for(group, self.conflict.to_cp)
|
||||
if escort:
|
||||
self.escort_targets.append((group, group.points.index(waypoint)))
|
||||
|
||||
self._add_radio_waypoint(group, destination.position, RTB_ALTITUDE)
|
||||
group.task = Transport.name
|
||||
group.land_at(destination)
|
||||
|
||||
@ -456,11 +470,11 @@ class AircraftConflictGenerator:
|
||||
group.task = CAP.name
|
||||
group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||
|
||||
wayp = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
||||
wayp = self._add_radio_waypoint(group, self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED)
|
||||
wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE))
|
||||
|
||||
if self.conflict.is_vector:
|
||||
group.add_waypoint(self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
||||
self._add_radio_waypoint(group, self.conflict.tail, CAS_ALTITUDE, WARM_START_ALTITUDE)
|
||||
|
||||
self._setup_group(group, CAP, client_count)
|
||||
self._rtb_for(group, self.conflict.from_cp, at)
|
||||
@ -476,9 +490,5 @@ class AircraftConflictGenerator:
|
||||
at=at and at or self._group_point(self.conflict.air_attackers_location)
|
||||
)
|
||||
|
||||
group.add_waypoint(
|
||||
pos=self.conflict.position,
|
||||
altitude=HELI_ALT,
|
||||
)
|
||||
|
||||
self._add_radio_waypoint(group, self.conflict.position, HELI_ALT)
|
||||
self._setup_group(group, Transport, client_count)
|
||||
|
||||
@ -28,7 +28,7 @@ CAP_CAS_DISTANCE = 10000, 120000
|
||||
|
||||
GROUND_INTERCEPT_SPREAD = 5000
|
||||
GROUND_DISTANCE_FACTOR = 1
|
||||
GROUND_DISTANCE = 4000
|
||||
GROUND_DISTANCE = 2000
|
||||
|
||||
GROUND_ATTACK_DISTANCE = 25000, 13000
|
||||
|
||||
@ -219,8 +219,8 @@ class Conflict:
|
||||
|
||||
pos = pos.point_from_heading(heading, 500)
|
||||
|
||||
logging.info("Didn't find ground position!")
|
||||
return None
|
||||
logging.error("Didn't find ground position!")
|
||||
return initial
|
||||
|
||||
@classmethod
|
||||
def capture_conflict(cls, attacker: Country, defender: Country, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater):
|
||||
@ -305,7 +305,7 @@ class Conflict:
|
||||
initial_location = to_cp.position.random_point_within(*GROUND_ATTACK_DISTANCE)
|
||||
position = Conflict._find_ground_position(initial_location, GROUND_INTERCEPT_SPREAD, _heading_sum(heading, 180), theater)
|
||||
if not position:
|
||||
heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position))
|
||||
heading = to_cp.find_radial(to_cp.positioN.heading_between_point(from_cp.position))
|
||||
position = to_cp.position.point_from_heading(heading, to_cp.size * GROUND_DISTANCE_FACTOR)
|
||||
|
||||
return cls(
|
||||
|
||||
@ -9,6 +9,7 @@ from dcs.unit import Static
|
||||
from theater import *
|
||||
from .conflictgen import *
|
||||
#from game.game import Game
|
||||
from game import db
|
||||
|
||||
|
||||
class MarkerSmoke(unittype.StaticType):
|
||||
|
||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
10
tests/integration/__init__.py
Normal file
10
tests/integration/__init__.py
Normal file
@ -0,0 +1,10 @@
|
||||
from tests.integration import baseattack, convoystrike, frontlineattack, insurgentattack, intercept, navalintercept, strike
|
||||
|
||||
if __name__ == "__main__":
|
||||
baseattack.execute_all()
|
||||
convoystrike.execute_all()
|
||||
frontlineattack.execute_all()
|
||||
insurgentattack.execute_all()
|
||||
intercept.execute_all()
|
||||
navalintercept.execute_all()
|
||||
strike.execute_all()
|
||||
46
tests/integration/baseattack.py
Normal file
46
tests/integration/baseattack.py
Normal file
@ -0,0 +1,46 @@
|
||||
from theater.caucasus import CaucasusTheater
|
||||
from theater.nevada import NevadaTheater
|
||||
|
||||
from tests.integration.util import *
|
||||
|
||||
PLAYER_COUNTRY = "USA"
|
||||
ENEMY_COUNTRY = "Russia"
|
||||
|
||||
|
||||
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||
e = BaseAttackEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||
|
||||
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||
for departure_cp in departures:
|
||||
if e.is_departure_available_from(departure_cp):
|
||||
print("{} for {} ({}) - {}".format(e, player_cp, departure_cp, enemy_cp))
|
||||
e.departure_cp = departure_cp
|
||||
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||
|
||||
e.generate()
|
||||
execute_autocommit(e)
|
||||
e.generate_quick()
|
||||
execute_autocommit(e)
|
||||
|
||||
|
||||
def execute_theater(theater_klass):
|
||||
print("Theater: {}".format(theater_klass))
|
||||
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||
|
||||
total_events = 0
|
||||
while len(theater.enemy_points()) > 0:
|
||||
for player_cp, enemy_cp in theater.conflicts():
|
||||
execute(game, player_cp, enemy_cp)
|
||||
|
||||
enemy_cp.captured = True
|
||||
|
||||
print("Total: {}".format(total_events))
|
||||
|
||||
|
||||
def execute_all():
|
||||
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||
execute_theater(theater_klass)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_all()
|
||||
50
tests/integration/convoystrike.py
Normal file
50
tests/integration/convoystrike.py
Normal file
@ -0,0 +1,50 @@
|
||||
from theater.caucasus import CaucasusTheater
|
||||
from theater.nevada import NevadaTheater
|
||||
|
||||
from tests.integration.util import *
|
||||
|
||||
PLAYER_COUNTRY = "USA"
|
||||
ENEMY_COUNTRY = "Russia"
|
||||
|
||||
|
||||
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||
e = ConvoyStrikeEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||
|
||||
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||
for departure_cp in departures:
|
||||
if e.is_departure_available_from(departure_cp):
|
||||
enemy_cp.base.strength = 1
|
||||
for _ in range(10):
|
||||
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||
e.departure_cp = departure_cp
|
||||
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||
|
||||
e.generate()
|
||||
execute_autocommit(e)
|
||||
e.generate_quick()
|
||||
execute_autocommit(e)
|
||||
|
||||
enemy_cp.base.affect_strength(-0.1)
|
||||
|
||||
|
||||
def execute_theater(theater_klass):
|
||||
print("Theater: {}".format(theater_klass))
|
||||
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||
|
||||
total_events = 0
|
||||
while len(theater.enemy_points()) > 0:
|
||||
for player_cp, enemy_cp in theater.conflicts():
|
||||
execute(game, player_cp, enemy_cp)
|
||||
|
||||
enemy_cp.captured = True
|
||||
|
||||
print("Total: {}".format(total_events))
|
||||
|
||||
|
||||
def execute_all():
|
||||
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||
execute_theater(theater_klass)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_all()
|
||||
52
tests/integration/frontlineattack.py
Normal file
52
tests/integration/frontlineattack.py
Normal file
@ -0,0 +1,52 @@
|
||||
from theater.caucasus import CaucasusTheater
|
||||
from theater.nevada import NevadaTheater
|
||||
|
||||
from game.event.frontlineattack import FrontlineAttackEvent
|
||||
|
||||
from tests.integration.util import *
|
||||
|
||||
PLAYER_COUNTRY = "USA"
|
||||
ENEMY_COUNTRY = "Russia"
|
||||
|
||||
|
||||
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||
e = FrontlineAttackEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||
|
||||
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||
for departure_cp in departures:
|
||||
if e.is_departure_available_from(departure_cp):
|
||||
enemy_cp.base.strength = 1
|
||||
for _ in range(10):
|
||||
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||
e.departure_cp = departure_cp
|
||||
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||
|
||||
e.generate()
|
||||
execute_autocommit(e)
|
||||
e.generate_quick()
|
||||
execute_autocommit(e)
|
||||
|
||||
enemy_cp.base.affect_strength(-0.1)
|
||||
|
||||
|
||||
def execute_theater(theater_klass):
|
||||
print("Theater: {}".format(theater_klass))
|
||||
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||
|
||||
total_events = 0
|
||||
while len(theater.enemy_points()) > 0:
|
||||
for player_cp, enemy_cp in theater.conflicts():
|
||||
execute(game, player_cp, enemy_cp)
|
||||
|
||||
enemy_cp.captured = True
|
||||
|
||||
print("Total: {}".format(total_events))
|
||||
|
||||
|
||||
def execute_all():
|
||||
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||
execute_theater(theater_klass)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_all()
|
||||
48
tests/integration/insurgentattack.py
Normal file
48
tests/integration/insurgentattack.py
Normal file
@ -0,0 +1,48 @@
|
||||
from theater.caucasus import CaucasusTheater
|
||||
from theater.nevada import NevadaTheater
|
||||
|
||||
from game.event.insurgentattack import InsurgentAttackEvent
|
||||
|
||||
from tests.integration.util import *
|
||||
|
||||
PLAYER_COUNTRY = "USA"
|
||||
ENEMY_COUNTRY = "Russia"
|
||||
|
||||
|
||||
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||
e = InsurgentAttackEvent(game, enemy_cp, player_cp, player_cp.position, ENEMY_COUNTRY, PLAYER_COUNTRY)
|
||||
|
||||
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||
for departure_cp in departures:
|
||||
if e.is_departure_available_from(departure_cp):
|
||||
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||
e.departure_cp = departure_cp
|
||||
e.player_defending(autoflights_for(e, PLAYER_COUNTRY))
|
||||
|
||||
e.generate()
|
||||
execute_autocommit(e)
|
||||
e.generate_quick()
|
||||
execute_autocommit(e)
|
||||
|
||||
|
||||
def execute_theater(theater_klass):
|
||||
print("Theater: {}".format(theater_klass))
|
||||
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||
|
||||
total_events = 0
|
||||
while len(theater.enemy_points()) > 0:
|
||||
for player_cp, enemy_cp in theater.conflicts():
|
||||
execute(game, player_cp, enemy_cp)
|
||||
|
||||
enemy_cp.captured = True
|
||||
|
||||
print("Total: {}".format(total_events))
|
||||
|
||||
|
||||
def execute_all():
|
||||
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||
execute_theater(theater_klass)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_all()
|
||||
48
tests/integration/intercept.py
Normal file
48
tests/integration/intercept.py
Normal file
@ -0,0 +1,48 @@
|
||||
from theater.caucasus import CaucasusTheater
|
||||
from theater.nevada import NevadaTheater
|
||||
|
||||
from game.event.intercept import InterceptEvent
|
||||
|
||||
from tests.integration.util import *
|
||||
|
||||
PLAYER_COUNTRY = "USA"
|
||||
ENEMY_COUNTRY = "Russia"
|
||||
|
||||
|
||||
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||
e = InterceptEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||
|
||||
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||
for departure_cp in departures:
|
||||
if e.is_departure_available_from(departure_cp):
|
||||
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||
e.departure_cp = departure_cp
|
||||
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||
|
||||
e.generate()
|
||||
execute_autocommit(e)
|
||||
e.generate_quick()
|
||||
execute_autocommit(e)
|
||||
|
||||
|
||||
def execute_theater(theater_klass):
|
||||
print("Theater: {}".format(theater_klass))
|
||||
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||
|
||||
total_events = 0
|
||||
while len(theater.enemy_points()) > 0:
|
||||
for player_cp, enemy_cp in theater.conflicts():
|
||||
execute(game, player_cp, enemy_cp)
|
||||
|
||||
enemy_cp.captured = True
|
||||
|
||||
print("Total: {}".format(total_events))
|
||||
|
||||
|
||||
def execute_all():
|
||||
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||
execute_theater(theater_klass)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_all()
|
||||
49
tests/integration/navalintercept.py
Normal file
49
tests/integration/navalintercept.py
Normal file
@ -0,0 +1,49 @@
|
||||
from theater.caucasus import CaucasusTheater
|
||||
from theater.nevada import NevadaTheater
|
||||
|
||||
from game.event.intercept import InterceptEvent
|
||||
|
||||
from tests.integration.util import *
|
||||
|
||||
PLAYER_COUNTRY = "USA"
|
||||
ENEMY_COUNTRY = "Russia"
|
||||
|
||||
|
||||
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||
e = NavalInterceptEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||
|
||||
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||
for departure_cp in departures:
|
||||
if e.is_departure_available_from(departure_cp):
|
||||
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||
e.departure_cp = departure_cp
|
||||
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||
|
||||
e.generate()
|
||||
execute_autocommit(e)
|
||||
e.generate_quick()
|
||||
execute_autocommit(e)
|
||||
|
||||
|
||||
def execute_theater(theater_klass):
|
||||
print("Theater: {}".format(theater_klass))
|
||||
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||
|
||||
total_events = 0
|
||||
while len(theater.enemy_points()) > 0:
|
||||
for player_cp, enemy_cp in theater.conflicts():
|
||||
if enemy_cp.radials != LAND:
|
||||
execute(game, player_cp, enemy_cp)
|
||||
|
||||
enemy_cp.captured = True
|
||||
|
||||
print("Total: {}".format(total_events))
|
||||
|
||||
|
||||
def execute_all():
|
||||
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||
execute_theater(theater_klass)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_all()
|
||||
48
tests/integration/strike.py
Normal file
48
tests/integration/strike.py
Normal file
@ -0,0 +1,48 @@
|
||||
from theater.caucasus import CaucasusTheater
|
||||
from theater.nevada import NevadaTheater
|
||||
|
||||
from game.event.intercept import InterceptEvent
|
||||
|
||||
from tests.integration.util import *
|
||||
|
||||
PLAYER_COUNTRY = "USA"
|
||||
ENEMY_COUNTRY = "Russia"
|
||||
|
||||
|
||||
def execute(game, player_cp, enemy_cp, departure_cp = None):
|
||||
e = StrikeEvent(game, player_cp, enemy_cp, enemy_cp.position, PLAYER_COUNTRY, ENEMY_COUNTRY)
|
||||
|
||||
departures = [departure_cp] if departure_cp else game.theater.player_points()
|
||||
for departure_cp in departures:
|
||||
if e.is_departure_available_from(departure_cp):
|
||||
print("{} for {} ({}) - {} ({})".format(e, player_cp, departure_cp, enemy_cp, enemy_cp.base.strength))
|
||||
e.departure_cp = departure_cp
|
||||
e.player_attacking(autoflights_for(e, PLAYER_COUNTRY))
|
||||
|
||||
e.generate()
|
||||
execute_autocommit(e)
|
||||
e.generate_quick()
|
||||
execute_autocommit(e)
|
||||
|
||||
|
||||
def execute_theater(theater_klass):
|
||||
print("Theater: {}".format(theater_klass))
|
||||
game, theater = init(PLAYER_COUNTRY, ENEMY_COUNTRY, theater_klass)
|
||||
|
||||
total_events = 0
|
||||
while len(theater.enemy_points()) > 0:
|
||||
for player_cp, enemy_cp in theater.conflicts():
|
||||
execute(game, player_cp, enemy_cp)
|
||||
|
||||
enemy_cp.captured = True
|
||||
|
||||
print("Total: {}".format(total_events))
|
||||
|
||||
|
||||
def execute_all():
|
||||
for theater_klass in [CaucasusTheater, PersianGulfTheater, NevadaTheater]:
|
||||
execute_theater(theater_klass)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
execute_all()
|
||||
80
tests/integration/util.py
Normal file
80
tests/integration/util.py
Normal file
@ -0,0 +1,80 @@
|
||||
from dcs.mission import Mission
|
||||
|
||||
from game import *
|
||||
from game.event import *
|
||||
from game.db import *
|
||||
|
||||
from theater.persiangulf import *
|
||||
from theater import start_generator
|
||||
|
||||
PLAYER_COUNTRY = None
|
||||
ENEMY_COUNTRY = None
|
||||
|
||||
|
||||
def init(player_country: str, enemy_country: str, theater_klass: typing.Type[ConflictTheater]) -> typing.Tuple[Game, ConflictTheater]:
|
||||
global PLAYER_COUNTRY
|
||||
global ENEMY_COUNTRY
|
||||
|
||||
PLAYER_COUNTRY = player_country
|
||||
ENEMY_COUNTRY = enemy_country
|
||||
|
||||
# prerequisites
|
||||
persistency.setup("./tests/userfolder/")
|
||||
theater = theater_klass()
|
||||
start_generator.generate_inital_units(theater, ENEMY_COUNTRY, True, 1)
|
||||
start_generator.generate_groundobjects(theater)
|
||||
return Game(PLAYER_COUNTRY, ENEMY_COUNTRY, theater), theater
|
||||
|
||||
|
||||
def autoflights_for(event: Event, country: str) -> TaskForceDict:
|
||||
result = {}
|
||||
for task in event.tasks:
|
||||
result[task] = {find_unittype(task, country)[0]: (1, 1)}
|
||||
return result
|
||||
|
||||
|
||||
class AutodebriefType(Enum):
|
||||
EVERYONE_DEAD = 0
|
||||
PLAYER_DEAD = 1
|
||||
ENEMY_DEAD = 2
|
||||
|
||||
|
||||
def autodebrief_for(event: Event, type: AutodebriefType) -> Debriefing:
|
||||
mission = event.operation.current_mission # type: Mission
|
||||
|
||||
countries = []
|
||||
if type == AutodebriefType.PLAYER_DEAD or type == AutodebriefType.EVERYONE_DEAD:
|
||||
countries.append(mission.country(PLAYER_COUNTRY))
|
||||
|
||||
if type == AutodebriefType.ENEMY_DEAD or type == AutodebriefType.EVERYONE_DEAD:
|
||||
countries.append(mission.country(ENEMY_COUNTRY))
|
||||
|
||||
dead_units = []
|
||||
for country in countries:
|
||||
for group in country.plane_group + country.vehicle_group + country.helicopter_group:
|
||||
for unit in group.units:
|
||||
dead_units.append(str(unit.name))
|
||||
|
||||
return Debriefing(dead_units, [])
|
||||
|
||||
|
||||
def event_state_save(e: Event) -> typing.Tuple[Base, Base]:
|
||||
return (copy.deepcopy(e.from_cp.base), copy.deepcopy(e.to_cp.base))
|
||||
|
||||
|
||||
def event_state_restore(e: Event, state: typing.Tuple[Base, Base]):
|
||||
e.from_cp.base, e.to_cp.base = state[0], state[1]
|
||||
|
||||
|
||||
def execute_autocommit(e: Event):
|
||||
state = event_state_save(e)
|
||||
e.commit(autodebrief_for(e, AutodebriefType.EVERYONE_DEAD))
|
||||
event_state_restore(e, state)
|
||||
|
||||
state = event_state_save(e)
|
||||
e.commit(autodebrief_for(e, AutodebriefType.PLAYER_DEAD))
|
||||
event_state_restore(e, state)
|
||||
|
||||
state = event_state_save(e)
|
||||
e.commit(autodebrief_for(e, AutodebriefType.ENEMY_DEAD))
|
||||
event_state_restore(e, state)
|
||||
0
tests/userfolder/DCS/Missions/empty.txt
Normal file
0
tests/userfolder/DCS/Missions/empty.txt
Normal file
@ -56,7 +56,7 @@ class Base:
|
||||
|
||||
def _find_best_unit(self, dict, for_type: Task, count: int) -> typing.Dict:
|
||||
if count <= 0:
|
||||
logging.info("{}: no units for {}".format(self, for_type))
|
||||
logging.warning("{}: no units for {}".format(self, for_type))
|
||||
return {}
|
||||
|
||||
sorted_units = [key for key in dict.keys() if key in db.UNIT_BY_TASK[for_type]]
|
||||
|
||||
@ -10,30 +10,29 @@ from ui.styles import STYLES
|
||||
from ui.window import *
|
||||
|
||||
|
||||
EVENT_DEPARTURE_MAX_DISTANCE = 340000
|
||||
|
||||
EVENT_COLOR_ATTACK = (100, 100, 255)
|
||||
EVENT_COLOR_DEFENSE = (255, 100, 100)
|
||||
|
||||
RED = (255, 125, 125)
|
||||
BRIGHT_RED = (200, 64, 64)
|
||||
BLUE = (164, 164, 255)
|
||||
DARK_BLUE = (45, 62, 80)
|
||||
WHITE = (255, 255, 255)
|
||||
GREEN = (128, 186, 128)
|
||||
BRIGHT_GREEN = (64, 200, 64)
|
||||
BLACK = (0, 0, 0)
|
||||
|
||||
BACKGROUND = pygame.Color(0, 64, 64)
|
||||
ANTIALIASING = True
|
||||
|
||||
WIDTH = 1066
|
||||
HEIGHT = 600
|
||||
MAP_PADDING = 100
|
||||
|
||||
|
||||
class OverviewCanvas:
|
||||
mainmenu = None # type: ui.mainmenu.MainMenu
|
||||
|
||||
BRIGHT_RED = (200, 64, 64)
|
||||
BRIGHT_GREEN = (64, 200, 64)
|
||||
|
||||
RED = (255, 125, 125)
|
||||
BLUE = (164, 164, 255)
|
||||
DARK_BLUE = (45, 62, 80)
|
||||
WHITE = (255, 255, 255)
|
||||
GREEN = (128, 186, 128)
|
||||
BLACK = (0, 0, 0)
|
||||
BACKGROUND = pygame.Color(0, 64, 64)
|
||||
ANTIALIASING = True
|
||||
|
||||
WIDTH = 1066
|
||||
HEIGHT = 600
|
||||
MAP_PADDING = 100
|
||||
budget_label = None # type: Label
|
||||
|
||||
started = None
|
||||
ground_assets_icons = None # type: typing.Dict[str, pygame.Surface]
|
||||
@ -84,29 +83,27 @@ class OverviewCanvas:
|
||||
self.wrapper.grid(column=0, row=0, sticky=NSEW) # Adds grid
|
||||
self.wrapper.pack(side=LEFT) # packs window to the left
|
||||
|
||||
self.embed = Frame(self.wrapper, width=self.WIDTH, height=self.HEIGHT, borderwidth=2, **STYLES["frame-wrapper"])
|
||||
self.embed.grid(column=0, row=0, sticky=NSEW) # Adds grid
|
||||
self.embed = Frame(self.wrapper, width=WIDTH, height=HEIGHT, borderwidth=2, **STYLES["frame-wrapper"])
|
||||
self.embed.grid(column=0, row=1, sticky=NSEW) # Adds grid
|
||||
|
||||
self.options = Frame(self.wrapper, borderwidth=2, **STYLES["frame-wrapper"])
|
||||
self.options.grid(column=0, row=1, sticky=NSEW)
|
||||
self.options.grid(column=0, row=0, sticky=NSEW)
|
||||
self.options.grid_columnconfigure(1, weight=1)
|
||||
self.build_map_options_panel()
|
||||
|
||||
self.init_sdl_layer()
|
||||
self.init_sdl_thread()
|
||||
|
||||
def build_map_options_panel(self):
|
||||
def force_redraw():
|
||||
if self.screen:
|
||||
self.redraw_required = True
|
||||
self.draw()
|
||||
col = 0
|
||||
Button(self.options, text="Configuration", command=None, **STYLES["btn-primary"]).grid(column=col, row=0, sticky=NE)
|
||||
Button(self.options, text="Configuration", command=self.parent.configuration_menu, **STYLES["btn-primary"]).grid(column=col, row=0, sticky=NE)
|
||||
col += 1
|
||||
|
||||
Label(self.options, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount), **STYLES["widget"]).grid(column=col, row=0, sticky=N+EW)
|
||||
self.budget_label = Label(self.options, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount), **STYLES["widget"])
|
||||
self.budget_label.grid(column=col, row=0, sticky=N+EW)
|
||||
col += 1
|
||||
|
||||
Button(self.options, text="Pass turn", command=None, **STYLES["btn-primary"]).grid(column=col, row=0, sticky=NE)
|
||||
Button(self.options, text="Pass turn", command=self.parent.pass_turn, **STYLES["btn-primary"]).grid(column=col, row=0, sticky=NW)
|
||||
col += 1
|
||||
|
||||
def map_size_toggle(self):
|
||||
@ -115,8 +112,8 @@ class OverviewCanvas:
|
||||
self.options.configure(width=0)
|
||||
self.expanded = False
|
||||
else:
|
||||
self.embed.configure(width=self.WIDTH)
|
||||
self.options.configure(width=self.WIDTH)
|
||||
self.embed.configure(width=WIDTH)
|
||||
self.options.configure(width=WIDTH)
|
||||
self.expanded = True
|
||||
|
||||
def on_close(self):
|
||||
@ -131,8 +128,8 @@ class OverviewCanvas:
|
||||
os.environ['SDL_VIDEODRIVER'] = 'windib'
|
||||
|
||||
# Create pygame 'screen'
|
||||
self.screen = pygame.display.set_mode((self.WIDTH, self.HEIGHT), pygame.DOUBLEBUF | pygame.HWSURFACE)
|
||||
self.screen.fill(pygame.Color(*self.BLACK))
|
||||
self.screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.DOUBLEBUF | pygame.HWSURFACE)
|
||||
self.screen.fill(pygame.Color(*BLACK))
|
||||
|
||||
# Load icons resources
|
||||
self.ground_assets_icons = {}
|
||||
@ -157,14 +154,14 @@ class OverviewCanvas:
|
||||
|
||||
# Load the map image
|
||||
self.map = pygame.image.load(os.path.join("resources", self.game.theater.overview_image)).convert()
|
||||
pygame.draw.rect(self.map, self.BLACK, (0, 0, self.map.get_width(), self.map.get_height()), 10)
|
||||
pygame.draw.rect(self.map, self.WHITE, (0, 0, self.map.get_width(), self.map.get_height()), 5)
|
||||
pygame.draw.rect(self.map, BLACK, (0, 0, self.map.get_width(), self.map.get_height()), 10)
|
||||
pygame.draw.rect(self.map, WHITE, (0, 0, self.map.get_width(), self.map.get_height()), 5)
|
||||
|
||||
# Create surfaces for drawing
|
||||
self.surface = pygame.Surface((self.map.get_width() + self.MAP_PADDING * 2,
|
||||
self.map.get_height() + self.MAP_PADDING * 2))
|
||||
self.surface = pygame.Surface((self.map.get_width() + MAP_PADDING * 2,
|
||||
self.map.get_height() + MAP_PADDING * 2))
|
||||
self.surface.set_alpha(None)
|
||||
self.overlay = pygame.Surface((self.WIDTH, self.HEIGHT), pygame.SRCALPHA)
|
||||
self.overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
|
||||
|
||||
# Init pygame display
|
||||
pygame.display.init()
|
||||
@ -210,6 +207,10 @@ class OverviewCanvas:
|
||||
if event.type == pygame.MOUSEMOTION:
|
||||
self.redraw_required = True
|
||||
elif event.type == pygame.MOUSEBUTTONDOWN:
|
||||
"""
|
||||
Due to rendering not really supporting the zoom this is currently disabled.
|
||||
@TODO: improve rendering so zoom would actually make sense
|
||||
|
||||
# Scroll wheel
|
||||
if event.button == 4:
|
||||
self.zoom += 0.25
|
||||
@ -217,6 +218,7 @@ class OverviewCanvas:
|
||||
elif event.button == 5:
|
||||
self.zoom -= 0.25
|
||||
self.redraw_required = True
|
||||
"""
|
||||
|
||||
if event.button == 3:
|
||||
right_down = True
|
||||
@ -239,8 +241,8 @@ class OverviewCanvas:
|
||||
|
||||
if self.redraw_required:
|
||||
# Fill
|
||||
self.screen.fill(self.BACKGROUND)
|
||||
self.surface.fill(self.BACKGROUND)
|
||||
self.screen.fill(BACKGROUND)
|
||||
self.surface.fill(BACKGROUND)
|
||||
self.overlay.fill(pygame.Color(0, 0, 0, 0))
|
||||
|
||||
# Surface
|
||||
@ -260,10 +262,10 @@ class OverviewCanvas:
|
||||
self.redraw_required = False
|
||||
|
||||
def draw_map(self, surface: pygame.Surface, overlay: pygame.Surface, mouse_pos: (int, int), mouse_down: [bool, bool]):
|
||||
self.surface.blit(self.map, (self.MAP_PADDING, self.MAP_PADDING))
|
||||
self.surface.blit(self.map, (MAP_PADDING, MAP_PADDING))
|
||||
|
||||
# Display zoom level on overlay
|
||||
zoom_lvl = self.font.render(" x " + str(self.zoom) + " ", self.ANTIALIASING, self.WHITE, self.DARK_BLUE)
|
||||
zoom_lvl = self.font.render(" x " + str(self.zoom) + " ", ANTIALIASING, WHITE, DARK_BLUE)
|
||||
self.overlay.blit(zoom_lvl, (self.overlay.get_width()-zoom_lvl.get_width()-5,
|
||||
self.overlay.get_height()-zoom_lvl.get_height()-5))
|
||||
|
||||
@ -281,7 +283,7 @@ class OverviewCanvas:
|
||||
elif connected_cp.captured and cp.captured:
|
||||
color = self._player_color()
|
||||
else:
|
||||
color = self.BLACK
|
||||
color = BLACK
|
||||
|
||||
pygame.draw.line(surface, color, coords, connected_coords, 2)
|
||||
|
||||
@ -325,12 +327,12 @@ class OverviewCanvas:
|
||||
else:
|
||||
color = self._enemy_color()
|
||||
|
||||
pygame.draw.circle(self.surface, self.BLACK, (int(coords[0]), int(coords[1])), int(radius))
|
||||
pygame.draw.circle(self.surface, BLACK, (int(coords[0]), int(coords[1])), int(radius))
|
||||
pygame.draw.circle(self.surface, color, (int(coords[0]), int(coords[1])), int(radius_m))
|
||||
|
||||
label = self.font.render(cp.name, self.ANTIALIASING, (225, 225, 225), self.BLACK)
|
||||
labelHover = self.font.render(cp.name, self.ANTIALIASING, (255, 255, 255), (128, 186, 128))
|
||||
labelClick = self.font.render(cp.name, self.ANTIALIASING, (255, 255, 255), (122, 122, 255))
|
||||
label = self.font.render(cp.name, ANTIALIASING, (225, 225, 225), BLACK)
|
||||
labelHover = self.font.render(cp.name, ANTIALIASING, (255, 255, 255), (128, 186, 128))
|
||||
labelClick = self.font.render(cp.name, ANTIALIASING, (255, 255, 255), (122, 122, 255))
|
||||
|
||||
point = coords[0] - label.get_width() / 2 + 1, coords[1] + 1
|
||||
rect = pygame.Rect(*point, label.get_width(), label.get_height())
|
||||
@ -346,56 +348,56 @@ class OverviewCanvas:
|
||||
self.draw_base_info(self.overlay, cp, (0, 0))
|
||||
if self.selected_event_info:
|
||||
if self._cp_available_for_selected_event(cp):
|
||||
pygame.draw.line(self.surface, self.WHITE, rect.center, self.selected_event_info[1])
|
||||
pygame.draw.line(self.surface, WHITE, rect.center, self.selected_event_info[1])
|
||||
|
||||
else:
|
||||
self.surface.blit(label, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
|
||||
|
||||
if self.display_forces.get():
|
||||
units_title = " {} / {} / {} ".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa)
|
||||
label2 = self.fontsmall.render(units_title, self.ANTIALIASING, color, (30, 30, 30))
|
||||
label2 = self.fontsmall.render(units_title, ANTIALIASING, color, (30, 30, 30))
|
||||
self.surface.blit(label2, (coords[0] - label2.get_width() / 2, coords[1] + label.get_height() + 1))
|
||||
|
||||
return mouse_down
|
||||
|
||||
def draw_base_info(self, surface: pygame.Surface, control_point: ControlPoint, pos):
|
||||
title = self.font.render(control_point.name, self.ANTIALIASING, self.BLACK, self.GREEN)
|
||||
hp = self.font.render("Strength : ", self.ANTIALIASING, (225, 225, 225), self.BLACK)
|
||||
title = self.font.render(control_point.name, ANTIALIASING, BLACK, GREEN)
|
||||
hp = self.font.render("Strength : ", ANTIALIASING, (225, 225, 225), BLACK)
|
||||
|
||||
armor_txt = "ARMOR > "
|
||||
for key, value in control_point.base.armor.items():
|
||||
armor_txt += key.id + " x " + str(value) + " | "
|
||||
armor = self.font.render(armor_txt, self.ANTIALIASING, (225, 225, 225), self.BLACK)
|
||||
armor = self.font.render(armor_txt, ANTIALIASING, (225, 225, 225), BLACK)
|
||||
|
||||
aircraft_txt = "AIRCRAFT > "
|
||||
for key, value in control_point.base.aircraft.items():
|
||||
aircraft_txt += key.id + " x " + str(value) + " | "
|
||||
aircraft = self.font.render(aircraft_txt, self.ANTIALIASING, (225, 225, 225), self.BLACK)
|
||||
aircraft = self.font.render(aircraft_txt, ANTIALIASING, (225, 225, 225), BLACK)
|
||||
|
||||
aa_txt = "AA/SAM > "
|
||||
for key, value in control_point.base.aa.items():
|
||||
aa_txt += key.id + " x " + str(value) + " | "
|
||||
aa = self.font.render(aa_txt, self.ANTIALIASING, (225, 225, 225), self.BLACK)
|
||||
aa = self.font.render(aa_txt, ANTIALIASING, (225, 225, 225), BLACK)
|
||||
|
||||
lineheight = title.get_height()
|
||||
w = max([max([a.get_width() for a in [title, armor, aircraft, aa]]), 150])
|
||||
h = 5 * lineheight + 4 * 5
|
||||
|
||||
# Draw frame
|
||||
pygame.draw.rect(surface, self.GREEN, (pos[0], pos[1], w + 8, h + 8))
|
||||
pygame.draw.rect(surface, self.BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
|
||||
pygame.draw.rect(surface, self.GREEN, (pos[0] + 2, pos[1], w + 4, lineheight + 4))
|
||||
pygame.draw.rect(surface, GREEN, (pos[0], pos[1], w + 8, h + 8))
|
||||
pygame.draw.rect(surface, BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
|
||||
pygame.draw.rect(surface, GREEN, (pos[0] + 2, pos[1], w + 4, lineheight + 4))
|
||||
|
||||
# Title
|
||||
surface.blit(title, (pos[0] + 4, 4 + pos[1]))
|
||||
surface.blit(hp, (pos[0] + 4, 4 + pos[1] + lineheight + 5))
|
||||
|
||||
# Draw gauge
|
||||
pygame.draw.rect(surface, self.WHITE,
|
||||
pygame.draw.rect(surface, WHITE,
|
||||
(pos[0] + hp.get_width() + 3, 4 + pos[1] + lineheight + 5, 54, lineheight))
|
||||
pygame.draw.rect(surface, self.BRIGHT_RED,
|
||||
pygame.draw.rect(surface, BRIGHT_RED,
|
||||
(pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50, lineheight - 4))
|
||||
pygame.draw.rect(surface, self.BRIGHT_GREEN, (
|
||||
pygame.draw.rect(surface, BRIGHT_GREEN, (
|
||||
pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50 * control_point.base.strength, lineheight - 4))
|
||||
|
||||
# Text
|
||||
@ -405,8 +407,8 @@ class OverviewCanvas:
|
||||
|
||||
def draw_selected_event_info(self):
|
||||
event = self.selected_event_info[0]
|
||||
title = self.font.render(str(event), self.ANTIALIASING, self.BLACK, self.GREEN)
|
||||
hint = self.font.render("Select CP to depart from.", self.ANTIALIASING, (225, 225, 225), self.BLACK)
|
||||
title = self.font.render(str(event), ANTIALIASING, BLACK, GREEN)
|
||||
hint = self.font.render("Select CP to depart from.", ANTIALIASING, (225, 225, 225), BLACK)
|
||||
|
||||
w = hint.get_width()
|
||||
h = title.get_height() + hint.get_height() + 20
|
||||
@ -414,9 +416,9 @@ class OverviewCanvas:
|
||||
pos = self.overlay.get_width() / 2 - w / 2, self.overlay.get_height() - h
|
||||
|
||||
# Draw frame
|
||||
pygame.draw.rect(self.overlay, self.GREEN, (pos[0], pos[1], w + 8, h + 8))
|
||||
pygame.draw.rect(self.overlay, self.BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
|
||||
pygame.draw.rect(self.overlay, self.GREEN, (pos[0] + 2, pos[1], w + 4, title.get_height() + 4))
|
||||
pygame.draw.rect(self.overlay, GREEN, (pos[0], pos[1], w + 8, h + 8))
|
||||
pygame.draw.rect(self.overlay, BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
|
||||
pygame.draw.rect(self.overlay, GREEN, (pos[0] + 2, pos[1], w + 4, title.get_height() + 4))
|
||||
|
||||
# Title
|
||||
self.overlay.blit(title, (pos[0] + 4, 4 + pos[1]))
|
||||
@ -444,7 +446,7 @@ class OverviewCanvas:
|
||||
self.draw_ground_object_info(ground_object, (x, y), color, surface)
|
||||
|
||||
def draw_ground_object_info(self, ground_object: TheaterGroundObject, pos, color, surface: pygame.Surface):
|
||||
lb = self.font.render(str(ground_object), self.ANTIALIASING, color, self.BLACK)
|
||||
lb = self.font.render(str(ground_object), ANTIALIASING, color, BLACK)
|
||||
surface.blit(lb, (pos[0] + 18, pos[1]))
|
||||
|
||||
def draw_events(self, surface: pygame.Surface, mouse_pos, mouse_down):
|
||||
@ -500,7 +502,7 @@ class OverviewCanvas:
|
||||
|
||||
if rect.collidepoint(*mouse_pos) or self.selected_event_info == (event, rect.center):
|
||||
if not label_to_draw:
|
||||
label_to_draw = self.font.render(str(event), self.ANTIALIASING, self.WHITE, self.BLACK), rect.center
|
||||
label_to_draw = self.font.render(str(event), ANTIALIASING, WHITE, BLACK), rect.center
|
||||
|
||||
if rect.collidepoint(*mouse_pos):
|
||||
if mouse_down[0]:
|
||||
@ -517,7 +519,7 @@ class OverviewCanvas:
|
||||
|
||||
def _selected_cp(self, cp):
|
||||
if self.selected_event_info:
|
||||
if self._cp_available_for_selected_event(cp):
|
||||
if self. _cp_available_for_selected_event(cp):
|
||||
event = self.selected_event_info[0]
|
||||
event.departure_cp = cp
|
||||
|
||||
@ -551,8 +553,8 @@ class OverviewCanvas:
|
||||
X = point_b_img[1] + X_offset * X_scale
|
||||
Y = point_a_img[0] - Y_offset * Y_scale
|
||||
|
||||
X += self.MAP_PADDING
|
||||
Y += self.MAP_PADDING
|
||||
X += MAP_PADDING
|
||||
Y += MAP_PADDING
|
||||
|
||||
return X > treshold and X or treshold, Y > treshold and Y or treshold
|
||||
|
||||
@ -575,27 +577,18 @@ class OverviewCanvas:
|
||||
|
||||
def _cp_available_for_selected_event(self, cp: ControlPoint) -> bool:
|
||||
event = self.selected_event_info[0]
|
||||
|
||||
if not cp.captured:
|
||||
return False
|
||||
|
||||
if event.location.distance_to_point(cp.position) > EVENT_DEPARTURE_MAX_DISTANCE:
|
||||
return False
|
||||
|
||||
if cp.is_global and not event.global_cp_available:
|
||||
return False
|
||||
|
||||
return True
|
||||
return event.is_departure_available_from(cp)
|
||||
|
||||
def _player_color(self):
|
||||
return self.game.player == "USA" and self.BLUE or self.RED
|
||||
return self.game.player == "USA" and BLUE or RED
|
||||
|
||||
def _enemy_color(self):
|
||||
return self.game.player == "USA" and self.RED or self.BLUE
|
||||
return self.game.player == "USA" and RED or BLUE
|
||||
|
||||
def update(self):
|
||||
self.redraw_required = True
|
||||
self.draw()
|
||||
self.budget_label.text = "Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount)
|
||||
|
||||
def compute_display_rules(self):
|
||||
return sum([1 if a.get() else 0 for a in [self.display_forces, self.display_road, self.display_bases, self.display_ground_targets]])
|
||||
|
||||
@ -80,7 +80,7 @@ class Debriefing:
|
||||
nonlocal dead_units
|
||||
object_mission_id = int(object_mission_id_str)
|
||||
if object_mission_id in dead_units:
|
||||
logging.info("debriefing: failed to append_dead_object {}: already exists!".format(object_mission_id))
|
||||
logging.error("debriefing: failed to append_dead_object {}: already exists!".format(object_mission_id))
|
||||
return
|
||||
|
||||
dead_units.append(object_mission_id)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user