diff --git a/game/game.py b/game/game.py index 9bac3b9f..6401a421 100644 --- a/game/game.py +++ b/game/game.py @@ -49,13 +49,13 @@ Events: * InfantryTransportEvent - helicopter infantry transport """ EVENT_PROBABILITIES = { - BaseAttackEvent: [100, 10], + BaseAttackEvent: [100, 15], FrontlineAttackEvent: [100, 0], FrontlinePatrolEvent: [100, 0], StrikeEvent: [100, 0], - InterceptEvent: [25, 10], + InterceptEvent: [25, 15], InsurgentAttackEvent: [0, 10], - NavalInterceptEvent: [25, 10], + NavalInterceptEvent: [25, 15], InfantryTransportEvent: [25, 0], } diff --git a/game/operation/baseattack.py b/game/operation/baseattack.py index 51d887f8..3c11a203 100644 --- a/game/operation/baseattack.py +++ b/game/operation/baseattack.py @@ -59,5 +59,11 @@ class BaseAttackOperation(Operation): self.briefinggen.title = "Base attack" self.briefinggen.description = "The goal of an attacker is to lower defender presence by destroying their armor and aircraft. Base will be considered captured if attackers on the ground overrun the defenders. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu." + + if self.game.player == self.attacker_name: + self.briefinggen.append_waypoint("TARGET") + else: + pass + super(BaseAttackOperation, self).generate() diff --git a/game/operation/frontlineattack.py b/game/operation/frontlineattack.py index 3be6ca03..381e542e 100644 --- a/game/operation/frontlineattack.py +++ b/game/operation/frontlineattack.py @@ -51,4 +51,6 @@ class FrontlineAttackOperation(Operation): 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") + self.briefinggen.append_waypoint("CAS AREA EGRESS") super(FrontlineAttackOperation, self).generate() diff --git a/game/operation/frontlinepatrol.py b/game/operation/frontlinepatrol.py index 2bad15f6..0761aa2a 100644 --- a/game/operation/frontlinepatrol.py +++ b/game/operation/frontlinepatrol.py @@ -51,4 +51,6 @@ class FrontlinePatrolOperation(Operation): self.briefinggen.title = "Frontline CAP" self.briefinggen.description = "Providing CAP support for ground units attacking enemy lines. Enemy will scramble its CAS and your task is to intercept it. Operation will be considered successful if total number of friendly units will be lower than enemy by at least a factor of 0.8 (i.e. with 12 units from both sides, there should be at least 8 friendly units alive), lowering targets strength as a result." + self.briefinggen.append_waypoint("CAP AREA IP") + self.briefinggen.append_waypoint("CAP AREA EGRESS") super(FrontlinePatrolOperation, self).generate() diff --git a/game/operation/infantrytransport.py b/game/operation/infantrytransport.py index d96bd2cd..b35f0973 100644 --- a/game/operation/infantrytransport.py +++ b/game/operation/infantrytransport.py @@ -36,6 +36,7 @@ class InfantryTransportOperation(Operation): self.briefinggen.title = "Infantry transport" self.briefinggen.description = "Helicopter operation to transport infantry troops from the base to the front line. Lowers target strength" + self.briefinggen.append_waypoint("DROP POINT") # TODO: horrible, horrible hack # this will disable vehicle activation triggers, diff --git a/game/operation/insurgentattack.py b/game/operation/insurgentattack.py index 410517da..1f1c1cba 100644 --- a/game/operation/insurgentattack.py +++ b/game/operation/insurgentattack.py @@ -28,10 +28,11 @@ class InsurgentAttackOperation(Operation): conflict=conflict) def generate(self): - self.airgen.generate_defense(*assigned_units_split(self.strikegroup), at=self.defenders_starting_position) + self.airgen.generate_defenders_cas(*assigned_units_split(self.strikegroup), at=self.defenders_starting_position) self.armorgen.generate(self.target, {}) self.briefinggen.title = "Destroy insurgents" self.briefinggen.description = "Destroy vehicles of insurgents in close proximity of the friendly base. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu." + self.briefinggen.append_waypoint("TARGET") super(InsurgentAttackOperation, self).generate() diff --git a/game/operation/intercept.py b/game/operation/intercept.py index ba396dbe..1d763113 100644 --- a/game/operation/intercept.py +++ b/game/operation/intercept.py @@ -56,7 +56,14 @@ class InterceptOperation(Operation): self.airgen.generate_interception(*assigned_units_split(self.interceptors), at=self.attackers_starting_position) self.briefinggen.title = "Air Intercept" - self.briefinggen.description = "Intercept enemy supply transport aircraft. Escort will also be present if there are available planes on the base. Operation will be considered successful if most of the targets are destroyed, lowering targets strength as a result" + + if self.game.player == self.attacker_name: + self.briefinggen.description = "Intercept enemy supply transport aircraft. Escort will also be present if there are available planes on the base. Operation will be considered successful if most of the targets are destroyed, lowering targets strength as a result" + self.briefinggen.append_waypoint("TARGET") + for unit_type, count in self.transport.items(): + self.briefinggen.append_target("{} ({})".format(db.unit_type_name(unit_type), count)) + else: + self.briefinggen.description = "Escort friendly supply transport aircraft. Operation will be considered failed if most of the targets are destroyed, lowering CP strength as a result" super(InterceptOperation, self).generate() diff --git a/game/operation/navalintercept.py b/game/operation/navalintercept.py index 77c5d746..94c479e1 100644 --- a/game/operation/navalintercept.py +++ b/game/operation/navalintercept.py @@ -48,7 +48,13 @@ class NavalInterceptionOperation(Operation): ) self.briefinggen.title = "Naval Intercept" - self.briefinggen.description = "Destroy supply transport ships. Lowers target strength. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu." + if self.game.player == self.attacker_name: + self.briefinggen.description = "Destroy supply transport ships. Lowers target strength. Be advised that your flight will not attack anything until you explicitly tell them so by comms menu." + for unit_type, count in self.targets: + self.briefinggen.append_target("{} ({})".format(db.unit_type_name(unit_type), count)) + else: + self.briefinggen.description = "Protect supply transport ships." + self.briefinggen.append_waypoint("TARGET") super(NavalInterceptionOperation, self).generate() diff --git a/game/operation/operation.py b/game/operation/operation.py index 96f5d4d1..171f97cd 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -111,8 +111,6 @@ class Operation: else: self.envgen.load(self.environment_settings) - # @TODO: ADD WAYPOINT INFORMATION! - # main frequencies self.briefinggen.append_frequency("Flight", "251 MHz AM") if self.conflict.from_cp.is_global or self.conflict.to_cp.is_global: diff --git a/game/operation/strike.py b/game/operation/strike.py index 20391305..6b79dede 100644 --- a/game/operation/strike.py +++ b/game/operation/strike.py @@ -47,7 +47,8 @@ class StrikeOperation(Operation): category_counters[object.category] = category_counters.get(object.category, 0) + 1 markpoint_name = "{}{}".format(object.name_abbrev, category_counters[object.category]) targets.append((markpoint_name, object.position)) - self.briefinggen.append_target(str(object), markpoint_name) + self.briefinggen.append_target(str(object)) + self.briefinggen.append_waypoint("TARGET {} (TP {})".format(str(object), markpoint_name)) targets.sort(key=lambda x: self.from_cp.position.distance_to_point(x[1])) diff --git a/gen/aircraft.py b/gen/aircraft.py index b550ba18..d5237163 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -449,10 +449,7 @@ class AircraftConflictGenerator: at=at and at or self._group_point(self.conflict.air_attackers_location)) group.task = CAP.name - - heading = group.position.heading_between_point(self.conflict.position) - initial_wayp = group.add_waypoint(group.position.point_from_heading(heading, WORKAROUND_WAYP_DIST), INTERCEPTION_ALT, INTERCEPTION_AIRSPEED) - initial_wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE)) + group.points[0].tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE)) wayp = group.add_waypoint(self.conflict.position, WARM_START_ALTITUDE, INTERCEPTION_AIRSPEED) wayp.tasks.append(EngageTargets(max_distance=INTERCEPT_MAX_DISTANCE)) diff --git a/gen/briefinggen.py b/gen/briefinggen.py index 468d0d8e..8966f6ff 100644 --- a/gen/briefinggen.py +++ b/gen/briefinggen.py @@ -12,6 +12,7 @@ class BriefingGenerator: title = "" # type: str description = "" # type: str targets = None # type: typing.List[typing.Tuple[str, str]] + waypoints = None # type: typing.List[str] def __init__(self, mission: Mission, conflict: Conflict, game): self.m = mission @@ -20,6 +21,7 @@ class BriefingGenerator: self.freqs = [] self.targets = [] + self.waypoints = [] def append_frequency(self, name: str, frequency: str): self.freqs.append((name, frequency)) @@ -27,7 +29,14 @@ class BriefingGenerator: def append_target(self, description: str, markpoint: str = None): self.targets.append((description, markpoint)) + def append_waypoint(self, description: str): + self.waypoints.append(description) + def generate(self): + self.waypoints.insert(0, "INITIAL") + self.waypoints.append("RTB") + self.waypoints.append("RTB Landing") + description = "" if self.title: @@ -43,7 +52,12 @@ class BriefingGenerator: if self.targets: description += "\n\nTARGETS:" - for name, tp in self.targets: - description += "\n{} {}".format(name, "(TP {})".format(tp) if tp else "") + for i, (name, tp) in enumerate(self.targets): + description += "\n#{} {} {}".format(i+1, name, "(TP {})".format(tp) if tp else "") + + if self.waypoints: + description += "\n\nWAYPOINTS:" + for i, descr in enumerate(self.waypoints): + description += "\n#{}: {}".format(i+1, descr) self.m.set_description_text(description) diff --git a/resources/cau_groundobjects.p b/resources/cau_groundobjects.p index 88a38e8f..c7f616e6 100644 Binary files a/resources/cau_groundobjects.p and b/resources/cau_groundobjects.p differ diff --git a/resources/tools/generate_groundobjectsmap.py b/resources/tools/generate_groundobjectsmap.py index 45130ca4..107ae981 100644 --- a/resources/tools/generate_groundobjectsmap.py +++ b/resources/tools/generate_groundobjectsmap.py @@ -3,20 +3,41 @@ import typing from dcs.mission import Mission from dcs.mapping import Point +from dcs.terrain import * from dcs.unitgroup import VehicleGroup, StaticGroup from dcs.unit import * from dcs.statics import warehouse_map, fortification_map from game import db from gen.groundobjectsgen import TheaterGroundObject +from theater.caucasus import CaucasusTheater +from theater.persiangulf import PersianGulfTheater +from theater.nevada import NevadaTheater m = Mission() -m.load_file("./cau_groundobjects.miz") +m.load_file("resources/tools/cau_groundobjects.miz") + +if isinstance(m.terrain, Caucasus): + theater = CaucasusTheater(load_ground_objects=False) +elif isinstance(m.terrain, PersianGulf): + theater = PersianGulfTheater(load_ground_objects=False) +elif isinstance(m.terrain, Nevada): + theater = NevadaTheater(load_ground_objects=False) +else: + assert False -def parse_name(name: str) -> int: - first_part = name.split()[0].split("|") - return int(first_part[0]) if len(first_part) == 1 else int(first_part[1]) +def closest_cp(location: Point) -> (int, float): + global theater + min_distance, min_cp = None, None + + for cp in theater.controlpoints: + if not min_distance or location.distance_to_point(cp.position) < min_distance: + min_distance = location.distance_to_point(cp.position) + min_cp = cp.id + + assert min_cp is not None + return min_cp if __name__ == "__main__": @@ -27,11 +48,6 @@ if __name__ == "__main__": theater_object = TheaterGroundObject() theater_object.object_id = len(theater_objects) + 1 - try: - theater_object.cp_id = parse_name(str(unit.name)) - except Exception as e: - theater_object.cp_id = parse_name(str(group.name)) - theater_object.position = unit.position theater_object.heading = unit.heading @@ -40,15 +56,7 @@ if __name__ == "__main__": else: theater_object.dcs_identifier = unit.type - airport_distance = m.terrain.airport_by_id(theater_object.cp_id).position.distance_to_point(theater_object.position) - if airport_distance > 150000: - print("Object {} {} is placed {}m from airport {}!".format(theater_object.dcs_identifier, - group.name, - airport_distance, - m.terrain.airport_by_id(theater_object.cp_id))) - assert theater_object.dcs_identifier - assert theater_object.cp_id assert theater_object.object_id theater_objects.append(theater_object) @@ -61,11 +69,15 @@ if __name__ == "__main__": continue elif object_a.group_id: object_b.group_id = object_a.group_id + object_b.cp_id = object_a.cp_id elif object_b.group_id: object_a.group_id = object_b.group_id + object_a.cp_id = object_b.cp_id else: object_a.group_id = group_ids object_b.group_id = group_ids + object_a.cp_id = closest_cp(object_a.position) + object_b.cp_id = object_a.cp_id group_ids += 1 assert object_a.cp_id == object_b.cp_id, "Object {} and {} are placed in group with different airports!".format(object_a.string_identifier, object_b.string_identifier) @@ -73,15 +85,23 @@ if __name__ == "__main__": for a in theater_objects: if not a.group_id: a.group_id = group_ids + a.cp_id = closest_cp(a.position) group_ids += 1 - print("Total {} objects".format(len(theater_objects))) - with open("../cau_groundobjects.p", "wb") as f: + with open("resources/cau_groundobjects.p", "wb") as f: result = {} for theater_object in theater_objects: + assert theater_object.cp_id + assert theater_object.group_id + assert theater_object.object_id + if theater_object.cp_id not in result: result[theater_object.cp_id] = [] result[theater_object.cp_id].append(theater_object) + print("Total {} objects".format(len(theater_objects))) + for cp_id, objects in result.items(): + print("{}: total {} objects".format(m.terrain.airport_by_id(cp_id), len(objects))) + pickle.dump(result, f) diff --git a/theater/__init__.py b/theater/__init__.py index 7e1236e3..282ea4f3 100644 --- a/theater/__init__.py +++ b/theater/__init__.py @@ -1,3 +1,3 @@ from .controlpoint import * from .conflicttheater import * -from .base import * \ No newline at end of file +from .base import * diff --git a/theater/caucasus.py b/theater/caucasus.py index a449fce9..ed91c98c 100644 --- a/theater/caucasus.py +++ b/theater/caucasus.py @@ -44,7 +44,7 @@ class CaucasusTheater(ConflictTheater): carrier_1 = ControlPoint.carrier("Carrier", mapping.Point(-305810.6875, 406399.1875)) - def __init__(self): + def __init__(self, load_ground_objects=True): super(CaucasusTheater, self).__init__() self.add_controlpoint(self.soganlug, connected_to=[self.kutaisi, self.beslan]) @@ -73,8 +73,9 @@ class CaucasusTheater(ConflictTheater): self.carrier_1.captured = True self.soganlug.captured = True - with open("resources/cau_groundobjects.p", "rb") as f: - self.set_groundobject(pickle.load(f)) + if load_ground_objects: + with open("resources/cau_groundobjects.p", "rb") as f: + self.set_groundobject(pickle.load(f)) def add_controlpoint(self, point: ControlPoint, connected_to: typing.Collection[ControlPoint] = []): point.name = " ".join(re.split(r"[ -]", point.name)[:1]) diff --git a/theater/nevada.py b/theater/nevada.py index a6f4aef5..da04e2de 100644 --- a/theater/nevada.py +++ b/theater/nevada.py @@ -32,7 +32,7 @@ class NevadaTheater(ConflictTheater): jean = ControlPoint.from_airport(nevada.Jean_Airport, LAND, SIZE_REGULAR, 1.2) laughlin = ControlPoint.from_airport(nevada.Laughlin_Airport, LAND, SIZE_LARGE, IMPORTANCE_HIGH) - def __init__(self): + def __init__(self, load_ground_objects=True): super(NevadaTheater, self).__init__() self.add_controlpoint(self.mina, connected_to=[self.tonopah]) diff --git a/theater/persiangulf.py b/theater/persiangulf.py index 0c06b2f7..8041fdd9 100644 --- a/theater/persiangulf.py +++ b/theater/persiangulf.py @@ -45,7 +45,7 @@ class PersianGulfTheater(ConflictTheater): west_carrier = ControlPoint.carrier("East carrier", Point(-100531.972946, 60939.275818)) - def __init__(self): + def __init__(self, load_ground_objects=True): super(PersianGulfTheater, self).__init__() self.add_controlpoint(self.shiraz, connected_to=[self.lar, self.kerman])