diff --git a/.gitignore b/.gitignore index 2aa12800..780b6059 100644 --- a/.gitignore +++ b/.gitignore @@ -1,10 +1,14 @@ *.pyc __pycache__ -build/* +build/** resources/payloads/*.lua venv logs.txt .DS_Store +dist/** +a.py +resources/tools/a.miz +tests/** # User-specific stuff .idea/**/workspace.xml .idea/**/tasks.xml diff --git a/.idea/modules.xml b/.idea/modules.xml index b52058de..c32a91ea 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -3,7 +3,6 @@ - \ No newline at end of file diff --git a/__init__.py b/__init__.py index f85f9c74..62d099e6 100755 --- a/__init__.py +++ b/__init__.py @@ -52,9 +52,7 @@ w = ui.window.Window() try: game = persistency.restore_game() if not game or not is_version_compatible(game.settings.version): - new_game_menu = None # type: NewGameMenu - new_game_menu = ui.newgamemenu.NewGameMenu(w, w.start_new_game) - new_game_menu.display() + ui.newgamemenu.NewGameMenu(w, w.start_new_game).display() else: game.settings.version = VERSION_STRING proceed_to_main_menu(game) diff --git a/a.py b/a.py new file mode 100644 index 00000000..6e421e47 --- /dev/null +++ b/a.py @@ -0,0 +1,38 @@ +from theater.caucasus import * +from gen.conflictgen import Conflict + +from matplotlib import pyplot +from matplotlib import lines +from shapely import geometry +from shapely.geometry import Polygon +from descartes.patch import PolygonPatch + +def put_lines(ls, ax): + for g in ls.geoms: + ax.plot([g.xy[0][0], g.xy[0][1]], [g.xy[1][0], g.xy[1][1]]) + +cau = CaucasusTheater() +#left, heading, dist = Conflict.frontline_vector(cau.soganlug, cau.kutaisi, cau) +#right = left.point_from_heading(heading, dist) + +left, heading = Conflict.frontline_position(cau, cau.soganlug, cau.kutaisi) +right = left.point_from_heading(heading+90, 80000) +left = left.point_from_heading(heading-90, 80000) + +line = geometry.LineString([(left.x, left.y), (right.x, right.y)]) +line = line.intersection(cau.land_poly) + +fig = pyplot.figure(1, figsize=(20, 20), dpi=90) +ax = fig.add_subplot(121) +ax.set_ylim([0, 1500000]) +ax.set_xlim([-600000, 400000]) + +patch = PolygonPatch(cau.land_poly, facecolor=(0, 0, 0), edgecolor=(0, 0, 0), alpha=0.5, zorder=2) +ax.add_patch(patch) +ax.plot([left.x, right.x], [left.y, right.y], 'k-', lw=2) + +ax.plot([cau.soganlug.position.x, cau.soganlug.position.x+1000], [cau.soganlug.position.y, cau.soganlug.position.y+1000], lw=5) +ax.plot([cau.kutaisi.position.x, cau.kutaisi.position.x+1000], [cau.kutaisi.position.y, cau.kutaisi.position.y+1000], lw=5) +put_lines(line, ax) +pyplot.show() + diff --git a/game/db.py b/game/db.py index 4721f5fb..7141d1f8 100644 --- a/game/db.py +++ b/game/db.py @@ -39,11 +39,11 @@ and prioritization for the enemy (i.e. less important bases will receive units w """ PRICES = { # fighter - MiG_23MLD: 18, - Su_27: 20, + MiG_23MLD: 13, + Su_27: 18, Su_33: 22, - MiG_29A: 23, - MiG_29S: 25, + MiG_29A: 18, + MiG_29S: 20, F_5E_3: 6, MiG_15bis: 5, @@ -54,6 +54,7 @@ PRICES = { M_2000C: 13, FA_18C_hornet: 18, F_15C: 20, + F_14B: 14, # bomber Su_25: 15, @@ -145,6 +146,7 @@ UNIT_BY_TASK = { MiG_29S, FA_18C_hornet, F_15C, + F_14B, M_2000C, ], CAS: [ @@ -304,6 +306,7 @@ UNIT_BY_COUNTRY = { "USA": [ F_5E_3, F_15C, + F_14B, FA_18C_hornet, AJS37, M_2000C, @@ -367,16 +370,25 @@ Payload will be used for operation of following type, "*" category will be used PLANE_PAYLOAD_OVERRIDES = { FA_18C_hornet: { CAP: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel", + Escort: "AIM-120*4,AIM-9*2,AIM-7*2,Fuel", PinpointStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel", AntishipStrike: "MK-82*8,AIM-9*2,AIM-7,FLIR Pod,Fuel", }, + F_14B: { + CAP: "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2", + Escort: "AIM-54A-MK47*4, AIM-7M*2, AIM-9M*2, XT*2", + CAS: "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN", + GroundAttack: "AIM-54A-MK60*1, AIM-7M*1, AIM-9M*2, XT*2, Mk-82*2, LANTIRN", + }, + Su_25T: { CAS: "APU-8 Vikhr-M*2,Kh-25ML,R-73*2,SPPU-22*2,Mercury LLTV Pod,MPS-410", }, Su_33: { CAP: "R-73*4,R-27R*2,R-27ER*6", + Escort: "R-73*4,R-27R*2,R-27ER*6", }, AJS37: { @@ -399,6 +411,7 @@ PLANE_PAYLOAD_OVERRIDES = { M_2000C: { CAP: "Combat Air Patrol", + Escort: "Combat Air Patrol", GroundAttack: "MK-82S Heavy Strike", }, diff --git a/game/game.py b/game/game.py index 3d016457..356e3375 100644 --- a/game/game.py +++ b/game/game.py @@ -111,7 +111,7 @@ class Game: # skip naval events for non-coastal CPs return - if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD: + if event_class == BaseAttackEvent and enemy_cp.base.strength > PLAYER_BASEATTACK_THRESHOLD and self.settings.version != "dev": # skip base attack events for CPs yet too strong return @@ -163,6 +163,7 @@ class Game: def _generate_events(self): strikes_generated_for = set() + base_attack_generated_for = set() for player_cp, enemy_cp in self.theater.conflicts(True): for event_class, (player_probability, enemy_probability) in EVENT_PROBABILITIES.items(): @@ -171,15 +172,20 @@ class Game: if not Conflict.has_frontline_between(player_cp, enemy_cp): continue - if event_class in [StrikeEvent]: - # don't generate multiple 100% strike events from each attack direction + # don't generate multiple 100% events from each attack direction + if event_class is StrikeEvent: if enemy_cp in strikes_generated_for: continue + if event_class is BaseAttackEvent: + if enemy_cp in base_attack_generated_for: + continue if player_probability == 100 or player_probability > 0 and self._roll(player_probability, player_cp.base.strength): self._generate_player_event(event_class, player_cp, enemy_cp) - if event_class in [StrikeEvent]: + if event_class is StrikeEvent: strikes_generated_for.add(enemy_cp) + if event_class is BaseAttackEvent: + base_attack_generated_for.add(enemy_cp) if enemy_probability == 100 or enemy_probability > 0 and self._roll(enemy_probability, enemy_cp.base.strength): self._generate_enemy_event(event_class, player_cp, enemy_cp) diff --git a/gen/conflictgen.py b/gen/conflictgen.py index 5f3b4a21..775d0787 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -162,6 +162,8 @@ class Conflict: strength_delta = (from_cp.base.strength - to_cp.base.strength) / 1.0 position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE) + return position, _opposite_heading(attack_heading) + ground_position = cls._find_ground_position(position, attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE, attack_heading, theater) if ground_position: return ground_position, _opposite_heading(attack_heading) @@ -172,6 +174,23 @@ class Conflict: @classmethod def frontline_vector(cls, from_cp: ControlPoint, to_cp: ControlPoint, theater: ConflictTheater) -> typing.Optional[typing.Tuple[Point, int, int]]: + initial, heading = cls.frontline_position(theater, from_cp, to_cp) + + """ + probe_end_point = initial.point_from_heading(heading, FRONTLINE_LENGTH) + probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ]) + intersection = probe.intersection(theater.land_poly) + + if isinstance(intersection, geometry.LineString): + intersection = intersection + elif isinstance(intersection, geometry.MultiLineString): + intersection = intersection.geoms[0] + else: + print(intersection) + return None + + return Point(*intersection.xy[0]), _heading_sum(heading, 90), intersection.length + """ frontline = cls.frontline_position(theater, from_cp, to_cp) if not frontline: return None @@ -207,9 +226,21 @@ class Conflict: pos = new_pos else: return pos - return pos + """ + probe_end_point = initial.point_from_heading(heading, max_distance) + probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y)]) + + intersection = probe.intersection(theater.land_poly) + if intersection is geometry.LineString: + return Point(*intersection.xy[1]) + elif intersection is geometry.MultiLineString: + return Point(*intersection.geoms[0].xy[1]) + + return None + """ + @classmethod def _find_ground_position(cls, initial: Point, max_distance: int, heading: int, theater: ConflictTheater) -> typing.Optional[Point]: pos = initial @@ -218,8 +249,18 @@ class Conflict: return pos pos = pos.point_from_heading(heading, 500) + """ + probe_end_point = initial.point_from_heading(heading, max_distance) + probe = geometry.LineString([(initial.x, initial.y), (probe_end_point.x, probe_end_point.y) ]) - logging.error("Didn't find ground position!") + intersection = probe.intersection(theater.land_poly) + if isinstance(intersection, geometry.LineString): + return Point(*intersection.xy[1]) + elif isinstance(intersection, geometry.MultiLineString): + return Point(*intersection.geoms[0].xy[1]) + """ + + logging.error("Didn't find ground position ({})!".format(initial)) return initial @classmethod @@ -305,7 +346,7 @@ class Conflict: initial_location = to_cp.position.random_point_within(*GROUND_ATTACK_DISTANCE) position = Conflict._find_ground_position(initial_location, GROUND_INTERCEPT_SPREAD, _heading_sum(heading, 180), theater) if not position: - heading = to_cp.find_radial(to_cp.positioN.heading_between_point(from_cp.position)) + heading = to_cp.find_radial(to_cp.position.heading_between_point(from_cp.position)) position = to_cp.position.point_from_heading(heading, to_cp.size * GROUND_DISTANCE_FACTOR) return cls( diff --git a/pyinstaller.spec b/pyinstaller.spec new file mode 100644 index 00000000..dd61e5f3 --- /dev/null +++ b/pyinstaller.spec @@ -0,0 +1,40 @@ +# -*- mode: python -*- + +block_cipher = None + + +a = Analysis(['__init__.py'], + pathex=['C:\\Users\\shdwp\\PycharmProjects\\dcs_liberation'], + binaries=[], + datas=[ + ('resources', 'resources'), + ('submodules/dcs/dcs/terrain/caucasus.p', 'dcs/terrain/'), + ('submodules/dcs/dcs/terrain/nevada.p', 'dcs/terrain/'), + ], + hookspath=[], + runtime_hooks=[], + excludes=[], + win_no_prefer_redirects=False, + win_private_assemblies=False, + cipher=block_cipher, + noarchive=False) +pyz = PYZ(a.pure, a.zipped_data, + cipher=block_cipher) +exe = EXE(pyz, + a.scripts, + [], + icon="resources/icon.ico", + exclude_binaries=True, + name='liberation_main', + debug=False, + bootloader_ignore_signals=False, + strip=False, + upx=True, + console=True ) +coll = COLLECT(exe, + a.binaries, + a.zipfiles, + a.datas, + strip=False, + upx=True, + name='dcs_liberation') diff --git a/icon.ico b/resources/icon.ico similarity index 100% rename from icon.ico rename to resources/icon.ico diff --git a/resources/tools/mkrelease.py b/resources/tools/mkrelease.py index 2295267b..9723cbd6 100644 --- a/resources/tools/mkrelease.py +++ b/resources/tools/mkrelease.py @@ -1,4 +1,5 @@ import os +import shutil from zipfile import * @@ -42,11 +43,13 @@ def _mk_archieve(): print("version already exists") return + shutil.rmtree("./dist") + + os.system("pyinstaller.exe pyinstaller.spec") + archieve = ZipFile(path, "w") - archieve.writestr("start.bat", "py.exe __init__.py \"%UserProfile%\\Saved Games\" \"{}\"".format(VERSION)) - _zip_dir(archieve, ".") - os.chdir("submodules\\dcs") - _zip_dir(archieve, "dcs") + archieve.writestr("dcs_liberation.bat", "cd dist\\dcs_liberation;\nliberation_main \"%UserProfile%\\Saved Games\" \"{}\"".format(VERSION)) + _zip_dir(archieve, "./dist/dcs_liberation") _mk_archieve() \ No newline at end of file diff --git a/resources/ui/ground_assets/aa.png b/resources/ui/ground_assets/aa.png new file mode 100644 index 00000000..26ea05c7 Binary files /dev/null and b/resources/ui/ground_assets/aa.png differ diff --git a/resources/ui/ground_assets/ammo.png b/resources/ui/ground_assets/ammo.png new file mode 100644 index 00000000..c2e6ffc0 Binary files /dev/null and b/resources/ui/ground_assets/ammo.png differ diff --git a/resources/ui/ground_assets/cleared.png b/resources/ui/ground_assets/cleared.png new file mode 100644 index 00000000..2912f6de Binary files /dev/null and b/resources/ui/ground_assets/cleared.png differ diff --git a/resources/ui/ground_assets/comms.png b/resources/ui/ground_assets/comms.png new file mode 100644 index 00000000..bff1a9f9 Binary files /dev/null and b/resources/ui/ground_assets/comms.png differ diff --git a/resources/ui/ground_assets/factory.png b/resources/ui/ground_assets/factory.png new file mode 100644 index 00000000..0d9f3b2d Binary files /dev/null and b/resources/ui/ground_assets/factory.png differ diff --git a/resources/ui/ground_assets/farp.png b/resources/ui/ground_assets/farp.png new file mode 100644 index 00000000..62fef986 Binary files /dev/null and b/resources/ui/ground_assets/farp.png differ diff --git a/resources/ui/ground_assets/fob.png b/resources/ui/ground_assets/fob.png new file mode 100644 index 00000000..27b0ab33 Binary files /dev/null and b/resources/ui/ground_assets/fob.png differ diff --git a/resources/ui/ground_assets/fuel.png b/resources/ui/ground_assets/fuel.png new file mode 100644 index 00000000..1324adcb Binary files /dev/null and b/resources/ui/ground_assets/fuel.png differ diff --git a/resources/ui/ground_assets/oil.png b/resources/ui/ground_assets/oil.png new file mode 100644 index 00000000..548cdbab Binary files /dev/null and b/resources/ui/ground_assets/oil.png differ diff --git a/resources/ui/ground_assets/power.png b/resources/ui/ground_assets/power.png new file mode 100644 index 00000000..fd0260d1 Binary files /dev/null and b/resources/ui/ground_assets/power.png differ diff --git a/resources/ui/ground_assets/target.png b/resources/ui/ground_assets/target.png new file mode 100644 index 00000000..c93255c1 Binary files /dev/null and b/resources/ui/ground_assets/target.png differ diff --git a/resources/ui/ground_assets/warehouse.png b/resources/ui/ground_assets/warehouse.png new file mode 100644 index 00000000..fa7a38e1 Binary files /dev/null and b/resources/ui/ground_assets/warehouse.png differ diff --git a/submodules/dcs b/submodules/dcs index fae12668..4fbb7ad3 160000 --- a/submodules/dcs +++ b/submodules/dcs @@ -1 +1 @@ -Subproject commit fae126689132d643d317252adfb03184042a0ded +Subproject commit 4fbb7ad3e0e2eecedc4e1dd14f2eb18025fef9f5 diff --git a/theater/conflicttheater.py b/theater/conflicttheater.py index 82e82794..9dd258ea 100644 --- a/theater/conflicttheater.py +++ b/theater/conflicttheater.py @@ -53,10 +53,18 @@ class ConflictTheater: reference_points = None # type: typing.Dict overview_image = None # type: str landmap = None # type: landmap.Landmap + """ + land_poly = None # type: Polygon + """ daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]] def __init__(self): self.controlpoints = [] + """ + self.land_poly = geometry.Polygon(self.landmap[0][0]) + for x in self.landmap[1]: + self.land_poly = self.land_poly.difference(geometry.Polygon(x)) + """ def add_controlpoint(self, point: ControlPoint, connected_to: typing.Collection[ControlPoint] = []): for connected_point in connected_to: diff --git a/theater/nevada.py b/theater/nevada.py index c55b127d..54ede6ac 100644 --- a/theater/nevada.py +++ b/theater/nevada.py @@ -19,7 +19,6 @@ class NevadaTheater(ConflictTheater): "night": (0, 5), } - mina = ControlPoint.from_airport(nevada.Mina_Airport_3Q0, LAND, SIZE_SMALL, IMPORTANCE_LOW) tonopah = ControlPoint.from_airport(nevada.Tonopah_Airport, LAND, SIZE_SMALL, IMPORTANCE_LOW) tonopah_test_range = ControlPoint.from_airport(nevada.Tonopah_Test_Range_Airfield, LAND, SIZE_SMALL, IMPORTANCE_LOW) lincoln_conty = ControlPoint.from_airport(nevada.Lincoln_County, LAND, SIZE_SMALL, 1.2) @@ -37,8 +36,7 @@ class NevadaTheater(ConflictTheater): def __init__(self): super(NevadaTheater, self).__init__() - self.add_controlpoint(self.mina, connected_to=[self.tonopah]) - self.add_controlpoint(self.tonopah, connected_to=[self.mina, self.tonopah_test_range, self.lincoln_conty]) + self.add_controlpoint(self.tonopah, connected_to=[self.tonopah_test_range, self.lincoln_conty]) self.add_controlpoint(self.tonopah_test_range, connected_to=[self.tonopah, self.lincoln_conty, self.groom_lake, self.pahute_mesa]) self.add_controlpoint(self.lincoln_conty, connected_to=[self.tonopah_test_range, self.mesquite]) @@ -52,5 +50,5 @@ class NevadaTheater(ConflictTheater): self.add_controlpoint(self.jean, connected_to=[self.laughlin, self.las_vegas]) self.add_controlpoint(self.laughlin, connected_to=[self.jean, self.las_vegas]) - self.mina.captured = True + self.tonopah.captured = True diff --git a/ui/overviewcanvas.py b/ui/overviewcanvas.py index 129365eb..0bf3d455 100644 --- a/ui/overviewcanvas.py +++ b/ui/overviewcanvas.py @@ -25,7 +25,7 @@ BLACK = (0, 0, 0) BACKGROUND = pygame.Color(0, 64, 64) ANTIALIASING = True -WIDTH = 1066 +WIDTH = 800 HEIGHT = 600 MAP_PADDING = 100 diff --git a/ui/window.py b/ui/window.py index 0b1df41f..b5e8f7b4 100644 --- a/ui/window.py +++ b/ui/window.py @@ -20,7 +20,7 @@ class Window: def __init__(self): self.tk = Tk() self.tk.title("DCS Liberation") - self.tk.iconbitmap("icon.ico") + self.tk.iconbitmap("resources/icon.ico") self.tk.resizable(False, False) self.tk.grid_columnconfigure(0, weight=1) self.tk.grid_rowconfigure(0, weight=1) @@ -44,7 +44,7 @@ class Window: helpmenu.add_separator() helpmenu.add_command(label="Contribute", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation")) helpmenu.add_command(label="Forum Thread", command=lambda: webbrowser.open_new_tab("https://forums.eagle.ru/showthread.php?t=214834")) - helpmenu.add_command(label="Report an issue", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/issues")) + helpmenu.add_command(label="Report an issue", command=self.report_issue) menubar.add_cascade(label="Help", menu=helpmenu) self.tk.config(menu=menubar) @@ -133,6 +133,9 @@ class Window: else: pass + def report_issue(self): + raise logging_module.ShowLogsException() + def exit(self): self.tk.destroy() sys.exit(0)