diff --git a/game/db.py b/game/db.py index 61848136..0ff1e24d 100644 --- a/game/db.py +++ b/game/db.py @@ -739,6 +739,14 @@ TIME_PERIODS = { "Syrian War [2011]": datetime(2011, 8, 7), } +REWARDS = { + "power": 4, "warehouse": 2, "fuel": 2, "ammo": 2, + "farp": 1, "fob": 1, "factory": 10, "comms": 10, "oil": 10 +} + +# Base post-turn bonus value +PLAYER_BUDGET_BASE = 20 + CARRIER_CAPABLE = [ FA_18C_hornet, F_14B, diff --git a/game/event/event.py b/game/event/event.py index be3fb9eb..25309856 100644 --- a/game/event/event.py +++ b/game/event/event.py @@ -8,6 +8,7 @@ from dcs.vehicles import AirDefence from dcs.unittype import UnitType from game import * +from game.infos.information import Information from theater import * from gen.environmentgen import EnvironmentSettings from gen.conflictgen import Conflict @@ -220,6 +221,10 @@ class Event: cp.base.aa = {} for g in cp.ground_objects: g.groups = [] + info = Information(cp.name + " lost !", + "The ennemy took control of " + cp.name + "\nShame on us !", + self.game.turn) + self.game.informations.append(info) elif not(cp.captured) and new_owner_coalition == coalition: cp.captured = True cp.base.aircraft = {} @@ -227,6 +232,10 @@ class Event: cp.base.aa = {} for g in cp.ground_objects: g.groups = [] + info = Information(cp.name + " captured !", + "The ennemy took control of " + cp.name + "\nShame on us !", + self.game.turn) + self.game.informations.append(info) except Exception as e: print(e) @@ -281,17 +290,25 @@ class Event: # No progress with defensive strategies if player_won and cp.stances[enemy_cp.id] in [CombatStance.DEFENSIVE, CombatStance.AMBUSH]: - print("Defensive stance, no progress") - delta = 0 + print("Defensive stance, progress is limited") + delta = MINOR_DEFEAT_INFLUENCE if player_won: print(cp.name + " won ! factor > " + str(delta)) cp.base.affect_strength(delta) enemy_cp.base.affect_strength(-delta) + info = Information("Frontline Report", + "Our ground forces from " + cp.name + " are making progress toward " + enemy_cp.name, + self.game.turn) + self.game.informations.append(info) else: - print(enemy_cp.name + " won ! factor > " + str(delta)) + print(cp.name + " lost ! factor > " + str(delta)) enemy_cp.base.affect_strength(delta) cp.base.affect_strength(-delta) + info = Information("Frontline Report", + "Our ground forces from " + cp.name + " are losing ground against the enemy forces from" + enemy_cp.name, + self.game.turn) + self.game.informations.append(info) def skip(self): pass @@ -319,4 +336,9 @@ class UnitsDeliveryEvent(Event): self.units[k] = self.units.get(k, 0) + v def skip(self): + + for k, v in self.units.items(): + info = Information("Ally Reinforcement", str(k.id) + " x " + str(v) + " at " + self.to_cp.name, self.game.turn) + self.game.informations.append(info) + self.to_cp.base.commision_units(self.units) diff --git a/game/factions/libya_2011.py b/game/factions/libya_2011.py index fbde9f03..eca6247b 100644 --- a/game/factions/libya_2011.py +++ b/game/factions/libya_2011.py @@ -4,7 +4,7 @@ from dcs.planes import * from dcs.helicopters import * Lybia_2011 = { - "country": "Libya", + "country": "Russia", "side": "red", "units": [ diff --git a/game/game.py b/game/game.py index 51f6afbc..330e8d2f 100644 --- a/game/game.py +++ b/game/game.py @@ -6,6 +6,7 @@ import math from dcs.task import * from dcs.vehicles import * +from game.db import REWARDS, PLAYER_BUDGET_BASE from game.game_stats import GameStats from game.infos.information import Information from gen.conflictgen import Conflict @@ -50,10 +51,7 @@ ENEMY_BASE_STRENGTH_RECOVERY = 0.05 AWACS_BUDGET_COST = 4 # Initial budget value -PLAYER_BUDGET_INITIAL = 450 - -# Base post-turn bonus value -PLAYER_BUDGET_BASE = 20 +PLAYER_BUDGET_INITIAL = 650 # Bonus multiplier logarithm base PLAYER_BUDGET_IMPORTANCE_LOG = 2 @@ -86,6 +84,7 @@ class Game: self.planners = {} self.ground_planners = {} self.informations = [] + self.informations.append(Information("Game Start", "-" * 40, 0)) def _roll(self, prob, mult): if self.settings.version == "dev": @@ -111,10 +110,12 @@ class Game: def _commision_units(self, cp: ControlPoint): for for_task in [CAS, CAP, AirDefence]: - limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_LIMITS_SCALE) * self.settings.multiplier + limit = COMMISION_LIMITS_FACTORS[for_task] * math.pow(cp.importance, + COMMISION_LIMITS_SCALE) * self.settings.multiplier missing_units = limit - cp.base.total_units(for_task) if missing_units > 0: - awarded_points = COMMISION_AMOUNTS_FACTORS[for_task] * math.pow(cp.importance, COMMISION_AMOUNTS_SCALE) * self.settings.multiplier + awarded_points = COMMISION_AMOUNTS_FACTORS[for_task] * math.pow(cp.importance, + COMMISION_AMOUNTS_SCALE) * self.settings.multiplier points_to_spend = cp.base.append_commision_points(for_task, awarded_points) if points_to_spend > 0: unittypes = self.commision_unit_types(cp, for_task) @@ -130,25 +131,8 @@ class Game: reward = PLAYER_BUDGET_BASE * len(self.theater.player_points()) for cp in self.theater.player_points(): for g in cp.ground_objects: - # (Reward is per building) - if g.category == "power": - reward = reward + 4 - elif g.category == "warehouse": - reward = reward + 2 - elif g.category == "fuel": - reward = reward + 2 - elif g.category == "ammo": - reward = reward + 2 - elif g.category == "farp": - reward = reward + 1 - elif g.category == "fob": - reward = reward + 1 - elif g.category == "factory": - reward = reward + 10 - elif g.category == "comms": - reward = reward + 10 - elif g.category == "oil": - reward = reward + 10 + if g.category in REWARDS.keys(): + reward = reward + REWARDS[g.category] return reward else: return reward @@ -197,9 +181,9 @@ class Game: else: return event.name == self.player_name - def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint]=None): + def pass_turn(self, no_action=False, ignored_cps: typing.Collection[ControlPoint] = None): logging.info("Pass turn") - + self.informations.append(Information("End of turn #" + str(self.turn), "-" * 40, 0)) self.turn = self.turn + 1 for event in self.events: @@ -210,7 +194,6 @@ class Game: else: event.skip() - self._enemy_reinforcement() self._budget_player() @@ -246,34 +229,17 @@ class Game: gplanner.plan_groundwar() self.ground_planners[cp.id] = gplanner - - def _enemy_reinforcement(self): - MAX_ARMOR = 50 * self.settings.multiplier - MAX_AIRCRAFT = 50 * self.settings.multiplier + MAX_ARMOR = 30 * self.settings.multiplier + MAX_AIRCRAFT = 25 * self.settings.multiplier - production = 0 + production = 0.0 for enemy_point in self.theater.enemy_points(): for g in enemy_point.ground_objects: - if g.category == "power": - production = production + 4 - elif g.category == "warehouse": - production = production + 2 - elif g.category == "fuel": - production = production + 2 - elif g.category == "ammo": - production = production + 2 - elif g.category == "farp": - production = production + 1 - elif g.category == "fob": - production = production + 1 - elif g.category == "factory": - production = production + 10 - elif g.category == "comms": - production = production + 8 - elif g.category == "oil": - production = production + 8 + if g.category in REWARDS.keys(): + production = production + REWARDS[g.category] + production = production * 0.75 budget_for_armored_units = production / 2 budget_for_aircraft = production / 2 @@ -302,7 +268,7 @@ class Game: if target_cp.base.total_armor >= MAX_ARMOR: continue unit = random.choice(potential_units) - price = db.PRICES[unit]*2 + price = db.PRICES[unit] * 2 budget_for_armored_units -= price * 2 target_cp.base.armor[unit] = target_cp.base.armor.get(unit, 0) + 2 info = Information("Enemy Reinforcement", unit.id + " x 2 at " + target_cp.name, self.turn) @@ -312,7 +278,8 @@ class Game: if budget_for_armored_units > 0: budget_for_aircraft += budget_for_armored_units - potential_units = [u for u in db.FACTIONS[self.enemy_name]["units"] if u in db.UNIT_BY_TASK[CAS] or u in db.UNIT_BY_TASK[CAP]] + potential_units = [u for u in db.FACTIONS[self.enemy_name]["units"] if + u in db.UNIT_BY_TASK[CAS] or u in db.UNIT_BY_TASK[CAP]] if len(potential_units) > 0 and len(potential_cp_armor) > 0: while budget_for_aircraft > 0: i = i + 1 @@ -322,22 +289,20 @@ class Game: if target_cp.base.total_planes >= MAX_AIRCRAFT: continue unit = random.choice(potential_units) - price = db.PRICES[unit]*2 + price = db.PRICES[unit] * 2 budget_for_aircraft -= price * 2 target_cp.base.aircraft[unit] = target_cp.base.aircraft.get(unit, 0) + 2 info = Information("Enemy Reinforcement", unit.id + " x 2 at " + target_cp.name, self.turn) print(str(info)) self.informations.append(info) - - @property def current_turn_daytime(self): return ["dawn", "day", "dusk", "night"][self.turn % 4] @property def current_day(self): - return self.date + timedelta(days=self.turn//4) + return self.date + timedelta(days=self.turn // 4) def next_unit_id(self): """ diff --git a/gen/flights/flight.py b/gen/flights/flight.py index 654c080b..b6e6ea68 100644 --- a/gen/flights/flight.py +++ b/gen/flights/flight.py @@ -39,6 +39,8 @@ class FlightWaypoint(): self.name = "" self.description = "" self.targets = [] + self.obj_name = "" + self.pretty_name = "" class Flight: diff --git a/gen/naming.py b/gen/naming.py index 59a04118..ab2c9e60 100644 --- a/gen/naming.py +++ b/gen/naming.py @@ -1,9 +1,48 @@ from game import db +import random +ALPHA_MILITARY = ["Alpha","Bravo","Charlie","Delta","Echo","Foxtrot", + "Golf","Hotel","India","Juliet","Kilo","Lima","Mike", + "November","Oscar","Papa","Quebec","Romeo","Sierra", + "Tango","Uniform","Victor","Whisky","XRay","Yankee", + "Zulu","Zero"] class NameGenerator: number = 0 + ANIMALS = [ + "SHARK", "TORTOISE", "BAT", "PANGOLIN", "AARDWOLF", + "MONKEY", "BUFFALO", "DOG", "BOBCAT", "LYNX", "PANTHER", "TIGER", + "LION", "OWL", "BUTTERFLY", "BISON", "DUCK", "COBRA", "MAMBA", + "DOLPHIN", "PHEASANT", "ARMADILLLO", "RACOON", "ZEBRA", "COW", "COYOTE", "FOX", + "LIGHTFOOT", "COTTONMOUTH", "TAURUS", "VIPER", "CASTOR", "GIRAFFE", "SNAKE", + "MONSTER", "ALBATROSS", "HAWK", "DOVE", "MOCKINGBIRD", "GECKO", "ORYX", "GORILLA", + "HARAMBE", "GOOSE", "MAVERICK", "HARE", "JACKAL", "LEOPARD", "CAT", "MUSK", "ORCA", + "OCELOT", "BEAR", "PANDA", "GULL", "PENGUIN", "PYTHON", "RAVEN", "DEER", "MOOSE", + "REINDEER", "SHEEP", "GAZELLE", "INSECT", "VULTURE", "WALLABY", "KANGAROO", "KOALA", + "KIWI", "WHALE", "FISH", "RHINO", "HIPPO", "RAT", "WOODPECKER", "WORM", "BABOON", + "YAK", "SCORPIO", "HORSE", "POODLE", "CENTIPEDE", "CHICKEN", "CHEETAH", "CHAMELEON", + "CATFISH", "CATERPILLAR", "CARACAL", "CAMEL", "CAIMAN", "BARRACUDA", "BANDICOOT", + "ALLIGATOR", "BONGO", "CORAL", "ELEPHANT", "ANTELOPE", "CRAB", "DACHSHUND", "DODO", + "FLAMINGO", "FERRET", "FALCON", "BULLDOG", "DONKEY", "IGUANA", "TAMARIN", "HARRIER", + "GRIZZLY", "GREYHOUND", "GRASSHOPPER", "JAGUAR", "LADYBUG", "KOMODO", "DRAGON", "LIZARD", + "LLAMA", "LOBSTER", "OCTOPUS", "MANATEE", "MAGPIE", "MACAW", "OSTRICH", "OYSTER", + "MOLE", "MULE", "MOTH", "MONGOOSE", "MOLLY", "MEERKAT", "MOUSE", "PEACOCK", "PIKE", "ROBIN", + "RAGDOLL", "PLATYPUS", "PELICAN", "PARROT", "PORCUPINE", "PIRANHA", "PUMA", "PUG", "TAPIR", + "TERMITE", "URCHIN", "SHRIMP", "TURKEY", "TOUCAN", "TETRA", "HUSKY", "STARFISH", "SWAN", + "FROG", "SQUIRREL", "WALRUS", "WARTHOG", "CORGI", "WEASEL", "WOMBAT", "WOLVERINE", "MAMMOTH", + "TOAD", "WOLF", "ZEBU", "SEAL", "SKATE", "JELLYFISH", "MOSQUITO", "LOCUST", "SLUG", "SNAIL", + "HEDGEHOG", "PIGLET", "FENNEC", "BADGER", "ALPACA" + ] + + def __init__(self): + self.number = 0 + self.ANIMALS = NameGenerator.ANIMALS.copy() + + def reset(self): + self.number = 0 + self.ANIMALS = NameGenerator.ANIMALS.copy() + def next_unit_name(self, country, parent_base_id, unit_type): self.number += 1 return "unit|{}|{}|{}|{}|".format(country.id, self.number, parent_base_id, db.unit_type_name(unit_type)) @@ -27,6 +66,16 @@ class NameGenerator: self.number += 1 return "carrier|{}|{}|0|".format(country.id, self.number) + def random_objective_name(self): + if len(self.ANIMALS) == 0: + random.choice(ALPHA_MILITARY).upper() + " #" + str(random.randint(0, 100)) + else: + animal = random.choice(self.ANIMALS) + self.ANIMALS.remove(animal) + return animal + namegen = NameGenerator() + + diff --git a/qt_ui/uiconstants.py b/qt_ui/uiconstants.py index 1c40423b..63e8ccb2 100644 --- a/qt_ui/uiconstants.py +++ b/qt_ui/uiconstants.py @@ -88,12 +88,10 @@ EVENT_ICONS: Dict[str, QPixmap] = {} def load_event_icons(): - for category, image in { - "strike": "strike", - FrontlineAttackEvent: "attack", - UnitsDeliveryEvent: "delivery"}.items(): - EVENT_ICONS[category] = QPixmap("./resources/ui/events/" + image + ".png") - + for image in os.listdir("./resources/ui/events/"): + print(image) + if image.endswith(".PNG"): + EVENT_ICONS[image[:-4]] = QPixmap(os.path.join("./resources/ui/events/", image)) def load_aircraft_icons(): for aircraft in os.listdir("./resources/ui/units/aircrafts/"): diff --git a/qt_ui/widgets/QBudgetBox.py b/qt_ui/widgets/QBudgetBox.py index f8157b4b..27233559 100644 --- a/qt_ui/widgets/QBudgetBox.py +++ b/qt_ui/widgets/QBudgetBox.py @@ -1,6 +1,7 @@ -from PySide2.QtWidgets import QLabel, QHBoxLayout, QGroupBox +from PySide2.QtWidgets import QLabel, QHBoxLayout, QGroupBox, QPushButton import qt_ui.uiconstants as CONST +from qt_ui.windows.finances.QFinancesMenu import QFinancesMenu class QBudgetBox(QGroupBox): @@ -8,16 +9,22 @@ class QBudgetBox(QGroupBox): UI Component to display current budget and player's money """ - def __init__(self): + def __init__(self, game): super(QBudgetBox, self).__init__("Budget") + self.game = game self.money_icon = QLabel() self.money_icon.setPixmap(CONST.ICONS["Money"]) self.money_amount = QLabel() + self.finances = QPushButton("Details") + self.finances.setProperty("style", "btn-primary") + self.finances.clicked.connect(self.openFinances) + self.layout = QHBoxLayout() self.layout.addWidget(self.money_icon) self.layout.addWidget(self.money_amount) + self.layout.addWidget(self.finances) self.setLayout(self.layout) def setBudget(self, budget, reward): @@ -26,4 +33,12 @@ class QBudgetBox(QGroupBox): :param budget: Current money available :param reward: Planned reward for next turn """ - self.money_amount.setText(str(budget) + "M (+" + str(reward) + "M)") \ No newline at end of file + self.money_amount.setText(str(budget) + "M (+" + str(reward) + "M)") + + def setGame(self, game): + self.game = game + self.setBudget(self.game.budget, self.game.budget_reward_amount) + + def openFinances(self): + self.subwindow = QFinancesMenu(self.game) + self.subwindow.show() \ No newline at end of file diff --git a/qt_ui/widgets/QFactionsInfos.py b/qt_ui/widgets/QFactionsInfos.py index 51240ae4..79b5848d 100644 --- a/qt_ui/widgets/QFactionsInfos.py +++ b/qt_ui/widgets/QFactionsInfos.py @@ -15,6 +15,7 @@ class QFactionsInfos(QGroupBox): self.setGame(game) self.layout = QGridLayout() + self.layout.setSpacing(0) self.layout.addWidget(QLabel("Player : "),0,0) self.layout.addWidget(self.player_name,0,1) self.layout.addWidget(QLabel("Enemy : "),1,0) diff --git a/qt_ui/widgets/QPredefinedWaypointSelectionComboBox.py b/qt_ui/widgets/QPredefinedWaypointSelectionComboBox.py new file mode 100644 index 00000000..ef8cdcaa --- /dev/null +++ b/qt_ui/widgets/QPredefinedWaypointSelectionComboBox.py @@ -0,0 +1,146 @@ +from PySide2.QtCore import QSortFilterProxyModel, Qt, QModelIndex +from PySide2.QtGui import QStandardItem, QStandardItemModel +from PySide2.QtWidgets import QComboBox, QCompleter +from game import Game +from gen.flights.flight import FlightWaypoint +from theater import ControlPointType + + +class QPredefinedWaypointSelectionComboBox(QComboBox): + + def __init__(self, game: Game, parent=None): + super(QPredefinedWaypointSelectionComboBox, self).__init__(parent) + + self.game = game + self.setFocusPolicy(Qt.StrongFocus) + self.setEditable(True) + self.completer = QCompleter(self) + + # always show all completions + self.completer.setCompletionMode(QCompleter.UnfilteredPopupCompletion) + self.pFilterModel = QSortFilterProxyModel(self) + self.pFilterModel.setFilterCaseSensitivity(Qt.CaseInsensitive) + + self.completer.setPopup(self.view()) + + self.setCompleter(self.completer) + + self.lineEdit().textEdited.connect(self.pFilterModel.setFilterFixedString) + self.completer.activated.connect(self.setTextIfCompleterIsClicked) + + self.find_possible_waypoints() + + def setModel(self, model): + super(QPredefinedWaypointSelectionComboBox, self).setModel(model) + self.pFilterModel.setSourceModel(model) + self.completer.setModel(self.pFilterModel) + + def setModelColumn(self, column): + self.completer.setCompletionColumn(column) + self.pFilterModel.setFilterKeyColumn(column) + super(QPredefinedWaypointSelectionComboBox, self).setModelColumn(column) + + def view(self): + return self.completer.popup() + + def index(self): + return self.currentIndex() + + def setTextIfCompleterIsClicked(self, text): + if text: + index = self.findText(text) + self.setCurrentIndex(index) + + def get_selected_waypoints(self, include_all_from_same_location=False): + n = self.currentText() + + first_waypoint = None + for w in self.wpts: + if w.pretty_name == n: + first_waypoint = w + break + + if first_waypoint is None: + return [] + + waypoints = [first_waypoint] + if include_all_from_same_location: + for w in self.wpts: + if w is not first_waypoint and w.obj_name and w.obj_name == first_waypoint.obj_name: + waypoints.append(w) + + return waypoints + + def find_possible_waypoints(self): + + self.wpts = [] + model = QStandardItemModel() + i = 0 + + def add_model_item(i, model, name, wpt): + print(name) + item = QStandardItem(name) + model.setItem(i, 0, item) + self.wpts.append(wpt) + return i + 1 + + for cp in self.game.theater.controlpoints: + print(cp) + if cp.captured: + enemy_cp = [ecp for ecp in cp.connected_points if ecp.captured != cp.captured] + for ecp in enemy_cp: + wpt = FlightWaypoint((cp.position.x + ecp.position.x)/2, (cp.position.y + ecp.position.y)/2, 800) + wpt.name = "Frontline " + cp.name + "/" + ecp.name + " [CAS]" + wpt.pretty_name = wpt.name + wpt.description = "Frontline" + i = add_model_item(i, model, wpt.pretty_name, wpt) + + + for cp in self.game.theater.controlpoints: + for ground_object in cp.ground_objects: + if not ground_object.is_dead and not ground_object.dcs_identifier == "AA": + wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0) + wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + ground_object.category + " #" + str(ground_object.object_id) + wpt.pretty_name = wpt.name + wpt.obj_name = ground_object.obj_name + if cp.captured: + wpt.description = "Friendly Building" + else: + wpt.description = "Enemy Building" + i = add_model_item(i, model, wpt.pretty_name, wpt) + + for cp in self.game.theater.controlpoints: + + for ground_object in cp.ground_objects: + if not ground_object.is_dead and ground_object.dcs_identifier == "AA": + for g in ground_object.groups: + for j, u in enumerate(g.units): + wpt = FlightWaypoint(u.position.x, u.position.y, 0) + wpt.name = wpt.name = "[" + str(ground_object.obj_name) + "] : " + u.type + " #" + str(j) + wpt.pretty_name = wpt.name + wpt.obj_name = ground_object.obj_name + if cp.captured: + wpt.description = "Friendly unit : " + u.type + else: + wpt.description = "Enemy unit : " + u.type + i = add_model_item(i, model, wpt.pretty_name, wpt) + + for cp in self.game.theater.controlpoints: + + wpt = FlightWaypoint(cp.position.x, cp.position.y, 0) + wpt.name = cp.name + if cp.captured: + wpt.description = "Position of " + cp.name + " [Friendly Airbase]" + else: + wpt.description = "Position of " + cp.name + " [Enemy Airbase]" + + if cp.cptype == ControlPointType.AIRCRAFT_CARRIER_GROUP: + wpt.pretty_name = cp.name + " (Aircraft Carrier Group)" + elif cp.cptype == ControlPointType.LHA_GROUP: + wpt.pretty_name = cp.name + " (LHA Group)" + else: + wpt.pretty_name = cp.name + " (Airbase)" + + i = add_model_item(i, model, wpt.pretty_name, wpt) + + self.setModel(model) diff --git a/qt_ui/widgets/QTopPanel.py b/qt_ui/widgets/QTopPanel.py index 114f8097..b741f3cf 100644 --- a/qt_ui/widgets/QTopPanel.py +++ b/qt_ui/widgets/QTopPanel.py @@ -1,8 +1,9 @@ -from PySide2.QtWidgets import QFrame, QHBoxLayout, QPushButton, QVBoxLayout +from PySide2.QtWidgets import QFrame, QHBoxLayout, QPushButton, QVBoxLayout, QGroupBox from game import Game from qt_ui.widgets.QBudgetBox import QBudgetBox from qt_ui.widgets.QFactionsInfos import QFactionsInfos +from qt_ui.windows.finances.QFinancesMenu import QFinancesMenu from qt_ui.windows.stats.QStatsWindow import QStatsWindow from qt_ui.widgets.QTurnCounter import QTurnCounter @@ -17,13 +18,14 @@ class QTopPanel(QFrame): def __init__(self, game: Game): super(QTopPanel, self).__init__() self.game = game + self.setMaximumHeight(70) self.init_ui() GameUpdateSignal.get_instance().gameupdated.connect(self.setGame) def init_ui(self): self.turnCounter = QTurnCounter() - self.budgetBox = QBudgetBox() + self.budgetBox = QBudgetBox(self.game) self.passTurnButton = QPushButton("Pass Turn") self.passTurnButton.setIcon(CONST.ICONS["PassTurn"]) @@ -36,14 +38,9 @@ class QTopPanel(QFrame): self.proceedButton.clicked.connect(self.proceed) if self.game and self.game.turn == 0: self.proceedButton.setEnabled(False) - elif not len(self.game.planners.keys()) == self.game.theater.controlpoints: - self.proceedButton.setEnabled(False) - else: - self.proceedButton.setEnabled(True) self.factionsInfos = QFactionsInfos(self.game) - self.submenus = QVBoxLayout() self.settings = QPushButton("Settings") self.settings.setIcon(CONST.ICONS["Settings"]) self.settings.setProperty("style", "btn-primary") @@ -54,28 +51,40 @@ class QTopPanel(QFrame): self.statistics.setProperty("style", "btn-primary") self.statistics.clicked.connect(self.openStatisticsWindow) - self.submenus.addWidget(self.settings) - self.submenus.addWidget(self.statistics) + self.buttonBox = QGroupBox("Misc") + self.buttonBoxLayout = QHBoxLayout() + self.buttonBoxLayout.addWidget(self.settings) + self.buttonBoxLayout.addWidget(self.statistics) + self.buttonBox.setLayout(self.buttonBoxLayout) + + self.proceedBox = QGroupBox("Proceed") + self.proceedBoxLayout = QHBoxLayout() + self.proceedBoxLayout.addWidget(self.passTurnButton) + self.proceedBoxLayout.addWidget(self.proceedButton) + self.proceedBox.setLayout(self.proceedBoxLayout) self.layout = QHBoxLayout() self.layout.addWidget(self.factionsInfos) self.layout.addWidget(self.turnCounter) self.layout.addWidget(self.budgetBox) - self.layout.addLayout(self.submenus) + self.layout.addWidget(self.buttonBox) self.layout.addStretch(1) - self.layout.addWidget(self.passTurnButton) - self.layout.addWidget(self.proceedButton) + self.layout.addWidget(self.proceedBox) + + self.layout.setContentsMargins(0,0,0,0) self.setLayout(self.layout) def setGame(self, game:Game): self.game = game if game is not None: self.turnCounter.setCurrentTurn(self.game.turn, self.game.current_day) - self.budgetBox.setBudget(self.game.budget, self.game.budget_reward_amount) + self.budgetBox.setGame(self.game) self.factionsInfos.setGame(self.game) - if not len(self.game.planners.keys()) == self.game.theater.controlpoints: + if not len(self.game.planners.keys()) == len(self.game.theater.controlpoints): self.proceedButton.setEnabled(False) + else: + self.proceedButton.setEnabled(True) def openSettings(self): self.subwindow = QSettingsWindow(self.game) diff --git a/qt_ui/widgets/map/QLiberationMap.py b/qt_ui/widgets/map/QLiberationMap.py index 25f6e0e2..19c6505c 100644 --- a/qt_ui/widgets/map/QLiberationMap.py +++ b/qt_ui/widgets/map/QLiberationMap.py @@ -76,13 +76,6 @@ class QLiberationMap(QGraphicsView): scene.addItem(QMapControlPoint(self, pos[0] - CONST.CP_SIZE / 2, pos[1] - CONST.CP_SIZE / 2, CONST.CP_SIZE, CONST.CP_SIZE, cp, self.game)) - text = scene.addText(cp.name, font=QFont("Trebuchet MS", 10, weight=5, italic=False)) - text.setPos(pos[0] + CONST.CP_SIZE, pos[1] - CONST.CP_SIZE / 2) - - text = scene.addText(cp.name, font=QFont("Trebuchet MS", 10, weight=5, italic=False)) - text.setDefaultTextColor(Qt.white) - text.setPos(pos[0] + CONST.CP_SIZE + 1, pos[1] - CONST.CP_SIZE / 2 + 1) - if cp.captured: pen = QPen(brush=CONST.COLORS["blue"]) @@ -105,7 +98,7 @@ class QLiberationMap(QGraphicsView): go_pos = self._transform_point(ground_object.position) if not ground_object.airbase_group: - scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 16, 16, cp, ground_object)) + scene.addItem(QMapGroundObject(self, go_pos[0], go_pos[1], 12, 12, cp, ground_object)) if ground_object.category == "aa" and self.get_display_rule("sam"): max_range = 0 @@ -126,6 +119,7 @@ class QLiberationMap(QGraphicsView): self.scene_create_lines_for_cp(cp) for cp in self.game.theater.controlpoints: + pos = self._transform_point(cp.position) if self.get_display_rule("flight_paths"): if cp.id in self.game.planners.keys(): planner = self.game.planners[cp.id] @@ -139,6 +133,14 @@ class QLiberationMap(QGraphicsView): prev_pos = list(new_pos) scene.addLine(prev_pos[0] + 2, prev_pos[1] + 2, pos[0] + 2, pos[1] + 2, flight_path_pen) + for cp in self.game.theater.controlpoints: + pos = self._transform_point(cp.position) + text = scene.addText(cp.name, font=QFont("Trebuchet MS", 10, weight=5, italic=False)) + text.setPos(pos[0] + CONST.CP_SIZE, pos[1] - CONST.CP_SIZE / 2) + text = scene.addText(cp.name, font=QFont("Trebuchet MS", 10, weight=5, italic=False)) + text.setDefaultTextColor(Qt.white) + text.setPos(pos[0] + CONST.CP_SIZE + 1, pos[1] - CONST.CP_SIZE / 2 + 1) + def scene_create_lines_for_cp(self, cp: ControlPoint): scene = self.scene() pos = self._transform_point(cp.position) diff --git a/qt_ui/widgets/map/QMapGroundObject.py b/qt_ui/widgets/map/QMapGroundObject.py index 9af06fee..357c2755 100644 --- a/qt_ui/widgets/map/QMapGroundObject.py +++ b/qt_ui/widgets/map/QMapGroundObject.py @@ -27,12 +27,12 @@ class QMapGroundObject(QGraphicsRectItem): units[u.type] = units[u.type]+1 else: units[u.type] = 1 - tooltip = "" + tooltip = "[" + self.model.obj_name + "]" + "\n" for unit in units.keys(): tooltip = tooltip + str(unit) + "x" + str(units[unit]) + "\n" self.setToolTip(tooltip[:-1]) else: - self.setToolTip(cp.name + "'s " + self.model.category) + self.setToolTip("[" + self.model.obj_name + "] : " + self.model.category) def paint(self, painter, option, widget=None): diff --git a/qt_ui/windows/QLiberationWindow.py b/qt_ui/windows/QLiberationWindow.py index f2332caf..31ba3bac 100644 --- a/qt_ui/windows/QLiberationWindow.py +++ b/qt_ui/windows/QLiberationWindow.py @@ -4,7 +4,8 @@ import webbrowser from PySide2 import QtGui from PySide2.QtCore import Qt from PySide2.QtGui import QIcon -from PySide2.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget +from PySide2.QtWidgets import QWidget, QHBoxLayout, QVBoxLayout, QMainWindow, QAction, QMessageBox, QDesktopWidget, \ + QSplitter import qt_ui.uiconstants as CONST from game import Game @@ -14,6 +15,7 @@ from qt_ui.widgets.map.QLiberationMap import QLiberationMap from qt_ui.windows.GameUpdateSignal import GameUpdateSignal, DebriefingSignal from qt_ui.windows.QDebriefingWindow import QDebriefingWindow from qt_ui.windows.QNewGameWizard import NewGameWizard +from qt_ui.windows.infos.QInfoPanel import QInfoPanel from userdata import persistency @@ -22,6 +24,7 @@ class QLiberationWindow(QMainWindow): def __init__(self): super(QLiberationWindow, self).__init__() + self.info_panel = None self.setGame(persistency.restore_game()) self.setGeometry(300, 100, 270, 100) @@ -44,11 +47,16 @@ class QLiberationWindow(QMainWindow): def initUi(self): self.liberation_map = QLiberationMap(self.game) + self.info_panel = QInfoPanel(self.game) + + hbox = QSplitter(Qt.Horizontal) + hbox.addWidget(self.info_panel) + hbox.addWidget(self.liberation_map) vbox = QVBoxLayout() vbox.setMargin(0) vbox.addWidget(QTopPanel(self.game)) - vbox.addWidget(self.liberation_map) + vbox.addWidget(hbox) central_widget = QWidget() central_widget.setLayout(vbox) @@ -164,6 +172,8 @@ class QLiberationWindow(QMainWindow): def setGame(self, game: Game): self.game = game + if self.info_panel: + self.info_panel.setGame(game) def showAboutDialog(self): text = "

DCS Liberation

" + \ diff --git a/qt_ui/windows/basemenu/QBaseMenu2.py b/qt_ui/windows/basemenu/QBaseMenu2.py index 385a0cbc..1c4ffcd6 100644 --- a/qt_ui/windows/basemenu/QBaseMenu2.py +++ b/qt_ui/windows/basemenu/QBaseMenu2.py @@ -1,20 +1,13 @@ -import traceback - from PySide2.QtCore import Qt from PySide2.QtGui import QCloseEvent -from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QVBoxLayout, QGridLayout, QPushButton, \ - QGroupBox, QSizePolicy, QSpacerItem -from dcs.unittype import UnitType +from PySide2.QtWidgets import QHBoxLayout, QLabel, QWidget, QDialog, QGridLayout -from game.event import UnitsDeliveryEvent, ControlPointType -from qt_ui.widgets.QBudgetBox import QBudgetBox -from qt_ui.widgets.base.QAirportInformation import QAirportInformation -from qt_ui.widgets.base.QBaseInformation import QBaseInformation -from qt_ui.windows.basemenu.QBaseMenuTabs import QBaseMenuTabs -from qt_ui.windows.mission.QPlannedFlightsView import QPlannedFlightsView -from qt_ui.windows.GameUpdateSignal import GameUpdateSignal -from theater import ControlPoint, CAP, Embarking, CAS, PinpointStrike, db from game import Game +from game.event import ControlPointType +from qt_ui.uiconstants import EVENT_ICONS +from qt_ui.windows.GameUpdateSignal import GameUpdateSignal +from qt_ui.windows.basemenu.QBaseMenuTabs import QBaseMenuTabs +from theater import ControlPoint class QBaseMenu2(QDialog): @@ -38,6 +31,8 @@ class QBaseMenu2(QDialog): if self.cp.captured: self.deliveryEvent = None + self.setWindowIcon(EVENT_ICONS["capture"]) + self.setWindowFlags(Qt.WindowStaysOnTopHint) self.setMinimumSize(300, 200) self.setModal(True) diff --git a/qt_ui/windows/finances/QFinancesMenu.py b/qt_ui/windows/finances/QFinancesMenu.py new file mode 100644 index 00000000..362f642c --- /dev/null +++ b/qt_ui/windows/finances/QFinancesMenu.py @@ -0,0 +1,70 @@ +from PySide2.QtWidgets import QDialog, QGridLayout, QLabel, QFrame, QSizePolicy + +import qt_ui.uiconstants as CONST +from game.db import REWARDS, PLAYER_BUDGET_BASE +from game.game import Game + + +class QHorizontalSeparationLine(QFrame): + + def __init__(self): + super().__init__() + self.setMinimumWidth(1) + self.setFixedHeight(20) + self.setFrameShape(QFrame.HLine) + self.setFrameShadow(QFrame.Sunken) + self.setSizePolicy(QSizePolicy.Preferred, QSizePolicy.Minimum) + +class QFinancesMenu(QDialog): + + def __init__(self, game: Game): + super(QFinancesMenu, self).__init__() + + self.game = game + self.setModal(True) + self.setWindowTitle("Finances") + self.setWindowIcon(CONST.ICONS["Money"]) + self.setMinimumSize(450, 200) + + reward = PLAYER_BUDGET_BASE * len(self.game.theater.player_points()) + layout = QGridLayout() + layout.addWidget(QLabel("Control Points"), 0, 0) + layout.addWidget(QLabel(str(len(self.game.theater.player_points())) + " bases x " + str(PLAYER_BUDGET_BASE) + "M"), 0, 1) + layout.addWidget(QLabel(str(reward) + "M"), 0, 2) + + layout.addWidget(QHorizontalSeparationLine(), 1, 0, 1, 3) + + i = 2 + for cp in self.game.theater.player_points(): + obj_names = [] + [obj_names.append(ground_object.obj_name) for ground_object in cp.ground_objects if ground_object.obj_name not in obj_names] + for obj_name in obj_names: + reward = 0 + g = None + cat = None + number = 0 + for ground_object in cp.ground_objects: + if ground_object.obj_name != obj_name or ground_object.is_dead: + continue + else: + if g is None: + g = ground_object + cat = g.category + if cat in REWARDS.keys(): + number = number + 1 + reward += REWARDS[cat] + + if g is not None and cat in REWARDS.keys(): + layout.addWidget(QLabel("" + g.category.upper() + " [" + obj_name + "]"), i, 0) + layout.addWidget(QLabel(str(number) + " buildings x " + str(REWARDS[cat]) + "M"), i, 1) + rlabel = QLabel(str(reward) + "M") + rlabel.setProperty("style", "green") + layout.addWidget(rlabel, i, 2) + i = i + 1 + + self.setLayout(layout) + + layout.addWidget(QHorizontalSeparationLine(), i+1, 0, 1, 3) + + layout.addWidget(QLabel("" + str(self.game.budget_reward_amount) + "M "), i+2, 2) + diff --git a/qt_ui/windows/infos/QInfoItem.py b/qt_ui/windows/infos/QInfoItem.py new file mode 100644 index 00000000..be5cf333 --- /dev/null +++ b/qt_ui/windows/infos/QInfoItem.py @@ -0,0 +1,12 @@ +from PySide2.QtGui import QStandardItem + +from game.infos.information import Information + + +class QInfoItem(QStandardItem): + + def __init__(self, info: Information): + super(QInfoItem, self).__init__() + self.info = info + self.setText("[%02d]" % self.info.turn + " " + self.info.title + ' : {:<16}'.format(info.text)) + self.setEditable(False) diff --git a/qt_ui/windows/infos/QInfoList.py b/qt_ui/windows/infos/QInfoList.py new file mode 100644 index 00000000..e2f934c0 --- /dev/null +++ b/qt_ui/windows/infos/QInfoList.py @@ -0,0 +1,33 @@ +from PySide2.QtCore import QItemSelectionModel, QPoint +from PySide2.QtGui import QStandardItemModel +from PySide2.QtWidgets import QListView + +from game import Game, game +from qt_ui.windows.infos.QInfoItem import QInfoItem + + +class QInfoList(QListView): + + def __init__(self, game:Game): + super(QInfoList, self).__init__() + self.model = QStandardItemModel(self) + self.setModel(self.model) + self.game = game + self.update_list() + + self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select) + self.selectionModel().selectionChanged.connect(self.on_selected_info_changed) + + def on_selected_info_changed(self): + index = self.selectionModel().currentIndex().row() + + def update_list(self): + self.model.clear() + if self.game is not None: + for i, info in enumerate(reversed(self.game.informations)): + self.model.appendRow(QInfoItem(info)) + self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select) + + def setGame(self, game): + self.game = game + self.update_list() \ No newline at end of file diff --git a/qt_ui/windows/infos/QInfoPanel.py b/qt_ui/windows/infos/QInfoPanel.py new file mode 100644 index 00000000..a7a81d56 --- /dev/null +++ b/qt_ui/windows/infos/QInfoPanel.py @@ -0,0 +1,28 @@ +from PySide2.QtWidgets import QFrame, QVBoxLayout, QLabel, QGroupBox + +from game import Game +from qt_ui.windows.infos.QInfoList import QInfoList + + +class QInfoPanel(QGroupBox): + + def __init__(self, game: Game): + super(QInfoPanel, self).__init__("Info Panel") + self.informations_list = QInfoList(game) + self.init_ui() + + def setGame(self, game): + self.game = game + self.informations_list.setGame(game) + + def update(self): + self.informations_list.update_list() + + def init_ui(self): + layout = QVBoxLayout() + layout.addWidget(self.informations_list) + layout.setSpacing(0) + layout.setContentsMargins(0, 0, 0, 0) + self.setLayout(layout) + + diff --git a/qt_ui/windows/infos/QInfoWidget.py b/qt_ui/windows/infos/QInfoWidget.py new file mode 100644 index 00000000..dfa8336a --- /dev/null +++ b/qt_ui/windows/infos/QInfoWidget.py @@ -0,0 +1,21 @@ +from PySide2.QtWidgets import QFrame, QLabel, QGridLayout + +from game.infos.information import Information + + +class QInfoWidget(QFrame): + + def __init__(self, info: Information): + super(QInfoWidget, self).__init__() + self.info = info + self.titleLabel = QLabel("" + info.title + "") + self.textLabel = QLabel(info.text) + self.init_ui() + + def init_ui(self): + layout = QGridLayout() + layout.addWidget(self.titleLabel,0,0) + layout.addWidget(self.textLabel,1,0) + self.setLayout(layout) + + diff --git a/qt_ui/windows/mission/QMissionPlanning.py b/qt_ui/windows/mission/QMissionPlanning.py index eaef78f0..7c2d8559 100644 --- a/qt_ui/windows/mission/QMissionPlanning.py +++ b/qt_ui/windows/mission/QMissionPlanning.py @@ -16,7 +16,7 @@ class QMissionPlanning(QDialog): super(QMissionPlanning, self).__init__() self.game = game self.setWindowFlags(Qt.WindowStaysOnTopHint) - self.setMinimumSize(750, 420) + self.setMinimumSize(800, 420) self.setModal(True) self.setWindowTitle("Mission Preparation") self.setWindowIcon(EVENT_ICONS["strike"]) @@ -133,8 +133,3 @@ class QMissionPlanning(QDialog): waiting = QWaitingForMissionResultWindow(self.gameEvent, self.game) waiting.show() self.close() - - - - - diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointInfoBox.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointInfoBox.py index b28d082a..ee72d8c0 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointInfoBox.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointInfoBox.py @@ -5,7 +5,7 @@ from gen.flights.flight import FlightWaypoint class QFlightWaypointInfoBox(QGroupBox): - def __init__(self, flight_wpt:FlightWaypoint): + def __init__(self, flight_wpt:FlightWaypoint = None): super(QFlightWaypointInfoBox, self).__init__("Waypoint") self.flight_wpt = flight_wpt if flight_wpt is None: @@ -22,31 +22,31 @@ class QFlightWaypointInfoBox(QGroupBox): layout = QVBoxLayout() x_pos_layout = QHBoxLayout() - x_pos_layout.addWidget(QLabel("X : ")) + x_pos_layout.addWidget(QLabel("X : ")) x_pos_layout.addWidget(self.x_position_label) x_pos_layout.addStretch() y_pos_layout = QHBoxLayout() - y_pos_layout.addWidget(QLabel("Y : ")) + y_pos_layout.addWidget(QLabel("Y : ")) y_pos_layout.addWidget(self.y_position_label) y_pos_layout.addStretch() alt_layout = QHBoxLayout() - alt_layout.addWidget(QLabel("Alt : ")) + alt_layout.addWidget(QLabel("Alt : ")) alt_layout.addWidget(self.alt_label) alt_layout.addStretch() name_layout = QHBoxLayout() - name_layout.addWidget(QLabel("Name : ")) + name_layout.addWidget(QLabel("Name : ")) name_layout.addWidget(self.name_label) name_layout.addStretch() desc_layout = QHBoxLayout() - desc_layout.addWidget(QLabel("Description : ")) + desc_layout.addWidget(QLabel("Description : ")) desc_layout.addWidget(self.desc_label) desc_layout.addStretch() - layout.addLayout(name_layout) + #layout.addLayout(name_layout) layout.addLayout(x_pos_layout) layout.addLayout(y_pos_layout) layout.addLayout(alt_layout) diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointItem.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointItem.py index b1410fd5..164b2966 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointItem.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointItem.py @@ -7,7 +7,9 @@ from gen.flights.flight import FlightWaypoint class QWaypointItem(QStandardItem): - def __init__(self, point: FlightWaypoint): + def __init__(self, point: FlightWaypoint, number): super(QWaypointItem, self).__init__() - self.setText('{0: <16}'.format(point.description) + " -- [X: " + str(int(point.x)) + "; Y: " + str(int(point.y)) + "; Alt: " + str(int(point.alt)) + "m]") + self.number = number + self.setText("%02d" % self.number + ' | ' + '{:<16}'.format(point.pretty_name)) + self.setEditable(False) diff --git a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py index 64777d49..c3521f33 100644 --- a/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py +++ b/qt_ui/windows/mission/flight/waypoints/QFlightWaypointList.py @@ -28,7 +28,8 @@ class QFlightWaypointList(QListView): self.model.clear() takeoff = FlightWaypoint(self.flight.from_cp.position.x, self.flight.from_cp.position.y, 0) takeoff.description = "Take Off" - self.model.appendRow(QWaypointItem(takeoff)) + takeoff.name = takeoff.pretty_name = "Take Off from " + self.flight.from_cp.name + self.model.appendRow(QWaypointItem(takeoff, 0)) for i, point in enumerate(self.flight.points): - self.model.appendRow(QWaypointItem(point)) + self.model.appendRow(QWaypointItem(point, i + 1)) self.selectionModel().setCurrentIndex(self.indexAt(QPoint(1, 1)), QItemSelectionModel.Select) \ No newline at end of file diff --git a/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py b/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py index 093db2d7..f2f4323e 100644 --- a/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py +++ b/qt_ui/windows/mission/flight/waypoints/QPredefinedWaypointSelectionWindow.py @@ -1,11 +1,13 @@ from PySide2.QtCore import Qt -from PySide2.QtWidgets import QDialog, QGridLayout, QLabel, QComboBox, QHBoxLayout, QVBoxLayout, QPushButton +from PySide2.QtWidgets import QDialog, QGridLayout, QLabel, QComboBox, QHBoxLayout, QVBoxLayout, QPushButton, QCheckBox from dcs import Point from game import Game from gen.flights.flight import Flight, FlightWaypoint from qt_ui.uiconstants import EVENT_ICONS +from qt_ui.widgets.QPredefinedWaypointSelectionComboBox import QPredefinedWaypointSelectionComboBox from qt_ui.windows.mission.flight.waypoints.QFlightWaypointInfoBox import QFlightWaypointInfoBox +from theater import ControlPointType PREDEFINED_WAYPOINT_CATEGORIES = [ "Frontline (CAS AREA)", @@ -23,135 +25,64 @@ class QPredefinedWaypointSelectionWindow(QDialog): self.game = game self.flight = flight self.setWindowFlags(Qt.WindowStaysOnTopHint) - self.setMinimumSize(450, 350) + self.setMinimumSize(400, 250) self.setModal(True) self.setWindowTitle("Add Predefined Waypoint") self.setWindowIcon(EVENT_ICONS["strike"]) self.flight_waypoint_list = flight_waypoint_list - self.selected_cp = self.game.theater.controlpoints[0] - self.cp_selection_box = QComboBox() - for cp in self.game.theater.controlpoints: - self.cp_selection_box.addItem(cp.name) - - self.wpt_type_selection_box = QComboBox() - for cat in PREDEFINED_WAYPOINT_CATEGORIES: - self.wpt_type_selection_box.addItem(cat) - - self.cp_selection_box.currentTextChanged.connect(self.on_parameters_changed) - self.wpt_type_selection_box.currentTextChanged.connect(self.on_parameters_changed) - - self.wpt_selection_box = QComboBox() + self.wpt_selection_box = QPredefinedWaypointSelectionComboBox(self.game) self.wpt_selection_box.setMinimumWidth(200) self.wpt_selection_box.currentTextChanged.connect(self.on_select_wpt_changed) - - self.selected_waypoint = None - self.wpt_info = QFlightWaypointInfoBox(self.selected_waypoint) + self.selected_waypoints = [] + self.wpt_info = QFlightWaypointInfoBox() self.add_button = QPushButton("Add") self.add_button.clicked.connect(self.add_waypoint) + self.include_all = QCheckBox() + self.include_all.stateChanged.connect(self.on_select_wpt_changed) + self.include_all.setChecked(True) + self.init_ui() - self.on_parameters_changed() + self.on_select_wpt_changed() print("DONE") def init_ui(self): layout = QVBoxLayout() - near_layout = QHBoxLayout() - near_layout.addWidget(QLabel("Near : ")) - near_layout.addWidget(self.cp_selection_box) - near_layout.addStretch() - - type_layout = QHBoxLayout() - type_layout.addWidget(QLabel("Type : ")) - type_layout.addWidget(self.wpt_type_selection_box) - type_layout.addStretch() - wpt_layout = QHBoxLayout() wpt_layout.addWidget(QLabel("Waypoint : ")) wpt_layout.addWidget(self.wpt_selection_box) wpt_layout.addStretch() - layout.addLayout(near_layout) - layout.addLayout(type_layout) + include_all = QHBoxLayout() + include_all.addWidget(QLabel("Include all objects from the same location : ")) + include_all.addWidget(self.include_all) + include_all.addStretch() + layout.addLayout(wpt_layout) layout.addWidget(self.wpt_info) + layout.addLayout(include_all) layout.addStretch() layout.addWidget(self.add_button) self.setLayout(layout) def on_select_wpt_changed(self): - self.selected_waypoint = self.wpt_selection_box.currentData() - self.wpt_info.set_flight_waypoint(self.selected_waypoint) - if self.selected_waypoint is None: + self.selected_waypoints = self.wpt_selection_box.get_selected_waypoints(self.include_all.isChecked()) + if self.selected_waypoints is None or len(self.selected_waypoints) <= 0: self.add_button.setDisabled(True) else: + self.wpt_info.set_flight_waypoint(self.selected_waypoints[0]) self.add_button.setDisabled(False) - def on_parameters_changed(self): - self.wpt_selection_box.clear() - - select_cp_text = self.cp_selection_box.currentText() - select_cp = None - for cp in self.game.theater.controlpoints: - if cp.name == select_cp_text: - select_cp = cp - break - if select_cp is not None: - selected_wpt_type = self.wpt_type_selection_box.currentText() - - if selected_wpt_type == PREDEFINED_WAYPOINT_CATEGORIES[0]: # CAS - enemy_cp = [cp for cp in select_cp.connected_points if cp.captured != select_cp.captured] - for ecp in enemy_cp: - wpt = FlightWaypoint((select_cp.position.x + ecp.position.x)/2, (select_cp.position.y + ecp.position.y)/2, 800) - wpt.name = "Frontline with " + ecp.name + " [CAS]" - wpt.description = "Frontline" - self.wpt_selection_box.addItem(wpt.name, userData=wpt) - if len(enemy_cp) == 0: - self.wpt_selection_box.addItem("None", userData=None) - elif selected_wpt_type == PREDEFINED_WAYPOINT_CATEGORIES[1]: # Building - for ground_object in select_cp.ground_objects: - if not ground_object.is_dead and not ground_object.dcs_identifier == "AA": - wpt = FlightWaypoint(ground_object.position.x,ground_object.position.y, 0) - wpt.name = ground_object.category + " #" + str(ground_object.object_id) + " @ site #" + str(ground_object.group_id) - if select_cp.captured: - wpt.description = "Friendly Building" - else: - wpt.description = "Enemy Building" - self.wpt_selection_box.addItem(wpt.name, userData=wpt) - elif selected_wpt_type == PREDEFINED_WAYPOINT_CATEGORIES[2]: # Known units position - for ground_object in select_cp.ground_objects: - if not ground_object.is_dead and ground_object.dcs_identifier == "AA": - for g in ground_object.groups: - for u in g.units: - wpt = FlightWaypoint(ground_object.position.x, ground_object.position.y, 0) - wpt.name = u.type + " @ site #" + str(ground_object.group_id) - if select_cp.captured: - wpt.description = "Friendly unit :" + u.type - else: - wpt.description = "Enemy unit :" + u.type - self.wpt_selection_box.addItem(wpt.name, userData=wpt) - elif selected_wpt_type == PREDEFINED_WAYPOINT_CATEGORIES[3]: # CAS - wpt = FlightWaypoint(select_cp.position.x, select_cp.position.y, 0) - wpt.name = select_cp.name - if select_cp.captured: - wpt.description = "Position of " + select_cp.name + " [Friendly Airbase]" - else: - wpt.description = "Position of " + select_cp.name + " [Enemy Airbase]" - self.wpt_selection_box.addItem("Airbase", userData=wpt) - else: - self.wpt_selection_box.addItem("None", userData=None) - else: - self.wpt_selection_box.addItem("None", userData=None) - - self.wpt_selection_box.setCurrentIndex(0) - def add_waypoint(self): - if not self.selected_waypoint is None: - self.flight.points.append(self.selected_waypoint) + + for wpt in self.selected_waypoints: + self.flight.points.append(wpt) + self.flight_waypoint_list.update_list() self.close() diff --git a/resources/ui/ground_assets/destroyed.png b/resources/ui/ground_assets/destroyed.png new file mode 100644 index 00000000..be8d5262 Binary files /dev/null and b/resources/ui/ground_assets/destroyed.png differ diff --git a/theater/persiangulf.py b/theater/persiangulf.py index 0efebf8e..74eb0608 100644 --- a/theater/persiangulf.py +++ b/theater/persiangulf.py @@ -166,16 +166,13 @@ class Emirates(ConflictTheater): def __init__(self): super(Emirates, self).__init__() - self.al_dhafra = ControlPoint.from_airport(persiangulf.Al_Dhafra_AB, LAND, SIZE_BIG, IMPORTANCE_LOW) + self.al_dhafra = ControlPoint.from_airport(persiangulf.Al_Dhafra_AB, LAND, SIZE_BIG, IMPORTANCE_MEDIUM) self.al_maktoum = ControlPoint.from_airport(persiangulf.Al_Maktoum_Intl, LAND, SIZE_BIG, IMPORTANCE_LOW) - self.al_minhad = ControlPoint.from_airport(persiangulf.Al_Minhad_AB, LAND, SIZE_REGULAR, 1.1) - self.sir_abu_nuayr = ControlPoint.from_airport(persiangulf.Sir_Abu_Nuayr, [0, 330], SIZE_SMALL, 1.1, has_frontline=False) - self.dubai = ControlPoint.from_airport(persiangulf.Dubai_Intl, COAST_DL_E, SIZE_LARGE, IMPORTANCE_MEDIUM) - self.sharjah = ControlPoint.from_airport(persiangulf.Sharjah_Intl, LAND, SIZE_BIG, 1.0) - self.fujairah = ControlPoint.from_airport(persiangulf.Fujairah_Intl, COAST_V_W, SIZE_REGULAR, 1.0) - self.ras_al_khaimah = ControlPoint.from_airport(persiangulf.Ras_Al_Khaimah, LAND, SIZE_REGULAR,IMPORTANCE_MEDIUM) - self.al_ain = ControlPoint.from_airport(persiangulf.Al_Ain_International_Airport, LAND, SIZE_BIG, - IMPORTANCE_HIGH) + self.al_minhad = ControlPoint.from_airport(persiangulf.Al_Minhad_AB, LAND, SIZE_REGULAR, IMPORTANCE_LOW) + self.sharjah = ControlPoint.from_airport(persiangulf.Sharjah_Intl, LAND, SIZE_BIG, IMPORTANCE_LOW) + self.fujairah = ControlPoint.from_airport(persiangulf.Fujairah_Intl, COAST_V_W, SIZE_REGULAR, IMPORTANCE_LOW) + self.ras_al_khaimah = ControlPoint.from_airport(persiangulf.Ras_Al_Khaimah, LAND, SIZE_REGULAR,IMPORTANCE_LOW) + self.al_ain = ControlPoint.from_airport(persiangulf.Al_Ain_International_Airport, LAND, SIZE_BIG,IMPORTANCE_LOW) self.east_carrier = ControlPoint.carrier("Carrier", Point(-61770, 69039), 1001) self.tarawa_carrier = ControlPoint.lha("LHA Carrier", Point(-79770, 49430), 1002) @@ -183,12 +180,10 @@ class Emirates(ConflictTheater): self.add_controlpoint(self.al_dhafra, connected_to=[self.al_ain, self.al_maktoum]) self.add_controlpoint(self.al_ain, connected_to=[self.fujairah, self.al_maktoum, self.al_dhafra]) self.add_controlpoint(self.al_maktoum, connected_to=[self.al_dhafra, self.al_minhad, self.al_ain]) - self.add_controlpoint(self.al_minhad, connected_to=[self.al_maktoum, self.dubai]) - self.add_controlpoint(self.dubai, connected_to=[self.al_minhad, self.sharjah]) - self.add_controlpoint(self.sharjah, connected_to=[self.dubai, self.ras_al_khaimah, self.fujairah]) + self.add_controlpoint(self.al_minhad, connected_to=[self.al_maktoum, self.sharjah]) + self.add_controlpoint(self.sharjah, connected_to=[self.al_minhad, self.ras_al_khaimah, self.fujairah]) self.add_controlpoint(self.ras_al_khaimah, connected_to=[self.sharjah]) self.add_controlpoint(self.fujairah, connected_to=[self.sharjah, self.al_ain]) - self.add_controlpoint(self.sir_abu_nuayr, connected_to=[]) self.add_controlpoint(self.tarawa_carrier) self.add_controlpoint(self.east_carrier) diff --git a/theater/start_generator.py b/theater/start_generator.py index ffbdbafe..c04ec490 100644 --- a/theater/start_generator.py +++ b/theater/start_generator.py @@ -4,6 +4,7 @@ import random import typing import logging +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.sam.sam_group_generator import generate_anti_air_group, generate_shorad_group @@ -79,6 +80,7 @@ def generate_groundobjects(theater: ConflictTheater, game): g.cp_id = cp.id g.airbase_group = True g.dcs_identifier = "CARRIER" + g.obj_name = namegen.random_objective_name() g.heading = 0 g.position = Point(cp.position.x, cp.position.y) group = generate_carrier_group(faction, game, g) @@ -98,6 +100,7 @@ def generate_groundobjects(theater: ConflictTheater, game): g.cp_id = cp.id g.airbase_group = True g.dcs_identifier = "LHA" + g.obj_name = namegen.random_objective_name() g.heading = 0 g.position = Point(cp.position.x, cp.position.y) group = generate_lha_group(faction, game, g) @@ -124,6 +127,7 @@ def generate_groundobjects(theater: ConflictTheater, game): g.cp_id = cp.id g.airbase_group = True g.dcs_identifier = "AA" + g.obj_name = namegen.random_objective_name() g.heading = 0 g.position = Point(point.x, point.y) @@ -160,6 +164,8 @@ def find_location(on_ground, near, theater, min, max, others) -> typing.Optional """ point = None for _ in range(1000): + + # Check if on land or sea p = near.random_point_within(max, min) if on_ground and theater.is_on_land(p): point = p @@ -180,6 +186,20 @@ def find_location(on_ground, near, theater, min, max, others) -> typing.Optional if other.position.distance_to_point(point) < 10000: point = None break + + if point: + for other in theater.controlpoints: + if other.position != near: + if point is None: + break + if other.position.distance_to_point(point) < 30000: + point = None + break + for ground_obj in other.ground_objects: + if ground_obj.position.distance_to_point(point) < 10000: + point = None + break + if point: return point return None @@ -201,13 +221,16 @@ def generate_cp_ground_points(cp: ControlPoint, theater, game, group_id, templat if cp.is_global: return False - amount = random.randrange(1, 7) + amount = random.randrange(3, 8) for i in range(0, amount): + available_categories = list(templates) + obj_name = namegen.random_objective_name() + if i >= amount - 1: tpl_category = "aa" else: - if random.randint(0, 2) == 0: + if random.randint(0, 3) == 0: tpl_category = "aa" else: tpl_category = random.choice(available_categories) @@ -223,6 +246,7 @@ def generate_cp_ground_points(cp: ControlPoint, theater, game, group_id, templat group_id = group_id + 1 logging.info("generated {} for {}".format(tpl_category, cp)) + for object in tpl: object_id += 1 @@ -231,12 +255,12 @@ def generate_cp_ground_points(cp: ControlPoint, theater, game, group_id, templat g.object_id = object_id g.cp_id = cp.id g.airbase_gorup = False + g.obj_name = obj_name g.dcs_identifier = object["type"] g.heading = object["heading"] g.position = Point(point.x + object["offset"].x, point.y + object["offset"].y) - if g.dcs_identifier == "AA": if cp.captured: faction = game.player_name diff --git a/theater/theatergroundobject.py b/theater/theatergroundobject.py index e3e8db98..b8fa91c9 100644 --- a/theater/theatergroundobject.py +++ b/theater/theatergroundobject.py @@ -49,15 +49,13 @@ class TheaterGroundObject: cp_id = 0 group_id = 0 object_id = 0 - dcs_identifier = None # type: str is_dead = False airbase_group = False - heading = 0 position = None # type: Point - groups = [] + obj_name = "" @property def category(self) -> str: