Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support for CarpetBombing task #356

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
40 changes: 40 additions & 0 deletions dcs/task.py
Original file line number Diff line number Diff line change
Expand Up @@ -323,6 +323,13 @@ class Expend(Enum):
Half = "Half"
All = "All"

def __eq__(self, other: Any) -> bool:
if isinstance(other, str):
DanAlbert marked this conversation as resolved.
Show resolved Hide resolved
return self.value == other
if isinstance(other, Expend):
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Close enough so meh, non-blocking, but for the future just return super().__eq__(other) after doing any custom checks.

return self.value == other.value
return False


class AttackGroup(Task):
"""Attack group task action
Expand Down Expand Up @@ -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,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think this is actually right. I suspect this field is supposed to be "did the caller make a choice, or did they use the default?". This can't distinguish between a chosen altitude of 2000 and a default behavior. For that you need alt_above: int | None = None.

I've also got no idea what the impact of getting this wrong is. Might be just UI behavior in the ME? I won't block on this for now, but for future reference, that's how this thing ought to be written.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Also, fyi, this is just a more verbose way to write "altitudeEnabled": alt_above != CarpetBombing.DEFAULT_ALT

"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,
Expand Down Expand Up @@ -1110,6 +1149,7 @@ def __eq__(self, other) -> bool:
AttackMapObject.Id: AttackMapObject,
EngageTargets.Id: EngageTargets,
WWIIFollowBigFormation.Id: WWIIFollowBigFormation,
CarpetBombing.Id: CarpetBombing,
}


Expand Down
Binary file added tests/missions/big-formation-carpet-bombing.miz
Binary file not shown.
57 changes: 57 additions & 0 deletions tests/test_mission.py
Original file line number Diff line number Diff line change
Expand Up @@ -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):
Expand Down Expand Up @@ -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)
Loading