diff --git a/__init__.py b/__init__.py index 1cf12453..2b376f4f 100755 --- a/__init__.py +++ b/__init__.py @@ -1,5 +1,5 @@ #!/usr/bin/env python3 -import pickle +import sys import theater.caucasus import ui.window @@ -9,6 +9,14 @@ from game.game import Game from theater import start_generator from userdata import persistency +from dcs.lua.parse import loads + +with open("/Users/sp/Downloads/won_cap.log", "r") as f: + s = f.read() + print(loads(s)) + +#sys.exit(0) + game = persistency.restore_game() if not game: theater = theater.caucasus.CaucasusTheater() diff --git a/game/db.py b/game/db.py index 3c6d2ea2..89cf2b7c 100644 --- a/game/db.py +++ b/game/db.py @@ -42,19 +42,20 @@ PRICES = { Armor.APC_BTR_80: 6, AirDefence.AAA_ZU_23_on_Ural_375: 4, + AirDefence.SAM_Avenger_M1097: 10, } UNIT_BY_TASK = { FighterSweep: [Su_27, Su_33, Su_25, F_15C, MiG_15bis, MiG_21Bis, MiG_29A, F_A_18C, AV8BNA], CAS: [Su_25T, A_10A, A_10C, ], CAP: [Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, Armor.APC_BTR_80, ], - AirDefence: [AirDefence.AAA_ZU_23_on_Ural_375, ], + AirDefence: [AirDefence.AAA_ZU_23_on_Ural_375, AirDefence.SAM_Avenger_M1097 ], Transport: [IL_76MD, S_3B_Tanker, ], } UNIT_BY_COUNTRY = { "Russia": [Su_25T, Su_27, Su_33, Su_25, MiG_15bis, MiG_21Bis, MiG_29A, AirDefence.AAA_ZU_23_on_Ural_375, Armor.APC_BTR_80, Armor.MBT_T_90, Armor.MBT_T_80U, Armor.MBT_T_55, IL_76MD, ], - "USA": [F_15C, A_10C, F_A_18C, AV8BNA, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, S_3B_Tanker], + "USA": [F_15C, A_10C, F_A_18C, AV8BNA, Armor.MBT_M1A2_Abrams, Armor.MBT_M60A3_Patton, Armor.ATGM_M1134_Stryker, S_3B_Tanker, AirDefence.SAM_Avenger_M1097], } UnitsDict = typing.Dict[UnitType, int] diff --git a/game/event.py b/game/event.py index 33785eb2..ee691e7a 100644 --- a/game/event.py +++ b/game/event.py @@ -56,12 +56,12 @@ class GroundInterceptEvent(Event): targets = None # type: db.ArmorDict def __str__(self): - return "Ground intercept from {} at {} ({})".format(self.from_cp, self.to_cp, "*" * self.difficulty) + return "Ground intercept from {} at {}".format(self.from_cp, self.to_cp) def is_successfull(self, debriefing: Debriefing): total_targets = sum(self.targets.values()) destroyed_targets = 0 - for unit, count in debriefing.destroyed_units[self.defender.name].items(): + for unit, count in debriefing.destroyed_units[self.defender_name].items(): if unit in self.targets: destroyed_targets += count @@ -113,10 +113,10 @@ class InterceptEvent(Event): transport_unit = None # type: FlyingType def __str__(self): - return "Intercept from {} at {} ({})".format(self.from_cp, self.to_cp, "*" * self.difficulty) + return "Intercept from {} at {}".format(self.from_cp, self.to_cp) def is_successfull(self, debriefing: Debriefing): - intercepted = self.transport_unit in debriefing.destroyed_units[self.defender.name].keys() + intercepted = self.transport_unit in debriefing.destroyed_units[self.defender_name].keys() if self.from_cp.captured: return intercepted else: @@ -185,10 +185,10 @@ class CaptureEvent(Event): STRENGTH_RECOVERY = 0.35 def __str__(self): - return "Attack from {} to {} ({})".format(self.from_cp, self.to_cp, "*" * self.difficulty) + return "Attack from {} to {}".format(self.from_cp, self.to_cp) def is_successfull(self, debriefing: Debriefing): - attackers_success = len(debriefing.destroyed_units[self.defender.name]) > len(debriefing.destroyed_units[self.attacker.name]) + attackers_success = len(debriefing.destroyed_units[self.defender_name]) > len(debriefing.destroyed_units[self.attacker_name]) if self.from_cp.captured: return attackers_success else: diff --git a/game/game.py b/game/game.py index 0811ff80..7e635dab 100644 --- a/game/game.py +++ b/game/game.py @@ -21,8 +21,8 @@ ENEMY_INTERCEPT_PROBABILITY_BASE = 10 ENEMY_INTERCEPT_GLOBAL_PROBABILITY_BASE = 1 ENEMY_CAPTURE_PROBABILITY_BASE = 3 -PLAYER_INTERCEPT_PROBABILITY_BASE = 30 -PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 30 +PLAYER_INTERCEPT_PROBABILITY_BASE = 100 +PLAYER_GROUNDINTERCEPT_PROBABILITY_BASE = 100 PLAYER_GLOBALINTERCEPT_PROBABILITY_BASE = 100 PLAYER_INTERCEPT_GLOBAL_PROBABILITY_BASE = 50 @@ -40,8 +40,8 @@ class Game: def __init__(self, theater: ConflictTheater): self.events = [] self.theater = theater - self.player = "USA" - self.enemy = "Russia" + self.player = "Russia" + self.enemy = "USA" def _roll(self, prob, mult): return random.randint(0, 100) <= prob * mult @@ -132,16 +132,6 @@ class Game: theater=self.theater)) break - def _generate_global(self): - for cp in self.theater.player_points(): - if cp.is_global: - if self._roll(PLAYER_GLOBALINTERCEPT_PROBABILITY_BASE, cp.base.strength): - enemy_points = list(set(self.theater.enemy_bases()) - set(self.theater.conflicts(False))) - self.events.append(InterceptEvent(attacker_name=self.player, - defender_name=self.enemy, - from_cp=cp, - to_cp=random.choice(enemy_points))) - def _commision_units(self, cp: ControlPoint): for for_task in [CAP, CAS, FighterSweep, AirDefence]: limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE) @@ -207,5 +197,4 @@ class Game: self._generate_interceptions() self._generate_globalinterceptions() self._generate_groundinterceptions() - self._generate_global() diff --git a/game/operation.py b/game/operation.py index 354b0c06..52c92127 100644 --- a/game/operation.py +++ b/game/operation.py @@ -159,7 +159,8 @@ class GroundInterceptOperation(Operation): radials=ALL_RADIALS ) - super(GroundInterceptOperation, self).__init__(mission, conflict) + self.initialize(mission=mission, + conflict=conflict) def generate(self): self.airgen.generate_cas(self.strikegroup, clients=self.attacker_clients, at=self.starting_position) diff --git a/ui/eventmenu.py b/ui/eventmenu.py index 82f9467d..57620e71 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -23,29 +23,33 @@ class EventMenu(Menu): self.window.clear_right_pane() row = 0 - def label(text): + def label(text, _row=None, _column=None): nonlocal row - Label(self.frame, text=text).grid() + Label(self.frame, text=text).grid(row=_row and _row or row, column=_column and _column or 0) - row += 1 + if _row is None: + row += 1 def scrable_row(unit_type, unit_count): nonlocal row - Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row) - scramble_entry = Entry(self.frame) + Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row, sticky=W) + scramble_entry = Entry(self.frame, width=10) scramble_entry.grid(column=1, row=row) + scramble_entry.insert(0, "0") self.aircraft_scramble_entries[unit_type] = scramble_entry - client_entry = Entry(self.frame) + client_entry = Entry(self.frame, width=10) client_entry.grid(column=2, row=row) + client_entry.insert(0, "0") self.aircraft_client_entries[unit_type] = client_entry row += 1 def scramble_armor_row(unit_type, unit_count): nonlocal row - Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row) - scramble_entry = Entry(self.frame) + Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count)).grid(row=row, sticky=W) + scramble_entry = Entry(self.frame, width=10) + scramble_entry.insert(0, "0") scramble_entry.grid(column=1, row=row) self.armor_scramble_entries[unit_type] = scramble_entry @@ -58,13 +62,23 @@ class EventMenu(Menu): base = self.event.to_cp.base label("Aircraft") + label("Amount", row, 1) + label("Client slots", row, 2) + row+=1 + for unit_type, count in base.aircraft.items(): scrable_row(unit_type, count) + if not base.total_planes: + label("None") + label("Armor") for unit_type, count in base.armor.items(): scramble_armor_row(unit_type, count) + if not base.total_armor: + label("None") + Button(self.frame, text="Commit", command=self.start).grid(column=0, row=row) Button(self.frame, text="Back", command=self.dismiss).grid(column=2, row=row) diff --git a/ui/eventresultsmenu.py b/ui/eventresultsmenu.py index a7b5e725..b8507023 100644 --- a/ui/eventresultsmenu.py +++ b/ui/eventresultsmenu.py @@ -21,14 +21,14 @@ class EventResultsMenu(Menu): self.window.clear_right_pane() if not self.finished: - Button(self.frame, text="no losses, succ", command=self.simulate_result(0, 1, True)).grid() - Button(self.frame, text="no losses, fail", command=self.simulate_result(0, 1, False)).grid(row=1, column=1) + Button(self.frame, text="no losses, succ", command=self.simulate_result(0, 1)).grid() + Button(self.frame, text="no losses, fail", command=self.simulate_result(0, 1)).grid(row=1, column=1) - Button(self.frame, text="half losses, succ", command=self.simulate_result(0.5, 0.5, True)).grid(row=2, ) - Button(self.frame, text="half losses, fail", command=self.simulate_result(0.5, 0.5, False)).grid(row=2, column=1) + Button(self.frame, text="half losses, succ", command=self.simulate_result(0.5, 0.5)).grid(row=2, ) + Button(self.frame, text="half losses, fail", command=self.simulate_result(0.5, 0.5)).grid(row=2, column=1) - Button(self.frame, text="full losses, succ", command=self.simulate_result(1, 0, True)).grid(row=3, ) - Button(self.frame, text="full losses, fail", command=self.simulate_result(1, 0, False)).grid(row=3, column=1) + Button(self.frame, text="full losses, succ", command=self.simulate_result(1, 0)).grid(row=3, ) + Button(self.frame, text="full losses, fail", command=self.simulate_result(1, 0)).grid(row=3, column=1) Label(self.frame, text="Play the mission and save debriefing to {}".format(debriefing_directory_location())).grid(row=0, column=0) else: @@ -55,6 +55,10 @@ class EventResultsMenu(Menu): Button(self.frame, text="Okay", command=self.dismiss).grid(columnspan=1, row=row); row += 1 def process_debriefing(self, debriefing: Debriefing): + debriefing.calculate_destroyed_units(mission=self.event.operation.mission, + player_name=self.game.player, + enemy_name=self.game.enemy) + self.game.finish_event(event=self.event, debriefing=debriefing) self.game.pass_turn() @@ -63,7 +67,7 @@ class EventResultsMenu(Menu): self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy, {}) self.display() - def simulate_result(self, player_factor: float, enemy_factor: float, result: bool): + def simulate_result(self, player_factor: float, enemy_factor: float): def action(): debriefing = Debriefing() diff --git a/ui/mainmenu.py b/ui/mainmenu.py index d2cae4be..9bceb0ee 100644 --- a/ui/mainmenu.py +++ b/ui/mainmenu.py @@ -34,9 +34,9 @@ class MainMenu(Menu): def event_button(event): nonlocal row Message(self.frame, text="{}{}".format( - event.defender.name == self.game.player and "Enemy attacking: " or "", + event.defender_name == self.game.player and "Enemy attacking: " or "", event - ), aspect=500).grid(column=0, row=row, sticky=NW) + ), aspect=800).grid(column=0, row=row, sticky=NW) Button(self.frame, text=">", command=self.start_event(event)).grid(column=0, row=row, sticky=NE+S) row += 1 Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1 diff --git a/userdata/debriefing.py b/userdata/debriefing.py index 069c4d5c..4ec1d1be 100644 --- a/userdata/debriefing.py +++ b/userdata/debriefing.py @@ -1,26 +1,67 @@ import typing - -import json import threading import time import os -from datetime import datetime +from dcs.lua import parse +from dcs.mission import Mission + +from dcs.unitgroup import FlyingGroup +from dcs.unit import UnitType + +from game import db DEBRIEFING_LOG_EXTENSION = "log" class Debriefing: - def __init__(self): + def __init__(self, alive_units): self.destroyed_units = {} # type: typing.Dict[str, typing.Dict[str, int]] + self.alive_units = alive_units # type: typing.Dict[str, typing.Dict[str, int]] @classmethod - def parse(cls, path: str): + def parse(cls, path: str, mission: Mission): with open(path, "r") as f: - events = json.load(f) + table_string = f.read() + table = parse.loads(table_string) + units = table.get("debriefing", {}).get("world_state", {}) + alive_units = {} - return Debriefing() + for unit in units: + type = unit["type"] # type: str + country_id = int(unit["country"]) + if type: + country_dict = alive_units.get(unit[country_id], {}) + country_dict[type] = country_dict.get(type, 0) + 1 + alive_units[unit[country_id]] = country_dict + return Debriefing(alive_units) + + def calculate_destroyed_units(self, mission: Mission, player_name: str, enemy_name: str): + def count_groups(groups: typing.List[UnitType]) -> typing.Dict[UnitType, int]: + result = {} + for group in groups: + for unit in group.units: + result[unit.unit_type] = result.get(unit.unit_type, 0) + 1 + + return result + + def calculate_losses(all_units: typing.Dict[UnitType, int], alive_units: typing.Dict[str, int]) -> typing.Dict[UnitType, int]: + result = {} + for t, count in all_units.items(): + result[t] = count - alive_units[db.unit_type_name(t)] + return result + + player = mission.country(player_name) + enemy = mission.country(enemy_name) + + player_units = count_groups(player.plane_group + player.vehicle_group) + enemy_units = count_groups(enemy.plane_group + enemy.vehicle_group) + + self.destroyed_units = { + player.name: calculate_losses(player_units, self.alive_units[player.id]), + enemy.name: calculate_losses(enemy_units, self.alive_units[enemy.id]), + } def debriefing_directory_location() -> str: return "build/debriefing"