diff --git a/game/db.py b/game/db.py index dd29ae0a..6b4b863a 100644 --- a/game/db.py +++ b/game/db.py @@ -76,6 +76,7 @@ PRICES = { S_3B_Tanker: 13, IL_78M: 13, KC_135: 13, + KC130: 13, A_50: 8, E_3A: 8, @@ -172,6 +173,7 @@ UNIT_BY_TASK = { Refueling: [ IL_78M, KC_135, + KC130, ], AWACS: [E_3A, A_50, ], @@ -297,6 +299,7 @@ UNIT_BY_COUNTRY = { AV8BNA, KC_135, + KC130, S_3B_Tanker, C_130, E_3A, diff --git a/game/operation/operation.py b/game/operation/operation.py index 06384a79..fe7a898f 100644 --- a/game/operation/operation.py +++ b/game/operation/operation.py @@ -86,7 +86,9 @@ class Operation: # air support self.airsupportgen.generate(self.is_awacs_enabled) - self.briefinggen.append_frequency("Tanker", "10X/131 MHz AM") + for i, tanker_type in enumerate(self.airsupportgen.generated_tankers): + self.briefinggen.append_frequency("{} Tanker".format(tanker_type), "{}X/{} MHz AM".format(97+i, 130+i)) + if self.is_awacs_enabled: self.briefinggen.append_frequency("AWACS", "133 MHz AM") diff --git a/game/settings.py b/game/settings.py index 0b91e164..2014d1f4 100644 --- a/game/settings.py +++ b/game/settings.py @@ -2,6 +2,7 @@ class Settings: player_skill = "Good" enemy_skill = "Average" + enemy_vehicle_skill = "Average" only_player_takeoff = True night_disabled = False multiplier = 1 diff --git a/gen/aaa.py b/gen/aaa.py index b2811824..3aaf999a 100644 --- a/gen/aaa.py +++ b/gen/aaa.py @@ -4,7 +4,7 @@ from .naming import * from dcs.mission import * DISTANCE_FACTOR = 0.5, 1 -EXTRA_AA_MIN_DISTANCE = 35000 +EXTRA_AA_MIN_DISTANCE = 50000 EXTRA_AA_MAX_DISTANCE = 150000 EXTRA_AA_POSITION_FROM_CP = 550 @@ -59,6 +59,9 @@ class ExtraAAConflictGenerator: if cp.position.distance_to_point(self.conflict.from_cp.position) < EXTRA_AA_MIN_DISTANCE: continue + if cp.position.distance_to_point(self.conflict.to_cp.position) < EXTRA_AA_MIN_DISTANCE: + continue + if cp.position.distance_to_point(self.conflict.position) > EXTRA_AA_MAX_DISTANCE: continue diff --git a/gen/airsupportgen.py b/gen/airsupportgen.py index 7b3557cd..fb18d01f 100644 --- a/gen/airsupportgen.py +++ b/gen/airsupportgen.py @@ -10,16 +10,20 @@ from dcs.terrain.terrain import NoParkingSlotError TANKER_DISTANCE = 15000 TANKER_ALT = 10000 +TANKER_HEADING_OFFSET = 45 AWACS_DISTANCE = 150000 AWACS_ALT = 10000 class AirSupportConflictGenerator: + generated_tankers = None # type: typing.List[str] + def __init__(self, mission: Mission, conflict: Conflict, game): self.mission = mission self.conflict = conflict self.game = game + self.generated_tankers = [] @classmethod def support_tasks(cls) -> typing.Collection[typing.Type[MainTask]]: @@ -27,22 +31,24 @@ class AirSupportConflictGenerator: def generate(self, is_awacs_enabled): player_cp = self.conflict.from_cp if self.conflict.from_cp.captured else self.conflict.to_cp - tanker_unit = db.find_unittype(Refueling, self.conflict.attackers_side.name)[0] - tanker_heading = self.conflict.to_cp.position.heading_between_point(self.conflict.from_cp.position) - tanker_position = player_cp.position.point_from_heading(tanker_heading, TANKER_DISTANCE) - tanker_group = self.mission.refuel_flight( - country=self.mission.country(self.game.player), - name=namegen.next_tanker_name(self.mission.country(self.game.player)), - airport=None, - plane_type=tanker_unit, - position=tanker_position, - altitude=TANKER_ALT, - frequency=131, - start_type=StartType.Warm, - tacanchannel="99X", - ) - tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=10, unit_id=tanker_group.id, aa=False)) + for i, tanker_unit_type in enumerate(db.find_unittype(Refueling, self.conflict.attackers_side.name)): + self.generated_tankers.append(db.unit_type_name(tanker_unit_type)) + tanker_heading = self.conflict.to_cp.position.heading_between_point(self.conflict.from_cp.position) + TANKER_HEADING_OFFSET * i + tanker_position = player_cp.position.point_from_heading(tanker_heading, TANKER_DISTANCE) + tanker_group = self.mission.refuel_flight( + country=self.mission.country(self.game.player), + name=namegen.next_tanker_name(self.mission.country(self.game.player)), + airport=None, + plane_type=tanker_unit_type, + position=tanker_position, + altitude=TANKER_ALT, + frequency=130 + i, + start_type=StartType.Warm, + tacanchannel="{}X".format(97 + i), + ) + + tanker_group.points[0].tasks.append(ActivateBeaconCommand(channel=97 + i, unit_id=tanker_group.id, aa=False)) if is_awacs_enabled: awacs_unit = db.find_unittype(AWACS, self.conflict.attackers_side.name)[0] diff --git a/gen/conflictgen.py b/gen/conflictgen.py index f28f70f3..93744958 100644 --- a/gen/conflictgen.py +++ b/gen/conflictgen.py @@ -154,32 +154,6 @@ class Conflict: def has_frontline_between(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> bool: return from_cp.has_frontline and to_cp.has_frontline - @classmethod - def frontline_position0(cls, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Tuple[Point, int]: - cp_distance = from_cp.position.distance_to_point(to_cp.position) - cp_distance -= cp_distance * to_cp.frontline_offset + cp_distance * from_cp.frontline_offset - - distance = max(cp_distance * FRONTLINE_DISTANCE_STRENGTH_FACTOR * to_cp.base.strength, FRONTLINE_MIN_CP_DISTANCE) - heading = to_cp.position.heading_between_point(from_cp.position) - return to_cp.position.point_from_heading(heading, distance), heading - - @classmethod - def frontline_position2(cls, theater: ConflictTheater, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Optional[typing.Tuple[Point, int]]: - attack_heading = from_cp.position.heading_between_point(to_cp.position) - attack_starting_position = cls._find_ground_position(from_cp.position, 200000, attack_heading, theater) - if not attack_starting_position: - return None - - attack_ending_position = cls._find_ground_position(to_cp.position, 200000, _opposite_heading(attack_heading), theater) - if not attack_ending_position: - return None - - attack_distance = attack_ending_position.distance_to_point(attack_starting_position) - strength_delta = (from_cp.base.strength - to_cp.base.strength) / 1.0 - middle_position = attack_starting_position.point_from_heading(attack_heading, attack_distance / 2) - - return middle_position.point_from_heading(attack_heading, strength_delta * attack_distance), _opposite_heading(attack_heading) - @classmethod def frontline_position(cls, theater: ConflictTheater, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Optional[typing.Tuple[Point, int]]: attack_heading = from_cp.position.heading_between_point(to_cp.position) @@ -190,10 +164,10 @@ class Conflict: position = middle_point.point_from_heading(attack_heading, strength_delta * attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE) ground_position = cls._find_ground_position(position, attack_distance / 2 - FRONTLINE_MIN_CP_DISTANCE, attack_heading, theater) if ground_position: - return ground_position, attack_heading + return ground_position, _opposite_heading(attack_heading) else: - print(from_cp, to_cp) - return None + print("Coudn't find frontline position between {} and {}!".format(from_cp, to_cp)) + return position, _opposite_heading(attack_heading) @classmethod diff --git a/gen/groundobjectsgen.py b/gen/groundobjectsgen.py index 08354825..871a18f6 100644 --- a/gen/groundobjectsgen.py +++ b/gen/groundobjectsgen.py @@ -8,6 +8,7 @@ from dcs.mission import * from dcs.statics import * FARP_FRONTLINE_DISTANCE = 10000 +AA_CP_MIN_DISTANCE = 40000 class GroundObjectsGenerator: @@ -47,6 +48,9 @@ class GroundObjectsGenerator: for ground_object in cp.ground_objects: if ground_object.dcs_identifier == "AA": + if ground_object.position.distance_to_point(self.conflict.from_cp.position) < AA_CP_MIN_DISTANCE: + continue + if ground_object.is_dead: continue diff --git a/gen/triggergen.py b/gen/triggergen.py index 0e87630e..d551d429 100644 --- a/gen/triggergen.py +++ b/gen/triggergen.py @@ -130,9 +130,9 @@ class TriggersGenerator: def _set_skill(self, player_coalition: str, enemy_coalition: str): for coalition_name, coalition in self.mission.coalition.items(): if coalition_name == player_coalition: - skill_level = self.game.settings.player_skill + skill_level = self.game.settings.player_skill, self.game.settings.player_skill elif coalition_name == enemy_coalition: - skill_level = self.game.settings.enemy_skill + skill_level = self.game.settings.enemy_skill, self.game.settings.enemy_vehicle_skill else: continue @@ -140,10 +140,10 @@ class TriggersGenerator: for plane_group in country.plane_group: for plane_unit in plane_group.units: if plane_unit.skill != Skill.Client and plane_unit.skill != Skill.Player: - plane_unit.skill = Skill(skill_level) + plane_unit.skill = Skill(skill_level[0]) for vehicle_group in country.vehicle_group: - vehicle_group.set_skill(Skill(skill_level)) + vehicle_group.set_skill(Skill(skill_level[1])) def generate(self, player_cp: ControlPoint, is_quick: bool, activation_trigger_radius: int, awacs_enabled: bool): player_coalition = self.game.player == "USA" and "blue" or "red" diff --git a/gen/visualgen.py b/gen/visualgen.py index 011765cf..227af12f 100644 --- a/gen/visualgen.py +++ b/gen/visualgen.py @@ -98,6 +98,9 @@ class VisualGenerator: def _generate_frontline_smokes(self): for from_cp, to_cp in self.game.theater.conflicts(): + if from_cp.is_global or to_cp.is_global: + continue + frontline = Conflict.frontline_position(self.game.theater, from_cp, to_cp) if not frontline: continue diff --git a/resources/caulandmap.p b/resources/caulandmap.p index 88ba4d3c..6edba188 100644 Binary files a/resources/caulandmap.p and b/resources/caulandmap.p differ diff --git a/resources/gulflandmap.p b/resources/gulflandmap.p index 98566550..539db733 100644 Binary files a/resources/gulflandmap.p and b/resources/gulflandmap.p differ diff --git a/resources/nevlandmap.p b/resources/nevlandmap.p new file mode 100644 index 00000000..5ac9c213 Binary files /dev/null and b/resources/nevlandmap.p differ diff --git a/resources/tools/cau_terrain.miz b/resources/tools/cau_terrain.miz index e2373b5e..b113c0b7 100644 Binary files a/resources/tools/cau_terrain.miz and b/resources/tools/cau_terrain.miz differ diff --git a/resources/tools/generate_landmap.py b/resources/tools/generate_landmap.py index e90293b1..fab94132 100644 --- a/resources/tools/generate_landmap.py +++ b/resources/tools/generate_landmap.py @@ -3,7 +3,7 @@ import pickle from dcs.mission import Mission from dcs.planes import A_10C -for terrain in ["cau", "gulf"]: +for terrain in ["cau", "gulf", "nev"]: m = Mission() m.load_file("./{}_terrain.miz".format(terrain)) diff --git a/resources/tools/gulf_terrain.miz b/resources/tools/gulf_terrain.miz index 01a262b0..6b5d6de7 100644 Binary files a/resources/tools/gulf_terrain.miz and b/resources/tools/gulf_terrain.miz differ diff --git a/theater/caucasus.py b/theater/caucasus.py index 0db76aec..2e5b9bc4 100644 --- a/theater/caucasus.py +++ b/theater/caucasus.py @@ -25,12 +25,12 @@ class CaucasusTheater(ConflictTheater): kutaisi = ControlPoint.from_airport(caucasus.Kutaisi, LAND, SIZE_SMALL, IMPORTANCE_LOW) senaki = ControlPoint.from_airport(caucasus.Senaki_Kolkhi, LAND, SIZE_REGULAR, IMPORTANCE_LOW) kobuleti = ControlPoint.from_airport(caucasus.Kobuleti, COAST_A_E, SIZE_SMALL, 1.1) - batumi = ControlPoint.from_airport(caucasus.Batumi, COAST_DL_E, SIZE_SMALL, 1.3, has_frontline=False) + batumi = ControlPoint.from_airport(caucasus.Batumi, COAST_DL_E, SIZE_SMALL, 1.3) sukhumi = ControlPoint.from_airport(caucasus.Sukhumi_Babushara, COAST_DR_E, SIZE_REGULAR, 1.2) - gudauta = ControlPoint.from_airport(caucasus.Gudauta, COAST_DR_E, SIZE_REGULAR, 1.2, has_frontline=False) - sochi = ControlPoint.from_airport(caucasus.Sochi_Adler, COAST_DR_E, SIZE_BIG, IMPORTANCE_HIGH, has_frontline=False) + gudauta = ControlPoint.from_airport(caucasus.Gudauta, COAST_DR_E, SIZE_REGULAR, 1.2) + sochi = ControlPoint.from_airport(caucasus.Sochi_Adler, COAST_DR_E, SIZE_BIG, IMPORTANCE_HIGH) - gelendzhik = ControlPoint.from_airport(caucasus.Gelendzhik, COAST_DR_E, SIZE_BIG, 1.1, has_frontline=False) + gelendzhik = ControlPoint.from_airport(caucasus.Gelendzhik, COAST_DR_E, SIZE_BIG, 1.1) maykop = ControlPoint.from_airport(caucasus.Maykop_Khanskaya, LAND, SIZE_LARGE, IMPORTANCE_HIGH) krasnodar = ControlPoint.from_airport(caucasus.Krasnodar_Center, LAND, SIZE_LARGE, IMPORTANCE_HIGH) novorossiysk = ControlPoint.from_airport(caucasus.Novorossiysk, COAST_DR_E, SIZE_BIG, 1.2) diff --git a/theater/nevada.py b/theater/nevada.py index 566ca762..66b8b350 100644 --- a/theater/nevada.py +++ b/theater/nevada.py @@ -1,6 +1,7 @@ from dcs.terrain import nevada from dcs import mapping +from .landmap import * from .conflicttheater import * from .base import * @@ -10,6 +11,7 @@ class NevadaTheater(ConflictTheater): overview_image = "nevada.gif" reference_points = {(nevada.Mina_Airport_3Q0.position.x, nevada.Mina_Airport_3Q0.position.y): (45, -360), (nevada.Laughlin_Airport.position.x, nevada.Laughlin_Airport.position.y): (440, 80), } + landmap = load_landmap("resources\\nev_landmap.p") daytime_map = { "dawn": (4, 6), "day": (6, 17), @@ -32,7 +34,7 @@ class NevadaTheater(ConflictTheater): jean = ControlPoint.from_airport(nevada.Jean_Airport, LAND, SIZE_REGULAR, 1.2) laughlin = ControlPoint.from_airport(nevada.Laughlin_Airport, LAND, SIZE_LARGE, IMPORTANCE_HIGH) - def __init__(self, load_ground_objects=True): + def __init__(self): super(NevadaTheater, self).__init__() self.add_controlpoint(self.mina, connected_to=[self.tonopah]) diff --git a/theater/start_generator.py b/theater/start_generator.py index bf5d8a75..25c84412 100644 --- a/theater/start_generator.py +++ b/theater/start_generator.py @@ -47,12 +47,26 @@ def generate_groundobjects(theater: ConflictTheater): tpls = pickle.load(f) def find_location(on_ground, near, theater, min, max) -> typing.Optional[Point]: - for _ in range(500): + point = None + for _ in range(1000): p = near.random_point_within(max, min) if on_ground and theater.is_on_land(p): - return p + point = p elif not on_ground and theater.is_in_sea(p): - return p + point = p + + if point: + for angle in range(0, 360, 45): + p = point.point_from_heading(angle, 1000) + if on_ground and not theater.is_on_land(p): + point = None + break + elif not on_ground and not theater.is_in_sea(p): + point = None + break + + if point: + return point return None diff --git a/ui/basemenu.py b/ui/basemenu.py index 1e2e86f2..9fc1df79 100644 --- a/ui/basemenu.py +++ b/ui/basemenu.py @@ -18,53 +18,61 @@ class BaseMenu(Menu): def display(self): self.window.clear_right_pane() - row = 0 - - def purchase_row(unit_type, unit_price): - nonlocal row - - existing_units = self.base.total_units_of_type(unit_type) - scheduled_units = self.event.units.get(unit_type, 0) - - Label(self.frame, text="{}".format(db.unit_type_name(unit_type)), **STYLES["widget"]).grid(row=row, sticky=W) - - label = Label(self.frame, text="({})".format(existing_units), **STYLES["widget"]) - label.grid(column=1, row=row) - self.bought_amount_labels[unit_type] = label - - Label(self.frame, text="{}m".format(unit_price), **STYLES["widget"]).grid(column=2, row=row, sticky=E) - Button(self.frame, text="+", command=self.buy(unit_type), **STYLES["btn-primary"]).grid(column=3, row=row, padx=(10,0)) - Button(self.frame, text="-", command=self.sell(unit_type), **STYLES["btn-warning"]).grid(column=4, row=row, padx=(10,5)) - row += 1 - units = { - PinpointStrike: db.find_unittype(PinpointStrike, self.game.player), - Embarking: db.find_unittype(Embarking, self.game.player), - CAS: db.find_unittype(CAS, self.game.player), CAP: db.find_unittype(CAP, self.game.player), + Embarking: db.find_unittype(Embarking, self.game.player), AirDefence: db.find_unittype(AirDefence, self.game.player), + CAS: db.find_unittype(CAS, self.game.player), + PinpointStrike: db.find_unittype(PinpointStrike, self.game.player), } # Header head = Frame(self.frame, **STYLES["header"]) - head.grid(row=row, column=0, columnspan=5, sticky=NSEW, pady=5) + head.grid(row=0, column=0, columnspan=99, sticky=NSEW, pady=5) Label(head, text=self.cp.name, **STYLES["title"]).grid(row=0, column=0, sticky=NW+S) units_title = "{}/{}/{}".format(self.cp.base.total_planes, self.cp.base.total_armor, self.cp.base.total_aa) Label(head, text=units_title, **STYLES["strong-grey"]).grid(row=0, column=1, sticky=NE+S) - row += 1 self.budget_label = Label(self.frame, text="Budget: {}m".format(self.game.budget), **STYLES["widget"]) - self.budget_label.grid(row=row, sticky=W) - Button(self.frame, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(column=4, row=row, padx=(0,15), pady=(0,5)) - row += 1 + self.budget_label.grid(row=1, sticky=W) + Button(self.frame, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(column=9, row=1, padx=(0,15), pady=(0,5)) - for task_type, units in units.items(): - Label(self.frame, text="{}".format(db.task_name(task_type)), **STYLES["strong"]).grid(row=row, columnspan=5, sticky=NSEW); row += 1 + tasks = list(units.keys()) + tasks_per_column = 3 - units = list(set(units)) - units.sort(key=lambda x: db.PRICES[x]) - for unit_type in units: - purchase_row(unit_type, db.PRICES[unit_type]) + column = 0 + for i, tasks_column in [(i, tasks[idx:idx+tasks_per_column]) for i, idx in enumerate(range(0, len(tasks), tasks_per_column))]: + row = 2 + + + def purchase_row(unit_type, unit_price): + nonlocal row + nonlocal column + + existing_units = self.base.total_units_of_type(unit_type) + scheduled_units = self.event.units.get(unit_type, 0) + + Label(self.frame, text="{}".format(db.unit_type_name(unit_type)), **STYLES["widget"]).grid(row=row, column=column, sticky=W) + + label = Label(self.frame, text="({})".format(existing_units), **STYLES["widget"]) + label.grid(column=column + 1, row=row) + self.bought_amount_labels[unit_type] = label + + Label(self.frame, text="{}m".format(unit_price), **STYLES["widget"]).grid(column=column + 2, row=row, sticky=E) + Button(self.frame, text="+", command=self.buy(unit_type), **STYLES["btn-primary"]).grid(column=column + 3, row=row, padx=(10,0)) + Button(self.frame, text="-", command=self.sell(unit_type), **STYLES["btn-warning"]).grid(column=column + 4, row=row, padx=(10,5)) + row += 1 + + for task_type in tasks_column: + Label(self.frame, text="{}".format(db.task_name(task_type)), **STYLES["strong"]).grid(row=row, column=column, columnspan=5, sticky=NSEW) + row += 1 + + units_column = list(set(units[task_type])) + units_column.sort(key=lambda x: db.PRICES[x]) + for unit_type in units_column: + purchase_row(unit_type, db.PRICES[unit_type]) + + column += 5 def dismiss(self): if sum([x for x in self.event.units.values()]) == 0: diff --git a/ui/configurationmenu.py b/ui/configurationmenu.py index 8702222f..e94ee4c1 100644 --- a/ui/configurationmenu.py +++ b/ui/configurationmenu.py @@ -18,6 +18,9 @@ class ConfigurationMenu(Menu): self.enemy_skill_var = StringVar() self.enemy_skill_var.set(self.game.settings.enemy_skill) + self.enemy_vehicle_var = StringVar() + self.enemy_vehicle_var.set(self.game.settings.enemy_vehicle_skill) + self.takeoff_var = BooleanVar() self.takeoff_var.set(self.game.settings.only_player_takeoff) @@ -30,6 +33,7 @@ class ConfigurationMenu(Menu): def dismiss(self): self.game.settings.player_skill = self.player_skill_var.get() self.game.settings.enemy_skill = self.enemy_skill_var.get() + self.game.settings.enemy_vehicle_skill = self.enemy_vehicle_var.get() self.game.settings.only_player_takeoff = self.takeoff_var.get() self.game.settings.night_disabled = self.night_var.get() self.game.settings.cold_start = self.cold_start_var.get() @@ -49,6 +53,7 @@ class ConfigurationMenu(Menu): Label(body, text="Player coalition skill", **STYLES["widget"]).grid(row=0, column=0, sticky=W) Label(body, text="Enemy coalition skill", **STYLES["widget"]).grid(row=1, column=0, sticky=W) + Label(body, text="Enemy AA and vehicle skill", **STYLES["widget"]).grid(row=2, column=0, sticky=W) p_skill = OptionMenu(body, self.player_skill_var, "Average", "Good", "High", "Excellent") p_skill.grid(row=0, column=1, sticky=E, pady=5) @@ -58,26 +63,30 @@ class ConfigurationMenu(Menu): e_skill.grid(row=1, column=1, sticky=E) e_skill.configure(**STYLES["btn-primary"]) - Label(body, text="Aircraft cold start", **STYLES["widget"]).grid(row=2, column=0, sticky=W) - Label(body, text="Takeoff only for player group", **STYLES["widget"]).grid(row=3, column=0, sticky=W) - Label(body, text="Disable night missions", **STYLES["widget"]).grid(row=4, column=0, sticky=W) + e_skill = OptionMenu(body, self.enemy_vehicle_var, "Average", "Good", "High", "Excellent") + e_skill.grid(row=2, column=1, sticky=E) + e_skill.configure(**STYLES["btn-primary"]) - Checkbutton(body, variable=self.cold_start_var, **STYLES["radiobutton"]).grid(row=2, column=1, sticky=E) - Checkbutton(body, variable=self.takeoff_var, **STYLES["radiobutton"]).grid(row=3, column=1, sticky=E) - Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=4, column=1, sticky=E) + Label(body, text="Aircraft cold start", **STYLES["widget"]).grid(row=3, column=0, sticky=W) + Label(body, text="Takeoff only for player group", **STYLES["widget"]).grid(row=4, column=0, sticky=W) + Label(body, text="Disable night missions", **STYLES["widget"]).grid(row=5, column=0, sticky=W) - Button(body, text="Display logs", command=self.display_logs, **STYLES["btn-primary"]).grid(row=5, column=0, sticky=E, pady=30) - Button(body, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(row=5, column=1, sticky=E, pady=30) + Checkbutton(body, variable=self.cold_start_var, **STYLES["radiobutton"]).grid(row=3, column=1, sticky=E) + Checkbutton(body, variable=self.takeoff_var, **STYLES["radiobutton"]).grid(row=5, column=1, sticky=E) + Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=5, column=1, sticky=E) - Label(body, text="Contributors: ", **STYLES["widget"]).grid(row=6, column=0, sticky=W) + Button(body, text="Display logs", command=self.display_logs, **STYLES["btn-primary"]).grid(row=6, column=0, sticky=E, pady=30) + Button(body, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(row=6, column=1, sticky=E, pady=30) - Label(body, text="shdwp - author, maintainer", **STYLES["widget"]).grid(row=7, column=0, sticky=W) - Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/shdwp"), **STYLES["widget"]).grid(row=7, column=1, sticky=E) + Label(body, text="Contributors: ", **STYLES["widget"]).grid(row=7, column=0, sticky=W) - Label(body, text="Khopa - contributions", **STYLES["widget"]).grid(row=8, column=0, sticky=W) - Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/Khopa"), **STYLES["widget"]).grid(row=8, column=1, sticky=E) + Label(body, text="shdwp - author, maintainer", **STYLES["widget"]).grid(row=8, column=0, sticky=W) + Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/shdwp"), **STYLES["widget"]).grid(row=9, column=1, sticky=E) - Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=10, column=1, pady=30) + Label(body, text="Khopa - contributions", **STYLES["widget"]).grid(row=9, column=0, sticky=W) + Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/Khopa"), **STYLES["widget"]).grid(row=9, column=1, sticky=E) + + Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=11, column=1, pady=30) def display_logs(self): raise ShowLogsException() diff --git a/ui/mainmenu.py b/ui/mainmenu.py index 07dac796..ed0f1721 100644 --- a/ui/mainmenu.py +++ b/ui/mainmenu.py @@ -28,17 +28,21 @@ class MainMenu(Menu): self.window.clear_right_pane() self.upd.update() - row = 0 # Header : header = Frame(self.frame, **STYLES["header"]) - Button(header, text="Configuration", command=self.configuration_menu, **STYLES["btn-primary"]).grid(column=0, row=0, sticky=NE) + Button(header, text="Configuration", command=self.configuration_menu, **STYLES["btn-primary"]).grid(column=0, row=0, sticky=NW) Label(header, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount), **STYLES["strong"]).grid(column=1, row=0, sticky=NSEW, padx=50) - Button(header, text="Pass turn", command=self.pass_turn, **STYLES["btn-primary"]).grid(column=2, row=0, sticky=NW) - header.grid(column=0, row=0, sticky=N+EW) + Button(header, text="Pass turn", command=self.pass_turn, **STYLES["btn-primary"]).grid(column=2, row=0, sticky=NE) + header.grid(column=0, columnspan=99, row=0, sticky=N+EW) - body = LabelFrame(self.frame, **STYLES["body"]) - body.grid(column=0, row=1, sticky=NSEW) + events = self.game.events + events.sort(key=lambda x: x.to_cp.name) + events.sort(key=lambda x: x.from_cp.name) + events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0)) + + column = 0 + row = 0 def label(text): nonlocal row, body @@ -58,21 +62,30 @@ class MainMenu(Menu): Button(body, text=">", command=self.start_event(event), **STYLES["btn-primary"]).grid(column=1, row=row, sticky=E) row += 1 + def departure_header(text): + nonlocal row, body + Label(body, text=text, **STYLES["strong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(0, 5)); row += 1 + def destination_header(text, pady=0): nonlocal row, body - Label(body, text=text, **STYLES["strong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(pady,0)); row += 1 - - events = self.game.events - events.sort(key=lambda x: x.to_cp.name) - events.sort(key=lambda x: x.from_cp.name) - events.sort(key=lambda x: x.informational and 2 or (self.game.is_player_attack(x) and 1 or 0)) + Label(body, text=text, **STYLES["substrong"]).grid(column=0, columnspan=2, row=row, sticky=N+EW, pady=(pady,0)); row += 1 destination = None + departure = None deliveries = False for event in events: if not event.informational: + if event.from_cp.name != departure: + body = LabelFrame(self.frame, **STYLES["body"]) + body.grid(column=column, row=1, sticky=NSEW) + row = 0 + column += 1 + + departure = event.from_cp.name + departure_header(event.from_cp.name) + if self.game.is_player_attack(event): - new_destination = "From {} to {}".format(event.from_cp.name, event.to_cp.name) + new_destination = "At {}".format(event.to_cp.name) else: new_destination = "Enemy attack" if destination != new_destination: diff --git a/ui/styles.py b/ui/styles.py index 10a2d48d..bd5911ec 100644 --- a/ui/styles.py +++ b/ui/styles.py @@ -12,6 +12,7 @@ GREEN = "#699245" YELLOW = "#BF9A46" RED = "#D0232E" BG_TITLE_COLOR = "#2D3E50" +BG_SUBTITLE_COLOR = "#3E4F61" # Fonts FONT_FAMILY = "Trebuchet MS" @@ -25,8 +26,9 @@ STYLES = {} STYLES["label-frame"] = {"font": BOLD_FONT, "bg": BG_COLOR, "fg": FG_COLOR} STYLES["frame-wrapper"] = {"bg": BG_COLOR, "relief":"sunken"} -STYLES["body"] = {"bg": BG_COLOR, "padx": 25, "pady": 35} +STYLES["body"] = {"bg": BG_COLOR, "padx": 10, "pady": 10} STYLES["strong"] = {"font": BOLD_FONT, "bg": BG_TITLE_COLOR, "fg": FG_COLOR} +STYLES["substrong"] = {"font": BOLD_FONT, "bg": BG_SUBTITLE_COLOR, "fg": FG_COLOR} STYLES["strong-grey"] = {"font": BOLD_FONT, "bg": BG_TITLE_COLOR, "fg": FG_COLOR_LIGHT} STYLES["mission-preview"] = {"font": BOLD_FONT, "bg": YELLOW, "fg": FG_COLOR} @@ -39,6 +41,7 @@ STYLES["title"] = {"bg": BG_TITLE_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pad STYLES["title-green"] = {"bg": GREEN, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": TITLE_FONT} STYLES["title-red"] = {"bg": RED, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": TITLE_FONT} STYLES["header"] = {"bg": BG_TITLE_COLOR} +STYLES["subheader"] = {"bg": BG_SUBTITLE_COLOR} STYLES["btn-primary"] = {"bg": GREEN, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": DEFAULT_FONT} STYLES["btn-danger"] = {"bg": RED, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": DEFAULT_FONT}