diff --git a/dcs/task.py b/dcs/task.py index 39831cfe..bd66d840 100644 --- a/dcs/task.py +++ b/dcs/task.py @@ -323,6 +323,13 @@ class Expend(Enum): Half = "Half" All = "All" + def __eq__(self, other: Any) -> bool: + if isinstance(other, str): + return self.value == other + if isinstance(other, Expend): + return self.value == other.value + return False + class AttackGroup(Task): """Attack group task action @@ -1077,6 +1084,38 @@ def __eq__(self, other) -> bool: return False +class CarpetBombing(Task): + """Create Carpet Bombing Task object + + :param alt_above: AGL altitude (in meters) from which bombing is to be performed. Defaults to 2000. + :param pattern_length: The pattern length (in meters) of the carpet bombing run. Defaults to 500. + :param release_qty: How many weapons to release. Defaults to Expend.Auto. See :py:class:`dcs.task.Expend` + :param weapon_type: The weapon to be used. Defaults to WeaponType.Auto. See :py:class:`dcs.task.WeaponType` + """ + Id = "CarpetBombing" + DEFAULT_ALT: int = 2000 + + def __init__(self, alt_above: int = DEFAULT_ALT, + pattern_length: int = 500, + release_qty: Expend = Expend.Auto, + weapon_type: WeaponType = WeaponType.Auto) -> None: + super().__init__(self.Id) + # hardcoded parameters are present in .miz file, but not visible in Mission Editor + self.params = { + "altitude": alt_above, + "altitudeEnabled": True if alt_above != CarpetBombing.DEFAULT_ALT else False, + "attackQty": 1, + "attackQtyLimit": False, + "attackType": "Carpet", + "carpetLength": pattern_length, + "expend": release_qty.value, + "groupAttack": False, + "weaponType": weapon_type.value, + "x": -52946.816024994, + "y": -52425.804462374, + } + + tasks_map: Dict[str, Type[Task]] = { ControlledTask.Id: ControlledTask, EscortTaskAction.Id: EscortTaskAction, @@ -1110,6 +1149,7 @@ def __eq__(self, other) -> bool: AttackMapObject.Id: AttackMapObject, EngageTargets.Id: EngageTargets, WWIIFollowBigFormation.Id: WWIIFollowBigFormation, + CarpetBombing.Id: CarpetBombing, } diff --git a/tests/missions/big-formation-carpet-bombing.miz b/tests/missions/big-formation-carpet-bombing.miz new file mode 100755 index 00000000..06a56e68 Binary files /dev/null and b/tests/missions/big-formation-carpet-bombing.miz differ diff --git a/tests/test_mission.py b/tests/test_mission.py index ec2ff50c..275eca4e 100644 --- a/tests/test_mission.py +++ b/tests/test_mission.py @@ -14,6 +14,10 @@ from dcs.task import WWIIFollowBigFormation from dcs.action import PictureAction from dcs.action import PictureToAll, PictureToCoalition, PictureToCountry, PictureToGroup, PictureToUnit +from dcs.task import Task, CarpetBombing, Expend, WeaponType +from dcs.action import Coalition +from dcs.mission import Mission +from enum import IntEnum class BasicTests(unittest.TestCase): @@ -1240,3 +1244,56 @@ def test_action_a_out_picture_u(self) -> None: m2.load_file(m2_name) self.assertEqual(m_action, m2.triggerrules.triggers[0].actions[5]) + + def test_smoke_action_carpet_bombing(self) -> None: + + # this is fictional enum to simplify addressing as defined + # in big-formation-carpet-bombing.miz + class FormationPosition(IntEnum): + Leader = 0, + Right = 1, + Back = 2, + Left = 3, + + def get_task(m: Mission, coalition: Coalition, country_name: str, + plane_group_idx: FormationPosition, point_idx: int, task_idx: int) -> Task: + return m.coalition[coalition.value].country( + country_name).plane_group[plane_group_idx].points[point_idx].tasks[task_idx] + + def get_carpetbombing_task(m: Mission, coalition: Coalition, country_name: str, + plane_group_idx: FormationPosition, point_idx: int, task_idx: int) -> CarpetBombing: + task = get_task(m, coalition, country_name, plane_group_idx, point_idx, task_idx) + assert isinstance(task, CarpetBombing) + return task + + m_name = "tests/missions/big-formation-carpet-bombing.miz" + m = dcs.mission.Mission() + m.load_file(m_name) + + m2_name = "missions/saved.big-formation-carpet-bombing.miz" + m.save(m2_name) + + m2 = dcs.mission.Mission() + m2.load_file(m2_name) + + coalition = Coalition.Blue + country = "USA" + + def validate_formation(m: Mission, m2: Mission, position: FormationPosition, expend: Expend, + weapon_type: WeaponType, altitude_enabled: bool) -> None: + point_idx = 2 + task_idx = 0 + + m_task = get_carpetbombing_task(m, coalition, country, position, point_idx, task_idx) + + self.assertEqual(m_task.params["expend"], expend) + self.assertEqual(m_task.params["weaponType"], weapon_type) + self.assertEqual(m_task.params["altitudeEnabled"], altitude_enabled) + + m2_task = get_carpetbombing_task(m2, coalition, country, position, point_idx, task_idx) + self.assertEqual(m_task, m2_task) + + validate_formation(m, m2, FormationPosition.Leader, Expend.Auto, WeaponType.Auto, True) + validate_formation(m, m2, FormationPosition.Left, Expend.Four, WeaponType.IronBombs, False) + validate_formation(m, m2, FormationPosition.Back, Expend.Auto, WeaponType.IronBombs, False) + validate_formation(m, m2, FormationPosition.Right, Expend.Auto, WeaponType.Auto, False)