From cd41bcf45cff0385796febb386fdf80e69c08316 Mon Sep 17 00:00:00 2001 From: Khopa Date: Sat, 13 Jun 2020 18:53:43 +0200 Subject: [PATCH] Generate WW2 Ship groups, added B17 to allies. Implemented modifiable doctrine to setup flight generator. --- changelog.md | 7 +- game/data/doctrine.py | 113 +++++++++++++++++ game/db.py | 8 +- game/factions/germany_1944.py | 9 +- game/factions/germany_1944_easy.py | 7 +- game/factions/usa_1944.py | 7 +- game/game.py | 9 ++ game/utils.py | 14 +++ gen/aircraft.py | 24 +--- gen/fleet/schnellboot.py | 19 +++ gen/fleet/ship_group_generator.py | 29 +++++ gen/fleet/uboat.py | 19 +++ gen/fleet/ww2lst.py | 22 ++++ gen/flights/ai_flight_planner.py | 119 +++++++----------- gen/flights/ai_flight_planner_db.py | 1 + gen/groundobjectsgen.py | 32 +++-- gen/sam/aaa_flak.py | 10 +- qt_ui/windows/QNewGameWizard.py | 9 +- .../flight/generator/QCASMissionGenerator.py | 2 +- .../flight/generator/QSEADMissionGenerator.py | 2 +- .../generator/QSTRIKEMissionGenerator.py | 2 +- resources/customized_payloads/B-17G.lua | 69 ++++++++++ resources/tools/groundobject_templates.miz | Bin 22762 -> 22775 bytes theater/start_generator.py | 37 +++++- theater/theatergroundobject.py | 1 + theater/thechannel.py | 58 ++++++++- 26 files changed, 501 insertions(+), 128 deletions(-) create mode 100644 game/data/doctrine.py create mode 100644 game/utils.py create mode 100644 gen/fleet/schnellboot.py create mode 100644 gen/fleet/uboat.py create mode 100644 gen/fleet/ww2lst.py create mode 100644 resources/customized_payloads/B-17G.lua diff --git a/changelog.md b/changelog.md index 84e1241c..ad08d52d 100644 --- a/changelog.md +++ b/changelog.md @@ -3,6 +3,7 @@ ##Features/Improvements : * **[Units/Factions]** Added P-47D-30 for factions allies_1944 +* **[Units/Factions]** Added factions : Bluefor Coldwar, Germany 1944 Easy * **[Campaign/Map]** Added a campaign in the Channel map * **[Campaign/Map]** Changed the Normandy campaign map @@ -22,6 +23,7 @@ * **[Flight Planner]** Added STRIKE mission generator * **[Flight Planner]** Added buttons to add autogenerated waypoints (ASCEND, DESCEND, RTB) * **[Flight Planner]** Improved waypoint list +* **[Flight Planner]** WW2 factions uses different parameters for flight planning. * **[Settings]** Added settings to disallow external views * **[Settings]** Added settings to choose F10 Map mode (All, Allies only, Player only, Fog of War, Map Only) @@ -37,6 +39,9 @@ * **[Map]** Added "ww2 bunker" building (WW2) * **[Map]** Added "ally camp" building (WW2) +* **[Map]** Added "V1 Launch Site" building (WW2) +* **[Map]** Added "A camp" building (WW2) + * **[Misc]** Made it possible to setup DCS Saved Games directory and DCS installation directory manually at first start ##Fixed issues : @@ -52,7 +57,7 @@ * **[Maps/Campaign]** Now using Vasiani airbase instead of Soganlung airport in North Caucasus campaign (More parking slots) -* **[Info Panel]** Message displayed on base capture event stated that the ennemy captured an airbase, while it was the player who captured it. +* **[Info Panel]** Message displayed on base capture event stated that the enemy captured an airbase, while it was the player who captured it. * **[Map View]** Graphical glitch on map when one building of an objective was destroyed, but not the others diff --git a/game/data/doctrine.py b/game/data/doctrine.py new file mode 100644 index 00000000..bfc5b714 --- /dev/null +++ b/game/data/doctrine.py @@ -0,0 +1,113 @@ +from game.utils import nm_to_meter, feet_to_meter + +MAX_NUMBER_OF_INTERCEPTION_GROUP = 3 +MISSION_DURATION = 120 # in minutes +CAP_EVERY_X_MINUTES = 20 +CAS_EVERY_X_MINUTES = 30 +SEAD_EVERY_X_MINUTES = 40 +STRIKE_EVERY_X_MINUTES = 40 + +INGRESS_EGRESS_DISTANCE = nm_to_meter(45) +INGRESS_ALT = feet_to_meter(20000) +EGRESS_ALT = feet_to_meter(20000) +PATROL_ALT_RANGE = (feet_to_meter(15000), feet_to_meter(33000)) +NAV_ALT = 9144 +PATTERN_ALTITUDE = feet_to_meter(5000) + +CAP_DEFAULT_ENGAGE_DISTANCE = nm_to_meter(40) + + + +MODERN_DOCTRINE = { + + "GENERATORS":{ + "CAS": True, + "CAP": True, + "SEAD": True, + "STRIKE": True, + "ANTISHIP": True, + }, + + "STRIKE_MAX_RANGE": 1500000, + "SEAD_MAX_RANGE": 1500000, + + "CAP_EVERY_X_MINUTES": 20, + "CAS_EVERY_X_MINUTES": 30, + "SEAD_EVERY_X_MINUTES": 40, + "STRIKE_EVERY_X_MINUTES": 40, + + "INGRESS_EGRESS_DISTANCE": nm_to_meter(45), + "INGRESS_ALT": feet_to_meter(20000), + "EGRESS_ALT": feet_to_meter(20000), + "PATROL_ALT_RANGE": (feet_to_meter(15000), feet_to_meter(33000)), + "PATTERN_ALTITUDE": feet_to_meter(5000), + + "CAP_PATTERN_LENGTH": (nm_to_meter(15), nm_to_meter(40)), + "FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(6), nm_to_meter(15)), + "CAP_DISTANCE_FROM_CP": (nm_to_meter(10), nm_to_meter(40)), + + "MAX_NUMBER_OF_INTERCEPTION_GROUP": 3, +} + +COLDWAR_DOCTRINE = { + + "GENERATORS": { + "CAS": True, + "CAP": True, + "SEAD": True, + "STRIKE": True, + "ANTISHIP": True, + }, + + "STRIKE_MAX_RANGE": 1500000, + "SEAD_MAX_RANGE": 1500000, + + "CAP_EVERY_X_MINUTES": 20, + "CAS_EVERY_X_MINUTES": 30, + "SEAD_EVERY_X_MINUTES": 40, + "STRIKE_EVERY_X_MINUTES": 40, + + "INGRESS_EGRESS_DISTANCE": nm_to_meter(30), + "INGRESS_ALT": feet_to_meter(18000), + "EGRESS_ALT": feet_to_meter(18000), + "PATROL_ALT_RANGE": (feet_to_meter(10000), feet_to_meter(24000)), + "PATTERN_ALTITUDE": feet_to_meter(5000), + + "CAP_PATTERN_LENGTH": (nm_to_meter(12), nm_to_meter(24)), + "FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(2), nm_to_meter(8)), + "CAP_DISTANCE_FROM_CP": (nm_to_meter(8), nm_to_meter(25)), + + "MAX_NUMBER_OF_INTERCEPTION_GROUP": 3, +} + +WWII_DOCTRINE = { + + "GENERATORS": { + "CAS": True, + "CAP": True, + "SEAD": False, + "STRIKE": True, + "ANTISHIP": True, + }, + + "STRIKE_MAX_RANGE": 1500000, + "SEAD_MAX_RANGE": 1500000, + + "CAP_EVERY_X_MINUTES": 20, + "CAS_EVERY_X_MINUTES": 30, + "SEAD_EVERY_X_MINUTES": 40, + "STRIKE_EVERY_X_MINUTES": 40, + + "INGRESS_EGRESS_DISTANCE": nm_to_meter(7), + "INGRESS_ALT": feet_to_meter(8000), + "EGRESS_ALT": feet_to_meter(8000), + "PATROL_ALT_RANGE": (feet_to_meter(4000), feet_to_meter(15000)), + "PATTERN_ALTITUDE": feet_to_meter(5000), + + "CAP_PATTERN_LENGTH": (nm_to_meter(8), nm_to_meter(18)), + "FRONTLINE_CAP_DISTANCE_FROM_FRONTLINE": (nm_to_meter(1), nm_to_meter(6)), + "CAP_DISTANCE_FROM_CP": (nm_to_meter(5), nm_to_meter(15)), + + "MAX_NUMBER_OF_INTERCEPTION_GROUP": 3, + +} \ No newline at end of file diff --git a/game/db.py b/game/db.py index 9533f8f4..1ecb9b8c 100644 --- a/game/db.py +++ b/game/db.py @@ -162,6 +162,7 @@ PRICES = { P_51D_30_NA: 6, P_51D: 6, P_47D_30: 6, + B_17G: 18, # armor Armor.APC_MTLB: 4, @@ -260,7 +261,6 @@ PRICES = { Artillery.M12_GMC: 2, Artillery.Sturmpanzer_IV_Brummbär: 2, - # ship CV_1143_5_Admiral_Kuznetsov: 100, CVN_74_John_C__Stennis: 100, @@ -350,6 +350,7 @@ UNIT_BY_TASK = { A_20G, P_47D_30, Ju_88A4, + B_17G ], Transport: [ IL_76MD, @@ -739,6 +740,8 @@ PLANE_PAYLOAD_OVERRIDES = { L_39C:COMMON_OVERRIDE, Su_17M4: COMMON_OVERRIDE, F_4E: COMMON_OVERRIDE, + P_47D_30:COMMON_OVERRIDE, + B_17G: COMMON_OVERRIDE, AH_64D:{ CAS: "AGM-114K*16" @@ -1053,4 +1056,5 @@ class DefaultLiveries: OH_58D.Liveries = DefaultLiveries F_16C_50.Liveries = DefaultLiveries P_51D_30_NA.Liveries = DefaultLiveries -Ju_88A4.Liveries = DefaultLiveries \ No newline at end of file +Ju_88A4.Liveries = DefaultLiveries +B_17G.Liveries = DefaultLiveries \ No newline at end of file diff --git a/game/factions/germany_1944.py b/game/factions/germany_1944.py index 9549f5fe..65039c9f 100644 --- a/game/factions/germany_1944.py +++ b/game/factions/germany_1944.py @@ -1,7 +1,9 @@ from dcs.planes import * +from dcs.ships import Uboat_VIIC_U_flak, Schnellboot_type_S130 from dcs.vehicles import * from game.data.building_data import WW2_GERMANY_BUILDINGS +from game.data.doctrine import WWII_DOCTRINE Germany_1944 = { "country": "Third Reich", @@ -32,11 +34,10 @@ Germany_1944 = { Infantry.Infantry_Mauser_98, AirDefence.AAA_8_8cm_Flak_36, ], - "shorad":[ + "shorad": [ AirDefence.AAA_8_8cm_Flak_36, ], "objects": WW2_GERMANY_BUILDINGS, - "doctrine": { - # TODO - } + "doctrine": WWII_DOCTRINE, + "boat": [Uboat_VIIC_U_flak, Schnellboot_type_S130] } \ No newline at end of file diff --git a/game/factions/germany_1944_easy.py b/game/factions/germany_1944_easy.py index db0b68ba..1d1edca0 100644 --- a/game/factions/germany_1944_easy.py +++ b/game/factions/germany_1944_easy.py @@ -1,7 +1,9 @@ from dcs.planes import * from dcs.vehicles import * +from dcs.ships import * from game.data.building_data import WW2_GERMANY_BUILDINGS +from game.data.doctrine import WWII_DOCTRINE Germany_1944_Easy = { "country": "Third Reich", @@ -29,7 +31,6 @@ Germany_1944_Easy = { AirDefence.AAA_8_8cm_Flak_36, ], "objects": WW2_GERMANY_BUILDINGS, - "doctrine": { - # TODO - } + "doctrine": WWII_DOCTRINE, + "boat": [Uboat_VIIC_U_flak, Schnellboot_type_S130] } \ No newline at end of file diff --git a/game/factions/usa_1944.py b/game/factions/usa_1944.py index 043041ce..a59487e9 100644 --- a/game/factions/usa_1944.py +++ b/game/factions/usa_1944.py @@ -3,6 +3,7 @@ from dcs.ships import * from dcs.vehicles import * from game.data.building_data import WW2_ALLIES_BUILDINGS +from game.data.doctrine import WWII_DOCTRINE USA_1944 = { "country": "USA", @@ -14,6 +15,7 @@ USA_1944 = { SpitfireLFMkIX, SpitfireLFMkIXCW, A_20G, + B_17G, Armor.MT_M4_Sherman, Armor.MT_M4A4_Sherman_Firefly, @@ -38,5 +40,8 @@ USA_1944 = { AirDefence.AAA_Bofors_40mm, ], "shorad":[ AirDefence.AAA_Bofors_40mm, - ], "objects": WW2_ALLIES_BUILDINGS + ], + "objects": WW2_ALLIES_BUILDINGS, + "doctrine": WWII_DOCTRINE, + "boat": [LS_Samuel_Chase] } \ No newline at end of file diff --git a/game/game.py b/game/game.py index 27be8a47..c90e91bf 100644 --- a/game/game.py +++ b/game/game.py @@ -73,6 +73,15 @@ class Game: self.informations = [] self.informations.append(Information("Game Start", "-" * 40, 0)) + + @property + def player_faction(self): + return db.FACTIONS[self.player_name] + + @property + def enemy_faction(self): + return db.FACTIONS[self.enemy_name] + def _roll(self, prob, mult): if self.settings.version == "dev": # always generate all events for dev diff --git a/game/utils.py b/game/utils.py new file mode 100644 index 00000000..34b82aa4 --- /dev/null +++ b/game/utils.py @@ -0,0 +1,14 @@ +def meter_to_feet(value_in_meter): + return int(3.28084 * value_in_meter) + + +def feet_to_meter(value_in_feet): + return int(float(value_in_feet)/3.048) + + +def meter_to_nm(value_in_meter): + return int(float(value_in_meter)*0.000539957) + + +def nm_to_meter(value_in_nm): + return int(float(value_in_nm)*1852) \ No newline at end of file diff --git a/gen/aircraft.py b/gen/aircraft.py index 6ea24693..81a1ce8f 100644 --- a/gen/aircraft.py +++ b/gen/aircraft.py @@ -5,38 +5,22 @@ from dcs.terrain.terrain import NoParkingSlotError from dcs.triggers import TriggerOnce, Event from game.settings import Settings -from gen.flights.ai_flight_planner import FlightPlanner, CAP_DEFAULT_ENGAGE_DISTANCE, nm_to_meter +from gen.flights.ai_flight_planner import FlightPlanner from gen.flights.flight import Flight, FlightType, FlightWaypointType from .conflictgen import * from .naming import * -from .triggergen import TRIGGER_WAYPOINT_OFFSET - -SPREAD_DISTANCE_FACTOR = 1, 2 -ESCORT_ENGAGEMENT_MAX_DIST = 100000 -WORKAROUND_WAYP_DIST = 1000 WARM_START_HELI_AIRSPEED = 120 WARM_START_HELI_ALT = 500 - WARM_START_ALTITUDE = 3000 WARM_START_AIRSPEED = 550 -INTERCEPTION_AIRSPEED = 1000 -BARCAP_RACETRACK_DISTANCE = 20000 +CAP_DURATION = 30 # minutes -ATTACK_CIRCLE_ALT = 1000 -ATTACK_CIRCLE_DURATION = 15 - -CAS_ALTITUDE = 800 RTB_ALTITUDE = 800 RTB_DISTANCE = 5000 HELI_ALT = 500 -TRANSPORT_LANDING_ALT = 2000 - -DEFENCE_ENGAGEMENT_MAX_DISTANCE = 60000 -INTERCEPT_MAX_DISTANCE = 200000 - class AircraftConflictGenerator: escort_targets = [] # type: typing.List[typing.Tuple[FlyingGroup, int]] @@ -393,8 +377,8 @@ class AircraftConflictGenerator: if not point.only_for_player or (point.only_for_player and flight.client_count > 0): pt = group.add_waypoint(Point(point.x, point.y), point.alt) if point.waypoint_type == FlightWaypointType.PATROL_TRACK: - action = OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack) - pt.tasks.append(action) + action = ControlledTask(OrbitAction(altitude=pt.alt, pattern=OrbitAction.OrbitPattern.RaceTrack)) + action.stop_after_duration(CAP_DURATION * 60) #for tgt in point.targets: # if hasattr(tgt, "position"): # engagetgt = EngageTargetsInZone(tgt.position, radius=CAP_DEFAULT_ENGAGE_DISTANCE, targets=[Targets.All.Air]) diff --git a/gen/fleet/schnellboot.py b/gen/fleet/schnellboot.py new file mode 100644 index 00000000..20459ac7 --- /dev/null +++ b/gen/fleet/schnellboot.py @@ -0,0 +1,19 @@ +import random + +from dcs.ships import Schnellboot_type_S130 + +from gen.sam.group_generator import GroupGenerator + + +class SchnellbootGroupGenerator(GroupGenerator): + + def __init__(self, game, ground_object, faction): + super(SchnellbootGroupGenerator, self).__init__(game, ground_object) + self.faction = faction + + def generate(self): + + for i in range(random.randint(2, 4)): + self.add_unit(Schnellboot_type_S130, "Schnellboot" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading) + + self.get_generated_group().points[0].speed = 20 \ No newline at end of file diff --git a/gen/fleet/ship_group_generator.py b/gen/fleet/ship_group_generator.py index 5892e28b..114312b6 100644 --- a/gen/fleet/ship_group_generator.py +++ b/gen/fleet/ship_group_generator.py @@ -1,6 +1,35 @@ +import random + from game import db from gen.fleet.carrier_group import CarrierGroupGenerator from gen.fleet.lha_group import LHAGroupGenerator +from dcs.ships import * + +from gen.fleet.schnellboot import SchnellbootGroupGenerator +from gen.fleet.uboat import UBoatGroupGenerator +from gen.fleet.ww2lst import WW2LSTGroupGenerator + +SHIP_MAP = { + Schnellboot_type_S130: SchnellbootGroupGenerator, + LS_Samuel_Chase: WW2LSTGroupGenerator, + Uboat_VIIC_U_flak: UBoatGroupGenerator +} + + +def generate_ship_group(game, ground_object, faction:str): + """ + This generate a ship group + :return: Nothing, but put the group reference inside the ground object + """ + faction = db.FACTIONS[faction] + if "boat" in faction.keys(): + ships = faction["boat"] + if len(ships) > 0: + sam = random.choice(ships) + generator = SHIP_MAP[sam](game, ground_object, faction) + generator.generate() + return generator.get_generated_group() + return None def generate_carrier_group(faction:str, game, ground_object): diff --git a/gen/fleet/uboat.py b/gen/fleet/uboat.py new file mode 100644 index 00000000..3087e428 --- /dev/null +++ b/gen/fleet/uboat.py @@ -0,0 +1,19 @@ +import random + +from dcs.ships import Uboat_VIIC_U_flak + +from gen.sam.group_generator import GroupGenerator + + +class UBoatGroupGenerator(GroupGenerator): + + def __init__(self, game, ground_object, faction): + super(UBoatGroupGenerator, self).__init__(game, ground_object) + self.faction = faction + + def generate(self): + + for i in range(random.randint(2, 6)): + self.add_unit(Uboat_VIIC_U_flak, "Uboat" + str(i), self.position.x + i * random.randint(100, 250), self.position.y + (random.randint(100, 200)-100), self.heading) + + self.get_generated_group().points[0].speed = 20 \ No newline at end of file diff --git a/gen/fleet/ww2lst.py b/gen/fleet/ww2lst.py new file mode 100644 index 00000000..ce421a97 --- /dev/null +++ b/gen/fleet/ww2lst.py @@ -0,0 +1,22 @@ +import random + +from dcs.ships import LS_Samuel_Chase, LST_Mk_II + +from gen.sam.group_generator import GroupGenerator + + +class WW2LSTGroupGenerator(GroupGenerator): + + def __init__(self, game, ground_object, faction): + super(WW2LSTGroupGenerator, self).__init__(game, ground_object) + self.faction = faction + + def generate(self): + + # Add LS Samuel Chase + self.add_unit(LS_Samuel_Chase, "SamuelChase", self.position.x, self.position.y, self.heading) + + for i in range(random.randint(2, 4)): + self.add_unit(LST_Mk_II, "LST" + str(i), self.position.x + i * random.randint(400, 600), self.position.y, self.heading) + + self.get_generated_group().points[0].speed = 20 \ No newline at end of file diff --git a/gen/flights/ai_flight_planner.py b/gen/flights/ai_flight_planner.py index 739a895c..18980fe4 100644 --- a/gen/flights/ai_flight_planner.py +++ b/gen/flights/ai_flight_planner.py @@ -3,46 +3,15 @@ import operator import random from game import db +from game.data.doctrine import MODERN_DOCTRINE +from game.data.radar_db import UNITS_WITH_RADAR +from game.utils import meter_to_feet, nm_to_meter from gen import Conflict from gen.flights.ai_flight_planner_db import INTERCEPT_CAPABLE, CAP_CAPABLE, CAS_CAPABLE, SEAD_CAPABLE, STRIKE_CAPABLE from gen.flights.flight import Flight, FlightType, FlightWaypoint, FlightWaypointType -def meter_to_feet(value_in_meter): - return int(3.28084 * value_in_meter) - - -def feet_to_meter(value_in_feet): - return int(float(value_in_feet)/3.048) - - -def meter_to_nm(value_in_meter): - return int(float(value_in_meter)*0.000539957) - - -def nm_to_meter(value_in_nm): - return int(float(value_in_nm)*1852) - - -# TODO : Ideally should be based on the aircraft type instead / Availability of fuel -STRIKE_MAX_RANGE = 1500000 -SEAD_MAX_RANGE = 1500000 - -MAX_NUMBER_OF_INTERCEPTION_GROUP = 3 -MISSION_DURATION = 120 # in minutes -CAP_EVERY_X_MINUTES = 20 -CAS_EVERY_X_MINUTES = 30 -SEAD_EVERY_X_MINUTES = 40 -STRIKE_EVERY_X_MINUTES = 40 - -INGRESS_EGRESS_DISTANCE = nm_to_meter(45) -INGRESS_ALT = feet_to_meter(20000) -EGRESS_ALT = feet_to_meter(20000) -PATROL_ALT_RANGE = (feet_to_meter(15000), feet_to_meter(33000)) -NAV_ALT = 9144 -PATTERN_ALTITUDE = feet_to_meter(5000) - -CAP_DEFAULT_ENGAGE_DISTANCE = nm_to_meter(40) +MISSION_DURATION = 120 class FlightPlanner: @@ -54,6 +23,17 @@ class FlightPlanner: self.game = game self.aircraft_inventory = {} # local copy of the airbase inventory + if from_cp.captured: + self.faction = self.game.player_faction + else: + self.faction = self.game.enemy_faction + + if "doctrine" in self.faction.keys(): + self.doctrine = self.faction["doctrine"] + else: + self.doctrine = MODERN_DOCTRINE + + def reset(self): """ Reset the planned flights and available units @@ -111,7 +91,7 @@ class FlightPlanner: """ # At least try to generate one interceptor group - number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, MAX_NUMBER_OF_INTERCEPTION_GROUP), 1) + number_of_interceptor_groups = min(max(sum([v for k, v in self.aircraft_inventory.items()]) / 4, self.doctrine["MAX_NUMBER_OF_INTERCEPTION_GROUP"]), 1) possible_interceptors = [k for k in self.aircraft_inventory.keys() if k in INTERCEPT_CAPABLE] if len(possible_interceptors) <= 0: @@ -144,7 +124,7 @@ class FlightPlanner: inventory = dict({k: v for k, v in self.aircraft_inventory.items() if k in possible_aircraft}) offset = random.randint(0,5) - for i in range(int(MISSION_DURATION/CAP_EVERY_X_MINUTES)): + for i in range(int(MISSION_DURATION/self.doctrine["CAP_EVERY_X_MINUTES"])): try: unit = random.choice([k for k, v in inventory.items() if v >= 2]) @@ -155,7 +135,7 @@ class FlightPlanner: flight = Flight(unit, 2, self.from_cp, FlightType.CAP) flight.points = [] - flight.scheduled_in = offset + i*random.randint(CAP_EVERY_X_MINUTES-5, CAP_EVERY_X_MINUTES+5) + flight.scheduled_in = offset + i*random.randint(self.doctrine["CAP_EVERY_X_MINUTES"] - 5, self.doctrine["CAP_EVERY_X_MINUTES"] + 5) if len(self._get_cas_locations()) > 0: enemy_cp = random.choice(self._get_cas_locations()) @@ -182,7 +162,7 @@ class FlightPlanner: if len(cas_location) > 0: offset = random.randint(0,5) - for i in range(int(MISSION_DURATION/CAS_EVERY_X_MINUTES)): + for i in range(int(MISSION_DURATION/self.doctrine["CAS_EVERY_X_MINUTES"])): try: unit = random.choice([k for k, v in inventory.items() if v >= 2]) @@ -192,7 +172,7 @@ class FlightPlanner: inventory[unit] = inventory[unit] - 2 flight = Flight(unit, 2, self.from_cp, FlightType.CAS) flight.points = [] - flight.scheduled_in = offset + i * random.randint(CAS_EVERY_X_MINUTES - 5, CAS_EVERY_X_MINUTES + 5) + flight.scheduled_in = offset + i * random.randint(self.doctrine["CAS_EVERY_X_MINUTES"] - 5, self.doctrine["CAS_EVERY_X_MINUTES"] + 5) location = random.choice(cas_location) self.generate_cas(flight, flight.from_cp, location) @@ -215,7 +195,7 @@ class FlightPlanner: if len(self.potential_sead_targets) > 0: offset = random.randint(0,5) - for i in range(int(MISSION_DURATION/SEAD_EVERY_X_MINUTES)): + for i in range(int(MISSION_DURATION/self.doctrine["SEAD_EVERY_X_MINUTES"])): if len(self.potential_sead_targets) <= 0: break @@ -229,7 +209,7 @@ class FlightPlanner: flight = Flight(unit, 2, self.from_cp, random.choice([FlightType.SEAD, FlightType.DEAD])) flight.points = [] - flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5) + flight.scheduled_in = offset + i*random.randint(self.doctrine["SEAD_EVERY_X_MINUTES"] - 5, self.doctrine["SEAD_EVERY_X_MINUTES"] + 5) location = self.potential_sead_targets[0][0] self.potential_sead_targets.pop(0) @@ -254,7 +234,7 @@ class FlightPlanner: if len(self.potential_strike_targets) > 0: offset = random.randint(0,5) - for i in range(int(MISSION_DURATION/STRIKE_EVERY_X_MINUTES)): + for i in range(int(MISSION_DURATION/self.doctrine["STRIKE_EVERY_X_MINUTES"])): if len(self.potential_strike_targets) <= 0: break @@ -268,7 +248,7 @@ class FlightPlanner: flight = Flight(unit, 2, self.from_cp, FlightType.STRIKE) flight.points = [] - flight.scheduled_in = offset + i*random.randint(SEAD_EVERY_X_MINUTES-5, SEAD_EVERY_X_MINUTES+5) + flight.scheduled_in = offset + i*random.randint(self.doctrine["STRIKE_EVERY_X_MINUTES"] - 5, self.doctrine["STRIKE_EVERY_X_MINUTES"] + 5) location = self.potential_strike_targets[0][0] self.potential_strike_targets.pop(0) @@ -306,7 +286,7 @@ class FlightPlanner: distance = math.hypot(cp.position.x - self.from_cp.position.x, cp.position.y - self.from_cp.position.y) - if distance > 2*STRIKE_MAX_RANGE: + if distance > 2*self.doctrine["STRIKE_MAX_RANGE"]: # Then it's unlikely any child ground object is in range return @@ -318,7 +298,7 @@ class FlightPlanner: distance = math.hypot(cp.position.x - self.from_cp.position.x, cp.position.y - self.from_cp.position.y) - if distance < SEAD_MAX_RANGE: + if distance < self.doctrine["SEAD_MAX_RANGE"]: self.potential_strike_targets.append((g, distance)) added_group.append(g) @@ -339,7 +319,7 @@ class FlightPlanner: cp.position.y - self.from_cp.position.y) # Then it's unlikely any ground object is range - if distance > 2*SEAD_MAX_RANGE: + if distance > 2*self.doctrine["SEAD_MAX_RANGE"]: return for g in cp.ground_objects: @@ -347,8 +327,7 @@ class FlightPlanner: if g.dcs_identifier == "AA": # Check that there is at least one unit with a radar in the ground objects unit groups - number_of_units = sum([len([r for r in group.units if hasattr(db.unit_type_from_name(r.type), "detection_range") - and db.unit_type_from_name(r.type).detection_range > 1000]) for group in g.groups]) + number_of_units = sum([len([r for r in group.units if db.unit_type_from_name(r.type) in UNITS_WITH_RADAR]) for group in g.groups]) if number_of_units <= 0: continue @@ -356,7 +335,7 @@ class FlightPlanner: distance = math.hypot(cp.position.x - self.from_cp.position.x, cp.position.y - self.from_cp.position.y) - if distance < SEAD_MAX_RANGE: + if distance < self.doctrine["SEAD_MAX_RANGE"]: self.potential_sead_targets.append((g, distance)) self.potential_sead_targets.sort(key=operator.itemgetter(1)) @@ -385,8 +364,8 @@ class FlightPlanner: ingress_heading = heading - 180 + 25 egress_heading = heading - 180 - 25 - ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE) - ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT) + ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"]) + ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, self.doctrine["INGRESS_ALT"]) ingress_point.pretty_name = "INGRESS on " + location.obj_name ingress_point.description = "INGRESS on " + location.obj_name ingress_point.name = "INGRESS" @@ -428,8 +407,8 @@ class FlightPlanner: ingress_point.targets.append(location) flight.points.append(point) - egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE) - egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT) + egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"]) + egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, self.doctrine["EGRESS_ALT"]) egress_point.name = "EGRESS" egress_point.pretty_name = "EGRESS from " + location.obj_name egress_point.description = "EGRESS from " + location.obj_name @@ -447,22 +426,20 @@ class FlightPlanner: Generate a barcap flight at a given location :param flight: Flight to setup :param for_cp: CP to protect - :param location: Location to protect in priority - :param location: Is the location to protect a frontline """ flight.flight_type = FlightType.BARCAP if for_cp.is_carrier else FlightType.CAP - patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1]) + patrol_alt = random.randint(self.doctrine["PATROL_ALT_RANGE"][0], self.doctrine["PATROL_ALT_RANGE"][1]) if len(for_cp.ground_objects) > 0: loc = random.choice(for_cp.ground_objects) hdg = for_cp.position.heading_between_point(loc.position) - radius = nm_to_meter(random.randint(15, 40)) + radius = random.randint(self.doctrine["CAP_PATTERN_LENGTH"][0], self.doctrine["CAP_PATTERN_LENGTH"][1]) orbit0p = loc.position.point_from_heading(hdg - 90, radius) orbit1p = loc.position.point_from_heading(hdg + 90, radius) else: - loc = for_cp.position.point_from_heading(random.randint(0, 360), random.randint(nm_to_meter(10), nm_to_meter(40))) + loc = for_cp.position.point_from_heading(random.randint(0, 360), random.randint(self.doctrine["CAP_DISTANCE_FROM_CP"][0], self.doctrine["CAP_DISTANCE_FROM_CP"][1])) hdg = for_cp.position.heading_between_point(loc) - radius = nm_to_meter(random.randint(15, 40)) + radius = random.randint(self.doctrine["CAP_PATTERN_LENGTH"][0], self.doctrine["CAP_PATTERN_LENGTH"][1]) orbit0p = loc.point_from_heading(hdg - 90, radius) orbit1p = loc.point_from_heading(hdg + 90, radius) @@ -507,7 +484,7 @@ class FlightPlanner: :param enemy_cp: Enemy connected cp """ flight.flight_type = FlightType.CAP - patrol_alt = random.randint(PATROL_ALT_RANGE[0], PATROL_ALT_RANGE[1]) + patrol_alt = random.randint(self.doctrine["PATROL_ALT_RANGE"][0], self.doctrine["PATROL_ALT_RANGE"][1]) # Find targets waypoints ingress, heading, distance = Conflict.frontline_vector(ally_cp, enemy_cp, self.game.theater) @@ -563,8 +540,8 @@ class FlightPlanner: ingress_heading = heading - 180 + 25 egress_heading = heading - 180 - 25 - ingress_pos = location.position.point_from_heading(ingress_heading, INGRESS_EGRESS_DISTANCE) - ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, INGRESS_ALT) + ingress_pos = location.position.point_from_heading(ingress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"]) + ingress_point = FlightWaypoint(ingress_pos.x, ingress_pos.y, self.doctrine["INGRESS_ALT"]) ingress_point.name = "INGRESS" ingress_point.pretty_name = "INGRESS on " + location.obj_name ingress_point.description = "INGRESS on " + location.obj_name @@ -599,8 +576,8 @@ class FlightPlanner: ingress_point.targets.append(location) flight.points.append(point) - egress_pos = location.position.point_from_heading(egress_heading, INGRESS_EGRESS_DISTANCE) - egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, EGRESS_ALT) + egress_pos = location.position.point_from_heading(egress_heading, self.doctrine["INGRESS_EGRESS_DISTANCE"]) + egress_point = FlightWaypoint(egress_pos.x, egress_pos.y, self.doctrine["EGRESS_ALT"]) egress_point.name = "INGRESS" egress_point.pretty_name = "EGRESS from " + location.obj_name egress_point.description = "EGRESS from " + location.obj_name @@ -670,11 +647,11 @@ class FlightPlanner: """ ascend_heading = from_cp.heading pos_ascend = from_cp.position.point_from_heading(ascend_heading, 10000) - ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, PATTERN_ALTITUDE) + ascend = FlightWaypoint(pos_ascend.x, pos_ascend.y, self.doctrine["PATTERN_ALTITUDE"]) ascend.name = "ASCEND" ascend.alt_type = "RADIO" - ascend.description = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], then proceed to next waypoint" - ascend.pretty_name = "Ascend to alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]" + ascend.description = "Ascend to alt [" + str(meter_to_feet(self.doctrine["PATTERN_ALTITUDE"])) + " ft AGL], then proceed to next waypoint" + ascend.pretty_name = "Ascend to alt [" + str(meter_to_feet(self.doctrine["PATTERN_ALTITUDE"])) + " ft AGL]" ascend.waypoint_type = FlightWaypointType.ASCEND_POINT return ascend @@ -687,11 +664,11 @@ class FlightPlanner: """ ascend_heading = from_cp.heading descend = from_cp.position.point_from_heading(ascend_heading - 180, 10000) - descend = FlightWaypoint(descend.x, descend.y, PATTERN_ALTITUDE) + descend = FlightWaypoint(descend.x, descend.y, self.doctrine["PATTERN_ALTITUDE"]) descend.name = "DESCEND" descend.alt_type = "RADIO" - descend.description = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL], contact tower, and land" - descend.pretty_name = "Descend to pattern alt [" + str(meter_to_feet(PATTERN_ALTITUDE)) + " ft AGL]" + descend.description = "Descend to pattern alt [" + str(meter_to_feet(self.doctrine["PATTERN_ALTITUDE"])) + " ft AGL], contact tower, and land" + descend.pretty_name = "Descend to pattern alt [" + str(meter_to_feet(self.doctrine["PATTERN_ALTITUDE"])) + " ft AGL]" descend.waypoint_type = FlightWaypointType.DESCENT_POINT return descend diff --git a/gen/flights/ai_flight_planner_db.py b/gen/flights/ai_flight_planner_db.py index 7debd2f8..b207fc0c 100644 --- a/gen/flights/ai_flight_planner_db.py +++ b/gen/flights/ai_flight_planner_db.py @@ -171,6 +171,7 @@ STRIKE_CAPABLE = [ P_51D, P_47D_30, A_20G, + B_17G, SpitfireLFMkIXCW, SpitfireLFMkIX, diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index 46a514e6..050f1f2c 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -66,21 +66,35 @@ class GroundObjectsGenerator: utype = unit_type_from_name(g.units[0].type) - vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading) - vg.units[0].name = self.m.string(g.units[0].name) - for i, u in enumerate(g.units): - if i > 0: - vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type) - vehicle.position.x = u.position.x - vehicle.position.y = u.position.y - vehicle.heading = u.heading - vg.add_unit(vehicle) + if not ground_object.sea_object: + vg = self.m.vehicle_group(side, g.name, utype, position=g.position, heading=g.units[0].heading) + vg.units[0].name = self.m.string(g.units[0].name) + for i, u in enumerate(g.units): + if i > 0: + vehicle = Vehicle(self.m.next_unit_id(), self.m.string(u.name), u.type) + vehicle.position.x = u.position.x + vehicle.position.y = u.position.y + vehicle.heading = u.heading + vg.add_unit(vehicle) + else: + vg = self.m.ship_group(side, g.name, utype, position=g.position, + heading=g.units[0].heading) + vg.units[0].name = self.m.string(g.units[0].name) + for i, u in enumerate(g.units): + utype = unit_type_from_name(u.type) + if i > 0: + ship = Ship(self.m.next_unit_id(), self.m.string(u.name), utype) + ship.position.x = u.position.x + ship.position.y = u.position.y + ship.heading = u.heading + vg.add_unit(ship) if self.game.settings.perf_red_alert_state: vg.points[0].tasks.append(OptAlarmState(2)) else: vg.points[0].tasks.append(OptAlarmState(1)) + elif ground_object.dcs_identifier in ["CARRIER", "LHA"]: for g in ground_object.groups: if len(g.units) > 0: diff --git a/gen/sam/aaa_flak.py b/gen/sam/aaa_flak.py index afbae9f0..54ee600a 100644 --- a/gen/sam/aaa_flak.py +++ b/gen/sam/aaa_flak.py @@ -15,7 +15,7 @@ class FlakGenerator(GroupGenerator): grid_x = random.randint(2, 4) grid_y = random.randint(2, 4) - spacing = random.randint(10,40) + spacing = random.randint(30,60) index = 0 mixed = random.choice([True, False]) @@ -25,8 +25,8 @@ class FlakGenerator(GroupGenerator): for j in range(grid_y): index = index+1 self.add_unit(unit_type, "AAA#" + str(index), - self.position.x + spacing*i, - self.position.y + spacing*j, self.heading) + self.position.x + spacing*i + random.randint(1,5), + self.position.y + spacing*j + random.randint(1,5), self.heading) if(mixed): unit_type = random.choice(GFLAK) @@ -35,5 +35,5 @@ class FlakGenerator(GroupGenerator): for i in range(grid_x): for j in range(grid_y): self.add_unit(Unarmed.Blitz_3_6_6700A, "AAA#" + str(index), - self.position.x + 200 + 9*i, - self.position.y + 9*j, 90) \ No newline at end of file + self.position.x + 200 + 15*i + random.randint(1,5), + self.position.y + 15*j + random.randint(1,5), 90) \ No newline at end of file diff --git a/qt_ui/windows/QNewGameWizard.py b/qt_ui/windows/QNewGameWizard.py index f61bfad5..c0d2cf96 100644 --- a/qt_ui/windows/QNewGameWizard.py +++ b/qt_ui/windows/QNewGameWizard.py @@ -43,6 +43,7 @@ class NewGameWizard(QtWidgets.QWizard): isTerrainNormandy = self.field("isTerrainNormandy") isTerrainNormandySmall = self.field("isTerrainNormandySmall") isTerrainChannel = self.field("isTerrainChannel") + isTerrainChannelComplete = self.field("isTerrainChannelComplete") isTerrainEmirates = self.field("isTerrainEmirates") timePeriod = db.TIME_PERIODS[list(db.TIME_PERIODS.keys())[self.field("timePeriod")]] midGame = self.field("midGame") @@ -71,6 +72,8 @@ class NewGameWizard(QtWidgets.QWizard): conflicttheater = normandy.NormandySmall() elif isTerrainChannel: conflicttheater = thechannel.ChannelTheater() + elif isTerrainChannelComplete: + conflicttheater = thechannel.ChannelTheaterComplete() else: conflicttheater = caucasus.CaucasusTheater() @@ -248,8 +251,10 @@ class TheaterConfiguration(QtWidgets.QWizardPage): terrainNormandy.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Normandy"])) terrainNormandySmall = QtWidgets.QRadioButton("Normandy Small") terrainNormandySmall.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Normandy"])) - terrainChannel = QtWidgets.QRadioButton("Channel") + terrainChannel = QtWidgets.QRadioButton("The Channel : Start in Dunkirk") terrainChannel.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Channel"])) + terrainChannelComplete = QtWidgets.QRadioButton("The Channel : Battle of Britain") + terrainChannelComplete.setIcon(QtGui.QIcon(CONST.ICONS["Terrain_Channel"])) terrainCaucasusSmall.setChecked(True) # Time Period @@ -273,6 +278,7 @@ class TheaterConfiguration(QtWidgets.QWizardPage): self.registerField('isTerrainNormandy', terrainNormandy) self.registerField('isTerrainNormandySmall', terrainNormandySmall) self.registerField('isTerrainChannel', terrainChannel) + self.registerField('isTerrainChannelComplete', terrainChannelComplete) self.registerField('timePeriod', timePeriodSelect) # Build layout @@ -287,6 +293,7 @@ class TheaterConfiguration(QtWidgets.QWizardPage): terrainGroupLayout.addWidget(terrainNttr) terrainGroupLayout.addWidget(terrainNormandy) terrainGroupLayout.addWidget(terrainNormandySmall) + terrainGroupLayout.addWidget(terrainChannelComplete) terrainGroupLayout.addWidget(terrainChannel) terrainGroup.setLayout(terrainGroupLayout) diff --git a/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py index f655c693..cfae4e52 100644 --- a/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py +++ b/qt_ui/windows/mission/flight/generator/QCASMissionGenerator.py @@ -3,7 +3,7 @@ from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox from dcs import Point from game import Game -from gen.flights.ai_flight_planner import meter_to_nm +from game.utils import meter_to_nm from gen.flights.flight import Flight from qt_ui.widgets.combos.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox from qt_ui.windows.mission.flight.generator.QAbstractMissionGenerator import QAbstractMissionGenerator diff --git a/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py index e243a5f5..7221844c 100644 --- a/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py +++ b/qt_ui/windows/mission/flight/generator/QSEADMissionGenerator.py @@ -2,7 +2,7 @@ from PySide2.QtGui import Qt from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox from game import Game -from gen.flights.ai_flight_planner import meter_to_nm +from game.utils import meter_to_nm from gen.flights.flight import Flight from qt_ui.widgets.combos.QSEADTargetSelectionComboBox import QSEADTargetSelectionComboBox from qt_ui.widgets.views.QSeadTargetInfoView import QSeadTargetInfoView diff --git a/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py b/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py index ea948814..1c07d735 100644 --- a/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py +++ b/qt_ui/windows/mission/flight/generator/QSTRIKEMissionGenerator.py @@ -2,7 +2,7 @@ from PySide2.QtGui import Qt from PySide2.QtWidgets import QLabel, QHBoxLayout, QVBoxLayout, QGroupBox from game import Game -from gen.flights.ai_flight_planner import meter_to_nm +from game.utils import meter_to_nm from gen.flights.flight import Flight from qt_ui.widgets.combos.QStrikeTargetSelectionComboBox import QStrikeTargetSelectionComboBox from qt_ui.widgets.views.QStrikeTargetInfoView import QStrikeTargetInfoView diff --git a/resources/customized_payloads/B-17G.lua b/resources/customized_payloads/B-17G.lua new file mode 100644 index 00000000..afc82a21 --- /dev/null +++ b/resources/customized_payloads/B-17G.lua @@ -0,0 +1,69 @@ +local unitPayloads = { + ["name"] = "B-17G", + ["payloads"] = { + [1] = { + ["name"] = "STRIKE", + ["pylons"] = { + [1] = { + ["CLSID"] = "{12xM64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 32, + }, + }, + [2] = { + ["name"] = "CAP", + ["pylons"] = { + [1] = { + ["CLSID"] = "{12xM64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 32, + }, + }, + [3] = { + ["name"] = "SEAD", + ["pylons"] = { + [1] = { + ["CLSID"] = "{12xM64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 32, + }, + }, + [4] = { + ["name"] = "ANTISHIP", + ["pylons"] = { + [1] = { + ["CLSID"] = "{12xM64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 32, + }, + }, + [5] = { + ["name"] = "CAS", + ["pylons"] = { + [1] = { + ["CLSID"] = "{12xM64}", + ["num"] = 1, + }, + }, + ["tasks"] = { + [1] = 32, + }, + }, + }, + ["tasks"] = { + }, + ["unitType"] = "B-17G", +} +return unitPayloads diff --git a/resources/tools/groundobject_templates.miz b/resources/tools/groundobject_templates.miz index 10ddade8c90da2f774dbec9467260a0cb3ca8820..b4fe14370e4046a3ccc3bc41258a1c89421f75b2 100644 GIT binary patch delta 18249 zcmY&_p?7`mM z)m1f1X02X_RiJlOpn$R*I0Oa=7z_*u2nY$tPPg2gATJ0=c?%>6^!tBoES#Jy>}*dm z^i}a?>dVhw=rP()%LmV*!<|5JS{5P^@$K&pwti>Rh~Z+ue4~{)?I(Qd$c%%~lBP7B z9WJGl)nS|dz$Q&++CE%&IaI-Q=8^N!j(h((b1V&5bFK!w0rxbjx0LIt?Zd5e%PA#E zyEn7P&Fj1e{9U`ptMQziF#`{-^xOnN>)VC+Oy?H}EncMDj z-BoNstZh0X>W8&gO|*gvhS*Ui*Cds)5O; zp4brk<@mTPe1{0B+{3h2_qkK{WqiPa_s4Q_+{Oc&oMnRNT)5J9U9S6tqYe%W(J`gT~nF%YGe!kZo!b+;<9~SNwCj# zs9*=j(Bt@Ui8#|K1Bp7IB%st&Hss}5d=fw7_Oa(;%kx#*)+1TT`?8@?NEVBn%u|eY z@!84@XeyaMqdpM3b98e}b{Um9jQjf9CBlkZuz<}pV5G>u+x|F%lx(193+CRZTP$kF zf>#hfa&fMn=vAdA2T9~ZNO^EKPkBAg$_Ty0^1`QB(OgkAQDUl#ztNLP&~!be@=HKs z^StQ#sOSDdAUgA&@7n-Z_+zVZ-^Gf(b7osQ;5>kQH)OeY<7&4)ICAkDMKo+ks9EU} z(rbI)K!@U%sKY*wp!C$S^4Zbr`-@{;()I!Ud|l1Mc%$@vz`d2wfe*gHUctWmQOoyS z!qJ}|L?eJqJ&Nb|{x6CfD!ra+# zK*&%~?=~bLm!(sv!+Qfap!CXJnoi>=w)t>s={>9DK|OQF9w_1#dr#AGuYKX@|#o1o-wzF?mg^b21zlPzFp+ zyS6DmRpBbEC9FlUx=?FIwfl@tUs0FRe!FV5wSE|{8UB{&I$lhz@k5{_Q~&j-)aP@a zR}I`$qSSbCBLh^1`bUO0A`=Tm!0L709WnWhwVH=uXyLA|SmpX9NO$tHN%lSXeZB;iO6oRhaKT6xGX7e_b*{3XNB#he8 z(=}8{r!DC?`#8l?HM9-xfK_WV?p$jA`RmxUP@3eSjm}xIBMZ7m+5t=ro7K(GBYF^_ z(Jz>5$W=Yt3YO$)`EX`R^*J^@v?kFry8W2_qeY(H!&Pe&lU(0kogVGo`xlVud-aLe zyzgJws(&^ZF*WKdP5Qh;dKIddiSCo{i^e8ZL#7;`h9+(x`-zvC@_ad=)5Sn`or;+< zrZs}Z%|J4XlZzY(y_qV_pw~I}$eg2(n>1Z)$$Y&adoWY9wJTtg?x03q$-{2BK1p53 zt#Pc8F>^P3JGg*u3zcfH3a`4)FLz@a>*l+yY5%-M{$q7w$DLS6uAI~7@#2O_m%7h~ zhxS&piH0~F&c)rXhEzv#1zDyFb#JQ~eTdNRHU4eWasC;24w9q9?OkiR>scLMjCCS% zz6hWn_ri-1D7hFJo;f#RQ6z?&%`9)FeT$3+4|TVkdx%b@;H|4lJ{1l zLjy%S!#ci=MTh?#lNMuy79#}161U(Iu8w#u*THU`0b`st0R&SPgGLSSZT}|26phC; zF3YeYR5>1ab+bqvcD5!H5$MXDe^9T+1!NGHRMku4RD z?dNLS@b+69+fEo3mFG&J++cs4Yr*CmoTC3 zr}Dx{prZ8($efPD_L%1UUh6f{f^{dXWmj(9!`d=eX20> zP|pR_+-WoYRN9$h^nlD~!ngoMjv>V!*Km*uBNWsoE%RHB44Q$Mwf52pjj z5-K<&a`y-c=cayhuOA)?TzQ<6>6o|Jl>3t*r{)KYcv~4%N?X0gg&z#C_X(9w;d)6% zMy?T(4&Y6Kr|-Yk^Y6bKqTVdani(FUPZ6jAl{|ZG=hA<3#A0ObVN(vChx~oivE`87 z44;_1flJd94%T#S{hgzn?17ZI&q+B98?u%+np(4M%m5EyRObAVyw5{384jj!+$!|^ zqwW=KXL#g>AmPZx@7P5!J;ILHb8TwX)USGHY_apw+YjTbAZ<4)Kl6%{aYfZifJsb_V6z@U%wFQayKz%${94wOGfSsgj6n&t$ICpF5^LNF&dXtP6c6 z6ZE3I{LDvAT64@XjDpA>TicdN!@MD`ZR|wY`zdY))SV(3zI49jnd9T*%cjFepb&-7 zF_4mL07by|0L4KIpd6e03d!(-4Ul9Cc?@ws0jHmLOaaj0*Poqs_nMB$jclcZa!+8K zx`v9nhE;zj~ zJ+x5VNZKZwPnSlwIVhP=GQ#QRb$xqXB^Y_=-GOz7osE@I?fcP4ar5V0n1V3rR8u2U zi7oYDb0nzNq%6{}kMv-TbAMkyiDis@s_i^KoW71xC@cB&(^svaM^voccjVL0^V;sS z9`+&^(o&FbnQPb!uxR$MfP5iYJlv2vD9X4#L;zz7M%MT7hG_*hSnDB(dUh$9*oDB{ zE83JFG2c0#QLSZ=>VMX7%0%?z4r~+jtvoM3m=!>0p3Vc#R|8z@E*H?hMjYaMHQTyw znsbawhzExS6jymWT7KV+6F1o+Y~3yUlXVgCGrK*Tn<)!7CKG!J0uT?kHVe_O2;ZiN zz1{dTbBbb7sh3fjZ1rkhN%57?Y+v_<#zQcZ~ zYbCh4y2%qdCNOFiy5@8}*I##v?$RWYH+$GIH=Y9?{D!hL*`eV3Im*bg)(pM~-QS!$ zz-^vnI;1WmQ0FXv1Evro@2T3An&F)YFKmj&-y+t+-IPyrR5QZgn7aJug&qweq=~sI zl*Yl=lp0c9)oD>>Z+WvSb>T7wZeQ|Q^ETj~=%03MI=C7I-Ze?sCW_I2YbrTTrg{9P zBGi@Y&bf`gjc&I7L6$5VQqyFG%yY*}v}UkCXTfEJe~@|uxDO=FY?T!>A`$e1tN^ZU zP9;vTE@gQB*>?#dlLb1{{?861nf92dpBpDt*g1i249tbWUXo0;QEGyRZS_mokPBEE zx0f6{CuZ3NPB2;8q75|Yn0i>z4J1)iX&Px77SR+%eU(&16o!wHC~%Z?)k*N8wW6X$ zsmc%l+HeU42B^UXEphAX1vMlv3g1BgW$3C0Eq#DuyeHi*5-6e5Xrqq9I{Gz z({eD!339wGKcN*5L%e3UHp%Up;9C?#OxXv4(=0(8n?oPt1QG@UgxtsWq7hZYoL_0A zaXxEnK;&i=TZEwXW?*d~QPy24KA5Z%k4D+1*wLQ<&n!e+Bt+r80lnblKdcK;WZ2qB z7T9aPalx-hR~1PVp-J;6q9gxcb3`3~un}tK1p!m6b#>)ZXSPzvDB1SmhN?lTNObvR z(J2(gzuGZkFPoyr7+>tz72eTIrM^HtxG(Bd+fD#F5uFHN zas$c6-ylgIVA&ZI85AMqmS#hTr4Nh^2BKa^_3o%+lI| zKdN1Lg(@$0^W(-p5A00q>#~Y?JyG!R0n!|w?^tAI%*g0>I1^HnY?j@A_G%Svi_|6` z5!oSf+OjE8G6Z!bu?P|bOyBHMM(;NO?=94i!g=b*HGkA$QLqbJ5;be>1I3RQ@Ttk^ z%QEUz$L=0TWD;j`>8)y^e6(1oSgJuEvo7*tj@aCkQ9*6t5*n+o3(`T^^Pg^{#ZWDD z{MjqznvE`wiDROEXxO~n?x_54gG;%5xIY{GVo02jYNqgCQ;2{viOGOCiUe; zyeGz7N0c3bTLv7)?9I*jy5=i%{0IUi5X_0^m+zQ`3d7C_iuduX%UPM|9C>?Gpf-t* zrA}4St#KVR&=w*{Z*_#d^?@}qmuiu2ETO)e4DJL9&VnOSTn=V79~a{%or);!80^@I zTC}K}djWiaq^hwf3Ag|@D1vaM9KX@WW8&bbS4`U~)n+bB7?WXGWNd!liAdiFU_>QF zCw*EZ1{RDpT{4)>0@3OV^@n*&%knYAc^KB&00fU?48N470mb9Jsv7A06c2%~pJ+RU zg1KDBzB~zh{ck7bm=Wug>;+a8WgNIThB&o}W6izDk1WSW8MGd4VASJdyA|6-{W`pUi7am6% zWa*GK`2(jSePc%KQ-Zkkb|jzKEBwnCHn;99(~w zG-i`eLNA6qZ7L@!h0r0aHwu=S(D?up1NtoXdy<2#?&V63(mCXaP;^fcWpL;2*Tfkv zDh&ddLtJE9>4#xXmveb>JrrYD37BFc{|UmXcuQmi;Lf^%I6#*6e_B(fX3MerPbB%q ze4cb}(^p+~(FccdfXL?bMX|$$@$>&ZDc}@Lf+2l@BFhCP%H(Mgd^-gr>L(m3*-Q7i z(PFn9fyuh}dp^6BCyZ!f0Lr3V16ZJV8wtb^=&Q)_;g208UuE2#jj#~qW>gf%6gi;g ze0Pcg%^zN()MOE>Tje!Z-J9C9eW!k?Y@Em#6*mS!-u+tU(@=286 zLMVt0{u&TmP0YtCOKamMwg+4^g;mGm$1bivYTd90BF##e?|*W0jX~&_fus0I@1Uv1Xiu6tx_H)#Fv%C| zUiQ(Sm86D}`Q)@5BTyV}C>W{HKu1bb-EK%WRhG7{u0;bhcmKK_4F3c4NVY*3ENiXd01blAlqiseU)wg z0dIItS>X}w-R1Zm$1TlfH81gkd4C-vJh+2AO6iZ{uZ)5+Me&&zU)MedAB+?iRt94O zflELWnL*U;xCD_z?aYB$3+WvBv(#Pb!EM0*@Pp#`Am$WQVi+o@e4VlypMgdIB$*(> z%L|@uU5^ELI*%@c%YeM19v?>bVU>F)PUDq3z}VoE5+;MP&vWktETj8+RueCa2eK-< zkyCeIRB@_Am{ZX*d8Q;!`c*)UB+M_U2H@xA z)#+*QzRXWQx9<{zS%H~sOiynT7x^jj6IPU6R4PG+G1q)M-&MkeQ*yH3Je-Y(g9LH) z+5p0WDL&1XzY*%p^v8!$h1)>xAUiXS+A&(fPgLi~rkd1c_3>WloPgruY45hO!{10D zaKz<-_va4e#()Tkh^)5%{A+O6UYeGkrXW;ob_>PhxfVLat&JPvPt$e0pUb%9LqM0u zulFYn_=$Dz+%V7>TnBMuY%i+HkpG0;$j|m{?>!z9vWf{RHp?z^d_MR$LAu_!4Idt7 z;Vbn6@hu`nr!}lUAMIbOoH%hGmNOoG;;#A5;+{zFiQFM;kUw{(sggGJa^W^Ux;5Q2 z1SZB2(z(K<;ET{Ed)9smRS8uwQkIok7!3SCm1;~|_I(hpJ2s!Lfc6`EoiRJ*Q68-{ z@5MND`H$6O5aq;=|7KMqx@7n|VQkA>8h}K?;S0G~f^uLnR0Wx8Qc}kqCTTId_{N84 zviqwrTQb#~AHyMe;Ad{D%N%Ugks+5OGKfjsfwhs!(Ie41Fu6pzL^wCUn1rZQ=*IAy zeG}89X+LnF^dvS8S4nM^=aT)NzPGtbod=e@4j9&n`=gjrs%+Y9+bjQK95`*%D+m%_ z7FqV&RKybXr;`X*1P|#aG$NO&0fpCeo7K8$4A6|M19uf4Z3G_zdo?;fjN8%JRK)!o z!pjcsa_~Xa6DU6ZrS=w>AL(J(bU2Nh%uRB0zb8R3FBB5_oYz;8owG@|N5-Ea=cMWG zC0SLlq_0o`G=5I01F7-=*s}|2cP*{Xl(hHsdL(E21>R$hru}W#2 zS2e{MOzkg2iD27V^h5C+uJU%`oBt#%O;#KE)THUC5|0`&FOBB6}BsH>J1nnsL-^PD=X2JkIWxkU0p@j(azc_ zo*B|8_K^;COKX@Ki;WchKtjh~9LpseGN0>W(GxCBH$hy5(hW2j_Z+%us7=;$i5^+~ z*x_5FgnU5Yspo7s#<0j%{sKM?DPsx0H$QBFIL{qeF9>pY875g`9vH`>{8_@JkX;XT zwYm(Zo;Uq%mqp6pf3T5Yp{8Q@Nfz1QF9l#2y#ox1nQQ&!`zM zL+hL}UGgetmc$H{_XD*#$+IT#1)6`(1-u{3prN(ps~&k5Nf!vU8b)O9&m@*foK6&p zrZ}h<2zqE7Ih0R2&@4$aa4cC7>%>##f=PB;m&&;BJUAp~&g!Yo>W-rviS$>XUy3z9 z2~18zO0Y(Vd~Yt9w$|pO@(p`{u7-{@TkV%rzg&p7?_Y14ET6EG|MoftKw1T%b54wH zjIAgm=SMcgh~VSs0*91HUYG&ICYK3Bv1SY6mC8lzwBdq(7k@oE$WOnt?-Qdr5#wTd zGKq8!a7I6Dj2-?gir0<5v`~U$#%bjt&6PNfXt1pvXlpRJYB16@5IHXtP*uqs26ZU^||woq-vQ*o1$3 zPw&v)9S?a}GFUPh8j7k^I3{oTq{8oSa%` z>X_Wde^oQZkkXSEy`X|Ji6dDu#f?VTOM;o6Fe0HNlVK+p-3pF7Oo3qTEDZ-_oHOLX z;Emt7oo^C8AK59<6#P?8JU@IJX-i@?K{JVCktZV?D~$Q|yMI?E^v9UDN`SlyPkff2 zjlTTnLsK;AKf+52X81ZOO#i9(7R*!*DRU~$WfE|pDxs*nXx;V;Gkt;@lG+5UHarqA zMR=k#T>-sK3how~N5HYaES{LmF4O59A6L&1&B25KO+UtCnW#i9AZ>d}$%C%jm{FH7 zWB*M|`UsMQXMUL!id95Bq&RhaG<~t9W#oO0OW^|#K8N5J{S`FSCl%u!Y(vF zfP&9MvpurmCgERKw+;5~z{p|7!Nmf9R(apxl;YG8n|6=_HZDR?7&HucFWglA? zAa}sOPCQuNo@htgfRXf1@7Pw^XL=*cVH+S33Jdmu7ax{IGGRvHBSK^$%A!iEOC=Hz z)^*iwlZ~U!+~@m8a?7Jm-3`9Q$iGy;KyAx0BoC?xs)*{>3_|4KwW5PX8t-y^Bv&bW zT(aS;Xfdr4hudZ&qgsyDk2F6Dus7+iH+5g1ivB~m1P4=%wq>kkjAi$)*}>3*18%?6 zV1{#r+|c}p>^faSH6Xw&DO`EftLAhbCC%k-L(kaqN10zP*qheZ>w5o_PHTmKdBS42 zm0nU^1j#(fJj4c`F*#$N#jrSl_q|)EyRnO~bJiG2Fw5U@kF>=s6(Mx|Dh>DMyGw@Qcwe!;X>R3a5;OLLTEZhT%9Ab`dVyVbCbO6MU-#LDgq5zb%4HZV^Bks zF3CG_(dY1o*ZNcNV#7z`le4LrgU05XF4eEP9{d7xCmmCW37| zMil!E-nO5kKcL*=q2DNEaL`^x+D=Q!uXm0j>bVg)a-D3^i{G0@;+A7hQHUCpBgnqW zf-5sjGmT}JQ4VT1A(aoantm5|WM{$LbVzgu1?$|udqA2oh3wwN3Dr*%(|H>$F9pmO zE!?un;%;pChoCHmf7HWj|3#8Rk`wp|LqV%IF+o&!1t%9JhkXN!(aXvgBRpn`Q@V;o z+E1b$FmL8nYPv~KQT&I8+;(FmQ#03u(uUF+vGj@iqmSmAe;I%uffGh7;KdIl$ybY_ zMYub}>PiUMSZ5vj!5P{D+^Nt21@Ol{6Lw_lAuTuLobUkTq^oat-esN}21i9>q?r%nf`9=9Tp1 z(0LYV{l5@^x6?CdODA0=S%p~Gqc;_=CjP-XH**{riDN8@7m4p})rsE<)e_{EW7UhV zC3%i)p;=(n5MNA83ZxRUxkVKrwRSnlGxp-E(*LWK-KjI=f*-i@zck5$xzz#p&C$;- zzzuyjfipTZu2;GX#qM40a|ku~i~hN9Tv-vy0;_|B+-v%h4~OUZhw-Y*J4nAM|J7y^ zHBU`W0f9-;MrhUE-))+=btCtW4FZd?fdYsRe7;9m=jW zta>fO^o`w(C0$-OqcW6hb_-FW)q;3+g7*R{0rgpIaq>W=`Nd6D_ODy~K;Ej6I zgsBUhO`3kDTCSq34wmjX^gs?Qq~VE8);9M;2-RRinTbId2~E1(-Ds zj5W^n1O^4}y;My}2PU7rdGTMzNN_T38AbK?NOSrP21}{HX7mMFMp%FUIIQAl)@?BLJ`M;5~ZwCT9vW7(R^Qp`X`xe-IqX?+{8l*$dkY&S$Z^S`hQky5)ZrjYW&5g zU@e*6X(!lK?;<{cx73Mz)N7MO2J~JC)+p%Ex zx9bnr(EHK>HtO?!-i!hb)ORY;NUkXn%o5D!c$AIF{$xX6X1;!5F75jyRak<&0(!Qp z@^2rEUDnv@3F&c1Rt9l(TBq|}Qqw}T|E;m_x>m|B>=Aop4 zx1yNIW#uq-C356N-|EBI@@zib;fW{S=7br29X?l~o&D7dSI2-BIB^VY^vVJ^r9UUW z=fRYu_iI}ty0*3i`&fx;!ZhCbXs3%x4v9KK!=^ zzR!lIF#j3t!c0vt9RAJXqkuEq#twy2WHt3u1SC_ia&{_ZGI&h9D)8F*8qKKmO3g08U$yD^TM;Lnq zIQT#~+-{3FlzS{!Xb^_L6VHk^`0O8x5-t-@iZ;~aZ7Ik<3Lef0BUWHqlDKyY#SrS3 z{eBH6`L88W-l~2}G=B?MMn|yB#SEsQ$9F0kf}%~54UY2Q5Joi6)rYghSxR9n2b;nJ`#Rx3d6dQp#1sZL5rZ2{c3Wt_DHQ`H8Y0@LMybGu zezGs-w;dQ{3W(AHW1eLqc(cWI)LJAh%{t*$N`4scybpV(@i;I~v{0mQAN6N-%-%EpTvs^D-YnwsDQbb|yyl@vUJKv-5gE{pw zR^f--FMDm5f35tJZ=+T7|E?DOBm7f?_Lm$83yBm;uk<1d#+j@Pd)h(^`-h>4RJ$66 zOlZ2yqK}`cZEG0Hy-e9SiY@t!cB;`e%&8-k(T_K&^S*O-uZxc&u^=`BunoZ$TBCP@ ziHhK}YjkiUW=d%jaYA2MXdIsHLq?06*feVz@F5qiAN2>DhjY}~K_ovTi1|bntg@ln0(IGx$SvD% zioA#li7f9GmmlAw;NdVU-;&vA=Mqf=EE_7D3T6fd)j|Yvy0uBaTNZ7ouws zP&fN%ZX5rLFmKvKRJ);Y4Vl$6zhqWm<5=0Ah8wwV4Miqc6L zoLH)u_{wh>m`OUTG4U)Ms5z;+w&(1w@8P1@eIksfLqFwP?F-7pW1%7g!%0T1&X;TI z@L&TjeE|}?Q%g)geAwjEcxqZ6AZ>>al)cX-0snng>t%PrwH`vHutF41smxe*zw^Fk4cp-+c6Y6%5C$7@`i$yYtmb zvY@aA%gg7;W*}7F9~~LIrzw;l=lQN zY=1%fZ-T%}4yv5{lOM9PBw2fcvP(bM;mIMAYFGuP9%e%c1iok5{4a&HneP>4VRm*n z!#8#FZ}FXJ)C=Tjh9RG^4hd&j{iMx2lNJ7st9*T?ZuR9)MKsMYA%6}9yr0Sub#9;R z9TqT_*135b3CUa+5``9oGc5j0-Hd*zqC)Dz@44J>I6L^nH`<=|PoQbkMOVH%4*%V8 zg|PL{;^tU5qUM3AguZy!sJ4hR6RF9W!+Y{#Jxmu%{(p>5J!##stYRs{dJgzraI+jid5U(ZDma1fwMqwO}rEaEHYY{u9t& z@+HMpX!G(mBSj*o5@e0fzntP_{fTREjvEhHu|KYTx1HkGPx=3q8%Pc&W}^UuZWs=~ ztbR$hJF3w%-WL<{uG)Hplzgas5w;C7K;DUkK#yu+8p`4B%pet{Yj;nTH!t(j-xeZ9 zl_dLwFs*#d37f!w1O^%Foi!RHY*xc?nxjS}phc+Us%v~$NRVJji6X8hjT4qH0#%(L zYYh5M*7aRQu>K`rde*x*V?E(Nj}$gq1Bi>SWQBpCn2cnyrJq#bArb*X5X{;a8`44= zoI>BvEgs4Xp!91h!behj$%z#r^o9_LCm@)5cSVV+6GII_=|Ax|6X@o=A;jci z$^XHIHqhT z8{Llak`OoHN$$`Dw-OGd)hJ0re5@iPraNnpy)4Rw^H=(*t6m&)hwlP*xfH? zGTee{SDgPQ?HH<%y!|F4^b|?#SZPz&t%R6r1r*CDV!zGU(k40OgUv=2zX_+)}+xhOZz}6m+pl7isxHwPapz zIjfLFIKuUb1Wlca5-yvp6x2xbfaf!VsZbaQ7v!)Lamyvd<90|Ze}fCX_IxI9ZyUPI z2U_%aG*S#P3iTit9||G{ZFxJ8r+Z(}cN>kpCu96kU7N_obWJ|s zm+8QwQ5}n&XU-yuSOha2hQj5_h@Ygv#imIeJ<^Ajt^l)neI{j$6aHq1%y*=`w(Y}u zdbgX?&lPPPdT?$G+rJAO3G&o?xjzZW08&a~3dNbPyo#Rl2f3LH*Zyz2-Ur=JL_+R7 zGSen3FXa#9xIpu?Q~$?(l+|kkgCFkTs^Umb1I4D4|S3+b{8HX|4C8!Gj0qM_bM(!&XBiuZv1^z~oVK zgR3hp`7fc?=P6@dUCo(U>i0XhGWjm*2Fj)w(am>l7dr|KnO^uEbR6lkx=c@^b*tlP zqIaLtZ(Gig$6BDdp4DGxnjjg=#C=?xuDJzH-d|W4ZzkSa&3Y2_+$+H{t*iZH>PYv4 zd_06g@%_fJ*Ge%zHX1|!xaDrpFW~&Nz2)w7-6dp1m2@NfQg74+&x`+4g<*gN=?nXJ zbvCJhHKOunshkaGq^HA%j@7D{tB^_VrXwQii<6Q$Q{%2K9h)R$r+KlZshbHBePsQL zfs2i&`HH^U7$4qW#ZC230;i`Z4S82D+8X2KuGv#O-L07vFQ*$qTj6f@o*Vnm(yVyJs1$ zRBq!<;2ZST>ive8*GsMFV=;h!uj2kPb?3MA7;el?hM-?XL?1@5cT@ zY&NUMcEp}xuwXU7KP~#P?Mo6?NqVMkbiCcxbhxIF>A9MGX2hf3tc365%nG02G2Eq8 z38|$GBbF2bu}}U~&B3t#NI!f@pJCzh*>+X=_WYUl$s!6q8>%>-%p&7N!cm5%IGYbw!`m`1< z(i8o)+OJOC#v9mlb7xcmN2??jnvp(99!?!z_Lm2r&uG9`7ZA+u#&eX2b=Kte=7xAw zSGrd{jU^n>?u?Rp6>pUUTWwihv}Nsw4GQ<0hsJ9xvd)l0a)672?bFvAB_F25dAm!e zrc0~EVPc0j&&$K5;noBLo1Lnk;XIrB$IsX2=lXa1(=9mxntZUEim3R9Rnm`VYs3V# z?v+WpenD_QPTEG|c2+O?CI@=L2cnF43Cwq*4@-YF=W^*+VkyzjRS501F_aq)>lS-_ zq)TGy`-4CBhSR^brLGCgUnK1@_VmRrWp39fP=zAuXphmbdJ69ir#0`@H9q zUck@%kBQy(7KJJ;0wx@ezDg5@tdP3sIWjbx%D84LGS{QlIeaA@P1AR4y4yZJUq9cE zt{=s!C=v5XX2F*{@m}_vPjb1Q1$%Nv;F)C(QP<^Mss*%)T$bc+@5U7Kw^ujYD1}NY zpRF=!p9U_i)!r|u(eKTLshLl6hu2%BOMpk!n4fZ6S@rNtk+pUp^b<*B*Hy>(N z3nemsy~m43%FeJI&R+c|p)Dt#HmSN1KD^|Hn1((1eeFWFY%~1vz*lPpr(5EDR~e5a zJw-x}_8W9<>`n?EbHosL+Nb@#s0smQQP zLzXstQ;|2|@yoHh6aCe_sxX&SR?IM{pwjlbENZhO1sT(6z+4=`fU1PASXQhYXwhRVgIK6)Bd_sNyrkgx_|`4RoFUYFx;g2bGZuPw(bcxc>2j@JHtO*i@Yd;*%+dLHex) zG2V^lr1@3gJ}2V)3tj$5eOIGUaj zrKZhQirR*#-MV-{^3=RUNY8+h6Nr#s+)9uJ=b@Y;DCouBu=lBQoji2&Del~~+zrX$ zY&cHev?Iw!9*{ai8jn^|L|%%N55LhWJl$i+s}p3dDC^yvSr|IFkfE24xsucIDQE2H zmRq@8O>!RbJhgq0(0OWfJ=YDdxoo{>*0|MiHIf2$x}y|c;mpd|zH`G{sj)cJ8x$Wn zN{9biHNcuz%FQdb`|Yy&V+15hOdrZVy(RHcf{e%V#;-SL5B%Fm!=>O_$I~u=ZP0iW znU9Uo@$mNlzf-xA-9&60@5@UC5R4$UK54>?aUh$=jlObkqd`dff+)tcl7y^x#pJnE zRvlw*yAY5M0?{TVt{!HdTGkKdzs9bw*@r$?Nw1vRMLqQt9EI3vb~Jh1tZ8aSzr9{O zx<9Q@Zt5`{jGNF#L(XTwQpHv!wug(ZIx{ zKQjwSsB1cM== zS7hX;G-Uj6?i1Bfb87eidTG{Hutl;x?%(j7dLne-M~kcc-^>%~V3#n|=T(zcsj^e9 zd*=E^PRI*pna;PAXmG8t8h5qQZOi%YWRv%NmWbkM9^0El*QStw#eo|Dt7(Vo}W11!6*hEd4#u9@#m!Se(>A*?>IRa|jNM|wvzs??n?t};6 z7P6@xV2wTS*|P%kC%>he%?HG4{z?QSdCrL}-z^fz4T&NcU?((-{t-^0&nbZ?`)CUDs5`!WFF}k86ah*TW3^O~!&MUI0v|oZcSL{Q7yZ3C_~+uN zkdZ#A1t`vM*>`i_^*O75yL{RPZ~l!TDH!Y@YfFkAftqjqfqR`GlI3SCHFV%(Je4yf zzc*$De-H}@0kBZ(g{##bJIwQJ-;3+>6BkF@0cmv9I9<&C{RNF&H@MKbzEo&FxnNqD zOmG~llhQp6V{U_Di^K!dcmllapXOO)$H9unTCQ?cmAPv7H;U$4k493#1CCmqR}O;5 zXYy38!}Tk(A!p#ejSaqHd%s7W#vH&9Q?Dc=8>Dn+(!^zsyicR+)&%G|Bb>N1PbsspG2{_+AU@Ea*fiQkvg6!Jq+xz9IqvZ_gY= zR~;_}cJlyoLlWrzpmDr}F2CK-=*HH-IM&CA2Mw(4AS~7{zdh2|EwBq>pD0%OX&BF& z->np^SCH#n)2Y&WQh8UUl7ev&^3Wb1EDaDeW7pdnKO~=gFT~xDDyPB!CDOG4L=AJn(qTl+YLmnl z(hpt-tIMgZl%AwmxuW?UmXeDY@m*miCHN-D+Q@gm2gF-_aMKH0SE1eU^O>Rx%4bGy z|C2h%+pGBz$;Cq!s7(>NbOd@FD)8NgdEQTI+$rA*a3efC_roKtPi`zbHRA!@@VVsIwcRK$hmHHS8I0o^f|1752jQHU; zsu~Ju6&GH4dU(%XU=gZpu-+Lo^P@QsYI;s;R)`JR%y6M&-YA9|SNoXfXylmnE6AuL zwnO!Ov5tO~Q5C0-S$9%EJBi~c84RYKda+m+@Y_fRCZO?iSs;GqF$si{M=+_&T)aG? z8pl3ew`u*sVg?F^icxg;uoGwWkZ5tUqsmgbIst^hFdjOeTT7rywuB6H6AfI=nykNB zWQlCW$HAVekEUo!l0=hvxk+$F(v`)e><+n#jvpu4BXwxyX+V>He~-j4qlMdolbKPV z1pG5l4(J9Hpd7J7et*!@@JCcYDSbdqK#~kmB!?m*L=}s~BF{}llb?iV$}gh6WL2yA z1f8}CQX!s$A&vB)bcws#{i8y~C4fa<%ZuJZ6pI#d@Q%7hr;}LnfCzbhRwaT}T=+tT zP(3eLz8xGq7d_ss=xC|lX-MlP>Q@r%0H7Me1xvq|fQU-JIOER;9@a=_svpbaY(E== z#Phs+s1V*ad7q{7PB(`xksN#{im2D2(rws#HAp-8vB3L4h#Tn;8FHKJNnzQ>HW1J-5M3v^HJqeb1$380A-=B1J(7jIk}geBVaVT$tV7VZtf zrKLuC6zNu9zG|53}3giYH*loP@4(iBO@T=P!{C1a`)}xL0=2sakXgPwt z+rOX`IEpuzcoV5KQo$U^YaYgtA9jbKv`%WCX!-4^*NcK^elu zRoIXO*bi4Xq#FOiW_K8EOiw}h-<8_mXRg+V3mWrTv(HC#uSZj zvmY(?Io`-E7Xz1FW+v|__z9csG@T2{tMp2%#iR>;v$g0-})lewcb zBHTpJ-5jSd5$QUB!IQS0t+xBFvKG%PYgPeW^8~xr;jKW^-PL79l$-7673y9(wkOTa zN{|$1=3=mv$!f&KWFRYl^IGkSNeF~8p|s;ToT6a0km5R&gL zcoOGW<*G0j^2m-RpGtTNvcI<3HCXGp!oeJAIyl?rkjcz1a0xLZ8D$j5SjG|SVD|bt z#O}GgWY#7HaZvz~^W0dM=op(tr^yRX_GplL(OP5QQN@=@yip9rqq}REGXzPB$APEG z$sD)}Ck=KsXlo*WtI+jyo03o&l^_lFP_miwC@rV;^6B<8tetr@~A!NsDS2b(Tt|K3mfV@KS8ywEsD9}mwbN#e z=j{b)Q!^7f{{!(s#iH4u$cgnb!tCRA`=aImw~APz;O8~}d4G2}f@HZ8R(H`$)LrVB zzsMtMJeir{EZNkwa1$aurZ^!^QF4`}7Po=nf${~hlT9rP&q5T>T-!LJT}-8rUNX++ zXgbz1Ihg~0O3Scdj$tpZI2zF@`R^F6RfaV97IkaJXLvou_b&4&y$G7{crr zel3HKU^(@maB4lef&$jGjz^eTHgHnsZ`ThyonD!L?VXc3sy!9|-{g{-e4nk;Y&*EK zwglgw{?L)s)a(r`!KLl!#_Qy{xy6+6pxIHEpQ*_aNtcYCX(6M|8Vg(pIDrDscpd#k zCo*+!VbuR)tWq6+t8LWbWDI-f0EbK}o_xRD<9}B_{dBeabbEDpe|3BMYkjZ&4^T@3 z1QYA_0IrjZS1cRdp}8Mp4FCYSZvX%n0000000001000000ArKV pS40B)N0SOj8j~_uDgjiJU062(hLe?8GyzbP%2*l(e^&qi002pd7%cz* delta 18275 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 E00vGgIRF3v diff --git a/theater/start_generator.py b/theater/start_generator.py index 824650c4..5834a986 100644 --- a/theater/start_generator.py +++ b/theater/start_generator.py @@ -7,7 +7,7 @@ import logging from game.data.building_data import DEFAULT_AVAILABLE_BUILDINGS from gen import namegen from gen.defenses.armor_group_generator import generate_armor_group -from gen.fleet.ship_group_generator import generate_carrier_group, generate_lha_group +from gen.fleet.ship_group_generator import generate_carrier_group, generate_lha_group, generate_ship_group from gen.sam.sam_group_generator import generate_anti_air_group, generate_shorad_group from theater import ControlPointType from theater.base import * @@ -81,6 +81,7 @@ def generate_groundobjects(theater: ConflictTheater, game): g.cp_id = cp.id g.airbase_group = True g.dcs_identifier = "CARRIER" + g.sea_object = True g.obj_name = namegen.random_objective_name() g.heading = 0 g.position = Point(cp.position.x, cp.position.y) @@ -101,6 +102,7 @@ def generate_groundobjects(theater: ConflictTheater, game): g.cp_id = cp.id g.airbase_group = True g.dcs_identifier = "LHA" + g.sea_object = True g.obj_name = namegen.random_objective_name() g.heading = 0 g.position = Point(cp.position.x, cp.position.y) @@ -114,7 +116,7 @@ def generate_groundobjects(theater: ConflictTheater, game): cp.name = random.choice(db.FACTIONS[faction]["lhanames"]) else: - for i in range(random.randint(2,6)): + for i in range(random.randint(3,6)): print("GENERATE BASE DEFENSE") point = find_location(True, cp.position, theater, 1000, 2800, [], True) @@ -132,6 +134,7 @@ def generate_groundobjects(theater: ConflictTheater, game): g.cp_id = cp.id g.airbase_group = True g.dcs_identifier = "AA" + g.sea_object = False g.obj_name = namegen.random_objective_name() g.heading = 0 g.position = Point(point.x, point.y) @@ -144,6 +147,35 @@ def generate_groundobjects(theater: ConflictTheater, game): for ground_object in cp.ground_objects: print(ground_object.groups) + for i in range(random.randint(2, 3)): + + print("GENERATE SHIPS") + point = find_location(False, cp.position, theater, 5000, 40000, [], False) + print(point) + + if point is None: + print("Couldn't find point for {} ships".format(cp)) + continue + + group_id = group_id + 1 + + g = TheaterGroundObject("aa") + g.group_id = group_id + g.object_id = 0 + g.cp_id = cp.id + g.airbase_group = False + g.dcs_identifier = "AA" + g.sea_object = True + g.obj_name = namegen.random_objective_name() + g.heading = 0 + g.position = Point(point.x, point.y) + + group = generate_ship_group(game, g, faction) + g.groups = [] + if group is not None: + g.groups.append(group) + cp.ground_objects.append(g) + def generate_airbase_defense_group(airbase_defense_group_id, ground_obj:TheaterGroundObject, faction, game, cp): @@ -286,6 +318,7 @@ def generate_cp_ground_points(cp: ControlPoint, theater, game, group_id, templat g.dcs_identifier = object["type"] g.heading = object["heading"] + g.sea_object = False g.position = Point(point.x + object["offset"].x, point.y + object["offset"].y) if g.dcs_identifier == "AA": diff --git a/theater/theatergroundobject.py b/theater/theatergroundobject.py index 51c54e2c..f7441663 100644 --- a/theater/theatergroundobject.py +++ b/theater/theatergroundobject.py @@ -69,6 +69,7 @@ class TheaterGroundObject: position = None # type: Point groups = [] obj_name = "" + sea_object = False def __init__(self, category: str): self.category = category diff --git a/theater/thechannel.py b/theater/thechannel.py index ce44d95f..97132d20 100644 --- a/theater/thechannel.py +++ b/theater/thechannel.py @@ -21,7 +21,54 @@ class ChannelTheater(ConflictTheater): super(ChannelTheater, self).__init__() self.abeville = ControlPoint.from_airport(thechannel.Abbeville_Drucat, LAND, SIZE_SMALL, IMPORTANCE_LOW) - self.detling = ControlPoint.from_airport(thechannel.Detling, LAND, SIZE_SMALL, IMPORTANCE_LOW) + #self.detling = ControlPoint.from_airport(thechannel.Detling, LAND, SIZE_SMALL, IMPORTANCE_LOW) + + self.stomer = ControlPoint.from_airport(thechannel.Saint_Omer_Longuenesse, LAND, SIZE_SMALL, IMPORTANCE_LOW) + self.dunkirk = ControlPoint.from_airport(thechannel.Dunkirk_Mardyck, LAND, SIZE_SMALL, IMPORTANCE_LOW) + self.hawkinge = ControlPoint.from_airport(thechannel.Hawkinge, LAND, SIZE_SMALL, IMPORTANCE_LOW) + #self.highhalden = ControlPoint.from_airport(thechannel.High_Halden, LAND, SIZE_SMALL, IMPORTANCE_LOW) + self.lympne = ControlPoint.from_airport(thechannel.Lympne, LAND, SIZE_SMALL, IMPORTANCE_LOW) + self.manston = ControlPoint.from_airport(thechannel.Manston, LAND, SIZE_SMALL, IMPORTANCE_LOW) + self.merville = ControlPoint.from_airport(thechannel.Merville_Calonne, LAND, SIZE_SMALL, IMPORTANCE_LOW) + + + # England + self.add_controlpoint(self.hawkinge, connected_to=[self.lympne, self.manston]) + self.add_controlpoint(self.lympne, connected_to=[self.hawkinge]) + self.add_controlpoint(self.manston, connected_to=[self.hawkinge]) + + # France + self.add_controlpoint(self.dunkirk, connected_to=[self.stomer]) + self.add_controlpoint(self.stomer, connected_to=[self.dunkirk, self.merville, self.abeville]) + self.add_controlpoint(self.merville, connected_to=[self.stomer]) + self.add_controlpoint(self.abeville, connected_to=[self.stomer]) + + #self.detling.captured = True + self.hawkinge.captured = True + self.dunkirk.captured = True + #self.highhalden.captured = True + self.lympne.captured = True + self.manston.captured = True + + +class ChannelTheaterComplete(ConflictTheater): + terrain = dcs.terrain.TheChannel() + overview_image = "thechannel.gif" + reference_points = {(thechannel.Abbeville_Drucat.position.x, thechannel.Abbeville_Drucat.position.y): (2400, 4100), + (thechannel.Detling.position.x, thechannel.Detling.position.y): (1100, 2000)} + landmap = load_landmap("resources\\channellandmap.p") + daytime_map = { + "dawn": (6, 8), + "day": (10, 17), + "dusk": (17, 18), + "night": (0, 5), + } + + def __init__(self): + super(ChannelTheaterComplete, self).__init__() + + self.abeville = ControlPoint.from_airport(thechannel.Abbeville_Drucat, LAND, SIZE_SMALL, IMPORTANCE_LOW) + #self.detling = ControlPoint.from_airport(thechannel.Detling, LAND, SIZE_SMALL, IMPORTANCE_LOW) self.stomer = ControlPoint.from_airport(thechannel.Saint_Omer_Longuenesse, LAND, SIZE_SMALL, IMPORTANCE_LOW) self.dunkirk = ControlPoint.from_airport(thechannel.Dunkirk_Mardyck, LAND, SIZE_SMALL, IMPORTANCE_LOW) @@ -31,13 +78,11 @@ class ChannelTheater(ConflictTheater): self.manston = ControlPoint.from_airport(thechannel.Manston, LAND, SIZE_SMALL, IMPORTANCE_LOW) self.merville = ControlPoint.from_airport(thechannel.Merville_Calonne, LAND, SIZE_SMALL, IMPORTANCE_LOW) - # England - self.add_controlpoint(self.detling, connected_to=[self.highhalden]) self.add_controlpoint(self.hawkinge, connected_to=[self.lympne, self.manston]) - self.add_controlpoint(self.highhalden, connected_to=[self.detling, self.lympne]) - self.add_controlpoint(self.lympne, connected_to=[self.highhalden, self.hawkinge]) + self.add_controlpoint(self.lympne, connected_to=[self.hawkinge, self.highhalden]) self.add_controlpoint(self.manston, connected_to=[self.hawkinge]) + self.add_controlpoint(self.highhalden, connected_to=[self.lympne]) # France self.add_controlpoint(self.dunkirk, connected_to=[self.stomer]) @@ -45,8 +90,9 @@ class ChannelTheater(ConflictTheater): self.add_controlpoint(self.merville, connected_to=[self.stomer]) self.add_controlpoint(self.abeville, connected_to=[self.stomer]) - self.detling.captured = True + #self.detling.captured = True self.hawkinge.captured = True + #self.dunkirk.captured = True self.highhalden.captured = True self.lympne.captured = True self.manston.captured = True \ No newline at end of file