diff --git a/game/data/building_data.py b/game/data/building_data.py index ab2555c3..ae4e2236 100644 --- a/game/data/building_data.py +++ b/game/data/building_data.py @@ -3,9 +3,9 @@ import dcs DEFAULT_AVAILABLE_BUILDINGS = ['fuel', 'ammo', 'comms', 'oil', 'ware', 'farp', 'fob', 'power', 'factory', 'derrick'] -WW2_FREE = ['fuel', 'factory', 'ware'] -WW2_GERMANY_BUILDINGS = ['fuel', 'factory', 'ww2bunker', 'ww2bunker', 'ww2bunker', 'allycamp', 'allycamp'] -WW2_ALLIES_BUILDINGS = ['fuel', 'factory', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'allycamp'] +WW2_FREE = ['fuel', 'factory', 'ware', 'fob'] +WW2_GERMANY_BUILDINGS = ['fuel', 'factory', 'ww2bunker', 'ww2bunker', 'ww2bunker', 'allycamp', 'allycamp', 'fob'] +WW2_ALLIES_BUILDINGS = ['fuel', 'factory', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'allycamp', 'fob'] FORTIFICATION_BUILDINGS = ['Siegfried Line', 'Concertina wire', 'Concertina Wire', 'Czech hedgehogs 1', 'Czech hedgehogs 2', 'Dragonteeth 1', 'Dragonteeth 2', 'Dragonteeth 3', 'Dragonteeth 4', 'Dragonteeth 5', diff --git a/game/db.py b/game/db.py index 960abd06..981a0de0 100644 --- a/game/db.py +++ b/game/db.py @@ -664,15 +664,14 @@ UNIT_BY_TASK = { Tu_95MS, UH_1H, WingLoong_I, + Hercules ], Transport: [ IL_76MD, An_26B, An_30M, Yak_40, - - C_130, - Hercules, + C_130 ], Refueling: [ IL_78M, diff --git a/game/theater/landmap.py b/game/theater/landmap.py index 0f7395c8..6e510087 100644 --- a/game/theater/landmap.py +++ b/game/theater/landmap.py @@ -2,8 +2,10 @@ import pickle from typing import Collection, Optional, Tuple import logging +from shapely import geometry + Zone = Collection[Tuple[float, float]] -Landmap = Tuple[Collection[Zone], Collection[Zone], Collection[Zone]] +Landmap = Tuple[Collection[geometry.Polygon], Collection[geometry.Polygon], Collection[geometry.Polygon]] def load_landmap(filename: str) -> Optional[Landmap]: @@ -15,22 +17,9 @@ def load_landmap(filename: str) -> Optional[Landmap]: return None -def poly_contains(x, y, poly): - n = len(poly) - inside = False - xints = 0.0 - p1x, p1y = poly[0] - for i in range(n+1): - p2x, p2y = poly[i % n] - if y > min(p1y, p2y): - if y <= max(p1y, p2y): - if x <= max(p1x, p2x): - if p1y != p2y: - xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x - if p1x == p2x or x <= xints: - inside = not inside - p1x, p1y = p2x, p2y - return inside +def poly_contains(x, y, poly:geometry.Polygon): + return poly.contains(geometry.Point(x, y)) + def poly_centroid(poly) -> Tuple[float, float]: x_list = [vertex[0] for vertex in poly] diff --git a/game/utils.py b/game/utils.py index 0355fbb2..b570e355 100644 --- a/game/utils.py +++ b/game/utils.py @@ -37,6 +37,7 @@ def knots_to_kph(value_in_knots: float) -> int: """ return int(value_in_knots * 1.852) + def mps_to_knots(value_in_mps: float) -> int: """Converts Meters Per Second To Knots @@ -44,6 +45,23 @@ def mps_to_knots(value_in_mps: float) -> int: """ return int(value_in_mps * 1.943) + +def mps_to_kph(speed: float) -> int: + """Converts meters per second to kilometers per hour. + + :arg speed Speed in m/s. + """ + return int(speed * 3.6) + + +def kph_to_mps(speed: float) -> int: + """Converts kilometers per hour to meters per second. + + :arg speed Speed in KPH. + """ + return int(speed / 3.6) + + def heading_sum(h, a) -> int: h += a if h > 360: diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index 5c3b7aa0..ea18eb46 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -34,6 +34,7 @@ from game.theater.theatergroundobject import ( LhaGroundObject, ShipGroundObject, ) from game.unitmap import UnitMap +from game.utils import knots_to_kph, kph_to_mps, mps_to_kph from .radios import RadioFrequency, RadioRegistry from .runways import RunwayData from .tacan import TacanBand, TacanChannel, TacanRegistry @@ -204,10 +205,11 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): tacan_callsign = self.tacan_callsign() icls = next(self.icls_alloc) - if self.control_point.target_position is not None: - brc = self.steam_to_target_position(ship_group) - else: - brc = self.steam_into_wind(ship_group) + # Always steam into the wind, even if the carrier is being moved. + # There are multiple unsimulated hours between turns, so we can + # count those as the time the carrier uses to move and the mission + # time as the recovery window. + brc = self.steam_into_wind(ship_group) self.activate_beacons(ship_group, tacan, tacan_callsign, icls) self.add_runway_data(brc or 0, atc, tacan, tacan_callsign, icls) self._register_unit_group(group, ship_group) @@ -242,19 +244,19 @@ class GenericCarrierGenerator(GenericGroundObjectGenerator): return ship def steam_into_wind(self, group: ShipGroup) -> Optional[int]: - brc = self.m.weather.wind_at_ground.direction + 180 + wind = self.game.conditions.weather.wind.at_0m + brc = wind.direction + 180 + # Aim for 25kts over the deck. + carrier_speed = knots_to_kph(25) - mps_to_kph(wind.speed) for attempt in range(5): point = group.points[0].position.point_from_heading( brc, 100000 - attempt * 20000) if self.game.theater.is_in_sea(point): - group.add_waypoint(point) + group.points[0].speed = kph_to_mps(carrier_speed) + group.add_waypoint(point, carrier_speed) return brc return None - def steam_to_target_position(self, group: ShipGroup) -> Optional[int]: - group.add_waypoint(self.control_point.target_position) - return group.position.heading_between_point(self.control_point.target_position) - def tacan_callsign(self) -> str: raise NotImplementedError diff --git a/mypy.ini b/mypy.ini index 045a50e6..e397c985 100644 --- a/mypy.ini +++ b/mypy.ini @@ -9,4 +9,7 @@ ignore_missing_imports = True ignore_missing_imports = True [mypy-winreg.*] +ignore_missing_imports = True + +[mypy-shapely.*] ignore_missing_imports = True \ No newline at end of file diff --git a/pydcs b/pydcs index c149afae..f924289c 160000 --- a/pydcs +++ b/pydcs @@ -1 +1 @@ -Subproject commit c149afae71dfb534c6f180e2dde9fdc20b4d7d1b +Subproject commit f924289c9cbe6e21a01906bdf11c1933110a32de diff --git a/qt_ui/displayoptions.py b/qt_ui/displayoptions.py index 28a9ae4d..55dcb10b 100644 --- a/qt_ui/displayoptions.py +++ b/qt_ui/displayoptions.py @@ -20,8 +20,9 @@ class DisplayRule: def value(self, value: bool) -> None: from qt_ui.widgets.map.QLiberationMap import QLiberationMap self._value = value - QLiberationMap.instance.reload_scene() - QLiberationMap.instance.update() + if QLiberationMap.instance is not None: + QLiberationMap.instance.reload_scene() + QLiberationMap.instance.update() def __bool__(self) -> bool: return self.value diff --git a/qt_ui/widgets/map/QLiberationMap.py b/qt_ui/widgets/map/QLiberationMap.py index 58c7ad19..8b49077c 100644 --- a/qt_ui/widgets/map/QLiberationMap.py +++ b/qt_ui/widgets/map/QLiberationMap.py @@ -270,8 +270,8 @@ class QLiberationMap(QGraphicsView): culling_distance_point = Point(point.x + culling_distance*1000, point.y + culling_distance*1000) distance_point = self._transform_point(culling_distance_point) transformed = self._transform_point(point) - diameter = distance_point[0] - transformed[0] - scene.addEllipse(transformed[0]-diameter/2, transformed[1]-diameter/2, diameter, diameter, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"]) + radius = distance_point[0] - transformed[0] + scene.addEllipse(transformed[0]-radius, transformed[1]-radius, 2*radius, 2*radius, CONST.COLORS["transparent"], CONST.COLORS["light_green_transparent"]) @staticmethod def should_display_ground_objects_at(cp: ControlPoint) -> bool: @@ -744,7 +744,7 @@ class QLiberationMap(QGraphicsView): for sea_zone in self.game.theater.landmap[2]: print(sea_zone) - poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in sea_zone]) + poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in sea_zone.exterior.coords]) if self.reference_point_setup_mode: color = "sea_blue_transparent" else: @@ -752,14 +752,14 @@ class QLiberationMap(QGraphicsView): scene.addPolygon(poly, CONST.COLORS[color], CONST.COLORS[color]) for inclusion_zone in self.game.theater.landmap[0]: - poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in inclusion_zone]) + poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in inclusion_zone.exterior.coords]) if self.reference_point_setup_mode: scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_grey_transparent"]) else: scene.addPolygon(poly, CONST.COLORS["grey"], CONST.COLORS["dark_grey"]) for exclusion_zone in self.game.theater.landmap[1]: - poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in exclusion_zone]) + poly = QPolygonF([QPointF(*self._transform_point(Point(point[0], point[1]))) for point in exclusion_zone.exterior.coords]) if self.reference_point_setup_mode: scene.addPolygon(poly, CONST.COLORS["grey_transparent"], CONST.COLORS["dark_dark_grey_transparent"]) else: diff --git a/qt_ui/windows/settings/QSettingsWindow.py b/qt_ui/windows/settings/QSettingsWindow.py index 2ec2566b..fa0d49b4 100644 --- a/qt_ui/windows/settings/QSettingsWindow.py +++ b/qt_ui/windows/settings/QSettingsWindow.py @@ -363,7 +363,7 @@ class QSettingsWindow(QDialog): self.culling.toggled.connect(self.applySettings) self.culling_distance = QSpinBox() - self.culling_distance.setMinimum(50) + self.culling_distance.setMinimum(10) self.culling_distance.setMaximum(10000) self.culling_distance.setValue(self.game.settings.perf_culling_distance) self.culling_distance.valueChanged.connect(self.applySettings) diff --git a/requirements.txt b/requirements.txt index 41233a08..1bedb462 100644 --- a/requirements.txt +++ b/requirements.txt @@ -8,3 +8,4 @@ tabulate~=0.8.7 mypy==0.782 mypy-extensions==0.4.3 jinja2>=2.11.2 +shapely==1.7.1 diff --git a/resources/campaigns/invasion_from_turkey.json b/resources/campaigns/invasion_from_turkey.json index 41cd39a2..f259a1e4 100644 --- a/resources/campaigns/invasion_from_turkey.json +++ b/resources/campaigns/invasion_from_turkey.json @@ -3,85 +3,5 @@ "theater": "Syria", "authors": "Khopa", "description": "

In this scenario, you start from Turkey and have to invade territories in northern Syria.

", - "player_points": [ - { - "type": "airbase", - "id": "Incirlik", - "size": 1000, - "importance": 1.4 - }, - { - "type": "airbase", - "id": "Hatay", - "size": 1000, - "importance": 1.4 - }, - { - "type": "carrier", - "id": 1001, - "x": 133000, - "y": -54000 - }, - { - "type": "lha", - "id": 1002, - "x": 155000, - "y": -19000 - } - ], - "enemy_points": [ - { - "type": "airbase", - "id": "Minakh", - "size": 1000, - "importance": 1 - }, - { - "type": "airbase", - "id": "Aleppo", - "size": 1000, - "importance": 1.2 - }, - { - "type": "airbase", - "id": "Kuweires", - "size": 1000, - "importance": 1 - }, - { - "type": "airbase", - "id": "Jirah", - "size": 1000, - "importance": 1 - }, - { - "type": "airbase", - "id": "Tabqa", - "size": 1000, - "importance": 1, - "captured_invert": true - } - ], - "links": [ - [ - "Hatay", - "Minakh" - ], - [ - "Aleppo", - "Minakh" - ], - [ - "Aleppo", - "Kuweires" - ], - [ - "Jirah", - "Kuweires" - ], - [ - "Jirah", - "Tabqa" - ] - ] + "miz": "invasion_from_turkey.miz" } \ No newline at end of file diff --git a/resources/campaigns/invasion_from_turkey.miz b/resources/campaigns/invasion_from_turkey.miz new file mode 100644 index 00000000..20527bc5 Binary files /dev/null and b/resources/campaigns/invasion_from_turkey.miz differ diff --git a/resources/campaigns/normandy.json b/resources/campaigns/normandy.json index b656709b..caf3b123 100644 --- a/resources/campaigns/normandy.json +++ b/resources/campaigns/normandy.json @@ -3,83 +3,5 @@ "theater": "Normandy", "authors": "Khopa", "description": "

Normandy 1944 D-Day scenario.

", - "player_points": [ - { - "type": "airbase", - "id": "Chailey", - "size": 600, - "importance": 1 - }, - { - "type": "airbase", - "id": "Needs Oar Point", - "size": 600, - "importance": 1 - }, - { - "type": "airbase", - "id": "Deux Jumeaux", - "size": 600, - "importance": 1 - } - ], - "enemy_points": [ - { - "type": "airbase", - "id": "Lignerolles", - "size": 600, - "importance": 1 - }, - { - "type": "airbase", - "id": "Lessay", - "size": 600, - "importance": 1 - }, - { - "type": "airbase", - "id": "Carpiquet", - "size": 600, - "importance": 1 - }, - { - "type": "airbase", - "id": "Maupertus", - "size": 600, - "importance": 1 - }, - { - "type": "airbase", - "id": "Evreux", - "size": 600, - "importance": 1, - "captured_invert": true - } - ], - "links": [ - [ - "Chailey", - "Needs Oar Point" - ], - [ - "Deux Jumeaux", - "Lignerolles" - ], - [ - "Lessay", - "Lignerolles" - ], - [ - "Carpiquet", - "Lignerolles" - ], - [ - "Lessay", - "Maupertus" - ], - [ - "Carpiquet", - "Evreux" - ] - ] + "miz":"normandy.miz" } \ No newline at end of file diff --git a/resources/campaigns/normandy.miz b/resources/campaigns/normandy.miz new file mode 100644 index 00000000..bdda71e8 Binary files /dev/null and b/resources/campaigns/normandy.miz differ diff --git a/resources/campaigns/normandy_full.miz b/resources/campaigns/normandy_full.miz new file mode 100644 index 00000000..6d78f013 Binary files /dev/null and b/resources/campaigns/normandy_full.miz differ diff --git a/resources/campaigns/normandy_small.miz b/resources/campaigns/normandy_small.miz index aebef85c..f422a7bb 100644 Binary files a/resources/campaigns/normandy_small.miz and b/resources/campaigns/normandy_small.miz differ diff --git a/resources/campaigns/russia_small.miz b/resources/campaigns/russia_small.miz index 312b2539..de890adb 100644 Binary files a/resources/campaigns/russia_small.miz and b/resources/campaigns/russia_small.miz differ diff --git a/resources/caulandmap.p b/resources/caulandmap.p index b7a3ecf3..a65459cd 100644 Binary files a/resources/caulandmap.p and b/resources/caulandmap.p differ diff --git a/resources/channellandmap.p b/resources/channellandmap.p index fbbb6e1e..9f5ba1f8 100644 Binary files a/resources/channellandmap.p and b/resources/channellandmap.p differ diff --git a/resources/factions/usa_2005_c130.json b/resources/factions/usa_2005_c130.json new file mode 100644 index 00000000..0ee4d192 --- /dev/null +++ b/resources/factions/usa_2005_c130.json @@ -0,0 +1,111 @@ +{ + "country": "USA", + "name": "USA 2005", + "authors": "Khopa", + "description": "

USA in the 2000s.

", + "aircrafts": [ + "F_15C", + "F_15E", + "F_14B", + "FA_18C_hornet", + "F_16C_50", + "A_10C", + "A_10C_2", + "AV8BNA", + "UH_1H", + "AH_64D", + "B_52H", + "B_1B", + "F_117A", + "Hercules" + ], + "awacs": [ + "E_3A" + ], + "tankers": [ + "KC_135", + "KC130" + ], + "frontline_units": [ + "MBT_M1A2_Abrams", + "ATGM_M1134_Stryker", + "APC_M1126_Stryker_ICV", + "IFV_M2A2_Bradley", + "IFV_LAV_25", + "APC_M1043_HMMWV_Armament", + "ATGM_M1045_HMMWV_TOW" + ], + "artillery_units": [ + "MLRS_M270", + "SPH_M109_Paladin" + ], + "logistics_units": [ + "Transport_M818" + ], + "infantry_units": [ + "Infantry_M4", + "Soldier_M249", + "Stinger_MANPADS" + ], + "air_defenses": [ + "AvengerGenerator", + "LinebackerGenerator", + "PatriotGenerator" + ], + "ewrs": [ + "PatriotEwrGenerator" + ], + "aircraft_carrier": [ + "CVN_74_John_C__Stennis" + ], + "helicopter_carrier": [ + "LHA_1_Tarawa" + ], + "destroyers": [ + "USS_Arleigh_Burke_IIa" + ], + "cruisers": [ + "Ticonderoga_class" + ], + "requirements": {}, + "carrier_names": [ + "CVN-71 Theodore Roosevelt", + "CVN-72 Abraham Lincoln", + "CVN-73 George Washington", + "CVN-74 John C. Stennis", + "CVN-75 Harry S. Truman" + ], + "helicopter_carrier_names": [ + "LHA-1 Tarawa", + "LHA-2 Saipan", + "LHA-3 Belleau Wood", + "LHA-4 Nassau", + "LHA-5 Peleliu" + ], + "navy_generators": [ + "ArleighBurkeGroupGenerator", + "OliverHazardPerryGroupGenerator" + ], + "has_jtac": true, + "jtac_unit": "MQ_9_Reaper", + "liveries_overrides": { + "FA_18C_hornet": [ + "VFA-37", + "VFA-106", + "VFA-113", + "VFA-122", + "VFA-131", + "VFA-192", + "VFA-34", + "VFA-83", + "VFA-87", + "VFA-97", + "VMFA-122", + "VMFA-132", + "VMFA-251", + "VMFA-312", + "VMFA-314", + "VMFA-323" + ] + } +} \ No newline at end of file diff --git a/resources/gulflandmap.p b/resources/gulflandmap.p index 83a4d573..3e2e566e 100644 Binary files a/resources/gulflandmap.p and b/resources/gulflandmap.p differ diff --git a/resources/nevlandmap.p b/resources/nevlandmap.p index 1086ba36..1b92631a 100644 Binary files a/resources/nevlandmap.p and b/resources/nevlandmap.p differ diff --git a/resources/normandylandmap.p b/resources/normandylandmap.p index d79fa2b4..808a2a1e 100644 Binary files a/resources/normandylandmap.p and b/resources/normandylandmap.p differ diff --git a/resources/syrialandmap.p b/resources/syrialandmap.p index 5cbe1025..658394d6 100644 Binary files a/resources/syrialandmap.p and b/resources/syrialandmap.p differ diff --git a/resources/tools/generate_landmap.py b/resources/tools/generate_landmap.py index 1cdf2931..1812ac4b 100644 --- a/resources/tools/generate_landmap.py +++ b/resources/tools/generate_landmap.py @@ -1,6 +1,7 @@ import pickle from dcs.mission import Mission +from shapely import geometry for terrain in ["cau", "nev", "syria", "channel", "normandy", "gulf"]: print("Terrain " + terrain) @@ -15,16 +16,16 @@ for terrain in ["cau", "nev", "syria", "channel", "normandy", "gulf"]: if terrain == "cau" and inclusion_zones: # legacy - exclusion_zones.append(zone) + exclusion_zones.append(geometry.Polygon(zone)) else: if plane_group.units[0].type == "F-15C": - exclusion_zones.append(zone) + exclusion_zones.append(geometry.Polygon(zone)) else: - inclusion_zones.append(zone) + inclusion_zones.append(geometry.Polygon(zone)) for ship_group in m.country("USA").ship_group: zone = [(x.position.x, x.position.y) for x in ship_group.points] - seas_zones.append(zone) + seas_zones.append(geometry.Polygon(zone)) with open("../{}landmap.p".format(terrain), "wb") as f: print(len(inclusion_zones), len(exclusion_zones), len(seas_zones)) diff --git a/resources/tools/normandy_terrain.miz b/resources/tools/normandy_terrain.miz index e35dff7b..9dbc18d4 100644 Binary files a/resources/tools/normandy_terrain.miz and b/resources/tools/normandy_terrain.miz differ