diff --git a/__init__.py b/__init__.py index b8b69e2e..dc6ee3d3 100755 --- a/__init__.py +++ b/__init__.py @@ -1,4 +1,7 @@ #!/usr/bin/env python3 +import os +import dcs + import theater.caucasus import theater.persiangulf import theater.nevada @@ -11,6 +14,8 @@ from game.game import Game from theater import start_generator from userdata import persistency +dcs.planes.FlyingType.payload_dirs.append(os.path.join(os.path.dirname(os.path.realpath(__file__)), "resources\\payloads")) + def proceed_to_main_menu(game: Game): m = ui.mainmenu.MainMenu(w, None, game) @@ -22,7 +27,7 @@ game = persistency.restore_game() if not game: new_game_menu = None # type: NewGameMenu - def start_new_game(player_name: str, enemy_name: str, terrain: str): + def start_new_game(player_name: str, enemy_name: str, terrain: str, sams: bool): if terrain == "persiangulf": conflicttheater = theater.persiangulf.PersianGulfTheater() elif terrain == "nevada": @@ -30,7 +35,7 @@ if not game: else: conflicttheater = theater.caucasus.CaucasusTheater() - start_generator.generate_initial(conflicttheater, enemy_name) + start_generator.generate_initial(conflicttheater, enemy_name, sams) proceed_to_main_menu(Game(player_name=player_name, enemy_name=enemy_name, theater=conflicttheater)) diff --git a/game/db.py b/game/db.py index d2373691..02c1996f 100644 --- a/game/db.py +++ b/game/db.py @@ -115,10 +115,17 @@ UNIT_BY_TASK = { 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_Vulcan_M163, + AirDefence.AAA_Vulcan_M163, + AirDefence.AAA_Vulcan_M163, + AirDefence.SAM_Avenger_M1097, AirDefence.SAM_Avenger_M1097, AirDefence.SAM_Patriot_ICC, AirDefence.AAA_ZU_23_on_Ural_375, + AirDefence.AAA_ZU_23_on_Ural_375, + AirDefence.AAA_ZU_23_on_Ural_375, + AirDefence.AAA_ZU_23_on_Ural_375, + AirDefence.SAM_SA_18_Igla_MANPADS, AirDefence.SAM_SA_18_Igla_MANPADS, AirDefence.SAM_SA_19_Tunguska_2S6, AirDefence.SAM_SA_8_Osa_9A33, @@ -126,6 +133,14 @@ UNIT_BY_TASK = { Carriage: [CVN_74_John_C__Stennis, CV_1143_5_Admiral_Kuznetsov, ], } +SAM_BAN = [ + AirDefence.SAM_Avenger_M1097, + AirDefence.SAM_Patriot_ICC, + + AirDefence.SAM_SA_19_Tunguska_2S6, + AirDefence.SAM_SA_8_Osa_9A33, +] + UNIT_BY_COUNTRY = { "Russia": [ C_101CC, @@ -256,7 +271,7 @@ def _validate_db(): # check unit by task uniquity total_set = set() for t, unit_collection in UNIT_BY_TASK.items(): - for unit_type in unit_collection: + for unit_type in set(unit_collection): assert unit_type not in total_set, "{} is duplicate".format(unit_type) total_set.add(unit_type) @@ -272,4 +287,5 @@ def _validate_db(): for unit_type in total_set: assert unit_type in PRICES, "{} not in prices".format(unit_type) + _validate_db() \ No newline at end of file diff --git a/game/event.py b/game/event.py index 7b4f273b..4eb0404a 100644 --- a/game/event.py +++ b/game/event.py @@ -258,7 +258,7 @@ class CaptureEvent(Event): attack=armor, intercept=interceptors, defense=self.to_cp.base.armor, - aa=self.to_cp.base.aa) + aa=self.to_cp.base.assemble_aa()) self.operation = op diff --git a/game/game.py b/game/game.py index 8f25b577..3e436049 100644 --- a/game/game.py +++ b/game/game.py @@ -5,7 +5,7 @@ COMMISION_LIMITS_FACTORS = { CAP: 2, CAS: 1, FighterSweep: 3, - AirDefence: 2, + AirDefence: 1, } COMMISION_AMOUNTS_SCALE = 2 @@ -39,6 +39,8 @@ class Game: budget = PLAYER_BUDGET_INITIAL events = None # type: typing.List[Event] pending_transfers = None # type: typing.Dict[] + player_skill = "Good" + enemy_skill = "Average" def __init__(self, player_name: str, enemy_name: str, theater: ConflictTheater): self.events = [] diff --git a/game/operation.py b/game/operation.py index 1fd53c0e..bd841c4f 100644 --- a/game/operation.py +++ b/game/operation.py @@ -7,8 +7,9 @@ from gen.aircraft import * from gen.aaa import * from gen.shipgen import * from gen.conflictgen import * -from gen.envsettingsgen import * +from gen.settingsgen import * from gen.awacsgen import * +from gen.visualgen import * class Operation: @@ -21,8 +22,9 @@ class Operation: aagen = None # type: AAConflictGenerator extra_aagen = None # type: ExtraAAConflictGenerator shipgen = None # type: ShipGenerator - envgen = None # type: EnvironmentSettingsGenerator + envgen = None # type: SettingsGenerator awacsgen = None # type: AWACSConflictGenerator + visualgen = None # type: VisualGenerator is_awacs_enabled = False @@ -52,7 +54,8 @@ class Operation: self.aagen = AAConflictGenerator(mission, conflict) self.shipgen = ShipGenerator(mission, conflict) self.awacsgen = AWACSConflictGenerator(mission, conflict, self.game) - self.envgen = EnvironmentSettingsGenerator(mission, conflict, self.game) + self.envgen = SettingsGenerator(mission, conflict, self.game) + self.visualgen = VisualGenerator(mission, conflict, self.game) player_name = self.from_cp.captured and self.attacker_name or self.defender_name enemy_name = self.from_cp.captured and self.defender_name or self.attacker_name @@ -70,6 +73,8 @@ class Operation: self.defenders_starting_position = self.to_cp.at def generate(self): + self.visualgen.generate() + if self.is_awacs_enabled: self.awacsgen.generate() @@ -117,6 +122,11 @@ class CaptureOperation(Operation): def prepare(self, terrain: dcs.terrain.Terrain, is_quick: bool): super(CaptureOperation, self).prepare(terrain, is_quick) + + self.defenders_starting_position = None + if self.game.player == self.defender_name: + self.attackers_starting_position = None + self.initialize(mission=self.mission, conflict=self.to_cp.conflict_attack(self.from_cp, self.mission.country(self.attacker_name), @@ -131,6 +141,7 @@ class CaptureOperation(Operation): self.airgen.generate_cas(self.cas, clients=self.attacker_clients, at=self.attackers_starting_position) self.airgen.generate_cas_escort(self.escort, clients=self.attacker_clients, at=self.attackers_starting_position) + self.visualgen.generate_target_smokes(self.to_cp) super(CaptureOperation, self).generate() @@ -152,6 +163,8 @@ class InterceptOperation(Operation): def prepare(self, terrain: dcs.terrain.Terrain, is_quick: bool): super(InterceptOperation, self).prepare(terrain, is_quick) + self.defenders_starting_position = None + conflict = Conflict.intercept_conflict( attacker=self.mission.country(self.attacker_name), defender=self.mission.country(self.defender_name), diff --git a/gen/envsettingsgen.py b/gen/settingsgen.py similarity index 79% rename from gen/envsettingsgen.py rename to gen/settingsgen.py index ce95877b..ed25196d 100644 --- a/gen/envsettingsgen.py +++ b/gen/settingsgen.py @@ -1,13 +1,14 @@ import typing import random -from datetime import datetime, timedelta +from datetime import datetime, timedelta, time from dcs.mission import Mission from dcs.triggers import * from dcs.condition import * from dcs.action import * -from dcs.task import * +from dcs.unit import Skill +from game import db from theater.weatherforecast import WeatherForecast from theater.conflicttheater import Conflict @@ -23,35 +24,27 @@ RANDOM_TIME = { RANDOM_WEATHER = { 0: 5, # thunderstorm - 1: 20, # heavy rain - 2: 30, # rain + 1: 10, # heavy rain + 2: 20, # rain 3: 100, # random dynamic } -class EnvironmentSettingsGenerator: +class SettingsGenerator: def __init__(self, mission: Mission, conflict: Conflict, game): self.mission = mission self.conflict = conflict self.game = game def _gen_random_time(self): - start_time = datetime.today() - daytime_map = { - "day": timedelta(hours=random.randrange(9, 18)), - "night": timedelta(hours=random.randrange(-3, 6)), - "dusk": timedelta(hours=random.randrange(18, 21)), - "dawn": timedelta(hours=random.randrange(6, 9)), - } - - time_period = None + start_time = datetime.combine(datetime.today(), time()) + time_range = None for k, v in RANDOM_TIME.items(): if random.randint(0, 100) <= v: - time_period = k + time_range = self.game.theater.daytime_map[k] break - print("generated {}".format(time_period)) - start_time += daytime_map[time_period] + start_time += timedelta(hours=random.randint(*time_range)) self.mission.start_time = start_time def _gen_random_weather(self): @@ -102,6 +95,16 @@ class EnvironmentSettingsGenerator: continue self.mission.terrain.airport_by_id(cp.at.id).set_coalition(cp.captured and player_coalition or enemy_coalition) + def _set_skill(self, player_coalition: str, enemy_coalition: str): + for coalition_name, coalition in self.mission.coalition.items(): + skill_level = player_coalition == coalition_name and self.game.player_skill or self.game.enemy_skill + for country in coalition.countries.values(): + for plane_group in country.plane_group: + plane_group.set_skill(Skill(skill_level)) + + for vehicle_group in country.vehicle_group: + vehicle_group.set_skill(Skill(skill_level)) + def generate(self, is_quick: bool): player_coalition = self.game.player == "USA" and "blue" or "red" enemy_coalition = player_coalition == "blue" and "red" or "blue" @@ -111,6 +114,7 @@ class EnvironmentSettingsGenerator: self._gen_random_time() self._gen_random_weather() + self._set_skill(player_coalition, enemy_coalition) self._set_allegiances(player_coalition, enemy_coalition) if not is_quick: diff --git a/gen/visualgen.py b/gen/visualgen.py new file mode 100644 index 00000000..07f01f0b --- /dev/null +++ b/gen/visualgen.py @@ -0,0 +1,126 @@ +import typing +import random +from datetime import datetime, timedelta + +from dcs.mission import Mission +from dcs.statics import * +from dcs.unit import Static + +from theater.conflicttheater import Conflict +#from game.game import Game + + +class Smoke(unittype.StaticType): + id = "big_smoke" + category = "Effects" + name = "big_smoke" + shape_name = 2 + rate = 100 + + +class BigSmoke(unittype.StaticType): + id = "big_smoke" + category = "Effects" + name = "big_smoke" + shape_name = 3 + rate = 100 + + +class MassiveSmoke(unittype.StaticType): + id = "big_smoke" + category = "Effects" + name = "big_smoke" + shape_name = 4 + rate = 100 + + +def __monkey_static_dict(self: Static): + global __original_static_dict + + d = __original_static_dict(self) + if self.type == "big_smoke": + d["effectPreset"] = self.shape_name + return d + + +__original_static_dict = Static.dict +Static.dict = __monkey_static_dict + +FRONT_SMOKE_MIN_DISTANCE = 10000 +FRONT_SMOKE_DISTANCE_FACTOR = 0.75 +FRONT_SMOKE_LENGTH = 80000 +FRONT_SMOKE_SPACING = 600 +FRONT_SMOKE_RANDOM_SPREAD = 1200 +FRONT_SMOKE_TYPE_CHANCES = { + 5: MassiveSmoke, + 40: BigSmoke, + 100: Smoke, +} + +DESTINATION_SMOKE_AMOUNT_FACTOR = 0.03 +DESTINATION_SMOKE_DISTANCE_FACTOR = 1 +DESTINATION_SMOKE_TYPE_CHANCES = { + 5: BigSmoke, + 100: Smoke, +} + + +def turn_heading(heading, fac): + heading += fac + if heading > 359: + heading = heading - 359 + if heading < 0: + heading = 359 + heading + return heading + + +class VisualGenerator: + game = None # type: Game + + def __init__(self, mission: Mission, conflict: Conflict, game): + self.mission = mission + self.conflict = conflict + self.game = game + + def _generate_frontline_smokes(self): + for from_cp, to_cp in self.game.theater.conflicts(): + distance = max(from_cp.position.distance_to_point(to_cp.position) * FRONT_SMOKE_DISTANCE_FACTOR * to_cp.base.strength, FRONT_SMOKE_MIN_DISTANCE) + heading = to_cp.position.heading_between_point(from_cp.position) + point = to_cp.position.point_from_heading(heading, distance) + plane_start = point.point_from_heading(turn_heading(heading, 90), FRONT_SMOKE_LENGTH / 2) + + for offset in range(0, FRONT_SMOKE_LENGTH, FRONT_SMOKE_SPACING): + position = plane_start.point_from_heading(turn_heading(heading, - 90), offset) + + for k, v in FRONT_SMOKE_TYPE_CHANCES.items(): + if random.randint(0, 100) <= k: + pos = position.random_point_within(FRONT_SMOKE_RANDOM_SPREAD, FRONT_SMOKE_RANDOM_SPREAD) + if not self.game.theater.is_on_land(pos): + break + + self.mission.static_group( + self.mission.country(self.game.enemy), + "", + _type=v, + position=pos) + break + + def generate_target_smokes(self, target): + spread = target.size * DESTINATION_SMOKE_DISTANCE_FACTOR + for _ in range(0, int(target.size * DESTINATION_SMOKE_AMOUNT_FACTOR * (1.1 - target.base.strength))): + for k, v in DESTINATION_SMOKE_TYPE_CHANCES.items(): + if random.randint(0, 100) <= k: + position = target.position.random_point_within(0, spread) + if not self.game.theater.is_on_land(position): + break + + self.mission.static_group( + self.mission.country(self.game.enemy), + "", + _type=v, + position=position) + + break + + def generate(self): + self._generate_frontline_smokes() diff --git a/resources/cau_terrain.miz b/resources/cau_terrain.miz new file mode 100644 index 00000000..6655e5ae Binary files /dev/null and b/resources/cau_terrain.miz differ diff --git a/resources/caulandmap.p b/resources/caulandmap.p new file mode 100644 index 00000000..df8dc635 Binary files /dev/null and b/resources/caulandmap.p differ diff --git a/resources/generate_landmap.py b/resources/generate_landmap.py new file mode 100644 index 00000000..324ad725 --- /dev/null +++ b/resources/generate_landmap.py @@ -0,0 +1,14 @@ +import pickle + +from dcs.mission import Mission +from dcs.terrain import PersianGulf + +m = Mission() +m.load_file("./gulf_terrain.miz") + +landmap = [] +for plane_group in m.country("USA").plane_group: + landmap.append([(x.position.x, x.position.y) for x in plane_group.points]) + +with open("gulflandmap.p", "wb") as f: + pickle.dump(landmap, f) diff --git a/resources/gulf_terrain.miz b/resources/gulf_terrain.miz new file mode 100644 index 00000000..ff0179df Binary files /dev/null and b/resources/gulf_terrain.miz differ diff --git a/resources/gulflandmap.p b/resources/gulflandmap.p new file mode 100644 index 00000000..59f83a97 Binary files /dev/null and b/resources/gulflandmap.p differ diff --git a/resources/nevada.gif b/resources/nevada.gif new file mode 100644 index 00000000..c73532c5 Binary files /dev/null and b/resources/nevada.gif differ diff --git a/resources/persiangulf.gif b/resources/persiangulf.gif new file mode 100644 index 00000000..42a360aa Binary files /dev/null and b/resources/persiangulf.gif differ diff --git a/theater/base.py b/theater/base.py index b0dcecd2..801cec34 100644 --- a/theater/base.py +++ b/theater/base.py @@ -127,7 +127,6 @@ class Base: def commit_losses(self, units_lost: typing.Dict[typing.Any, int]): for unit_type, count in units_lost.items(): - target_array = None if unit_type in self.aircraft: target_array = self.aircraft elif unit_type in self.armor: @@ -164,3 +163,7 @@ class Base: def assemble_defense(self, factor: float) -> typing.Dict[Armor, int]: return self._find_best_armor(CAP, math.ceil(self.total_armor * factor * self.strength)) + + def assemble_aa(self) -> typing.Dict[AirDefence, int]: + count = int(self.total_aa * (self.strength > 0.2 and self.strength or 0)) + return self._find_best_unit(self.aa, AirDefence, count) diff --git a/theater/caucasus.py b/theater/caucasus.py index 80e236b0..e2b83827 100644 --- a/theater/caucasus.py +++ b/theater/caucasus.py @@ -1,6 +1,7 @@ from dcs.terrain import caucasus from dcs import mapping +from .landmap import * from .conflicttheater import * from .base import * @@ -10,6 +11,13 @@ class CaucasusTheater(ConflictTheater): overview_image = "caumap.gif" reference_points = {(-317948.32727306, 635639.37385346): (282.5, 319), (-355692.3067714, 617269.96285781): (269, 352), } + landmap_poly = load_poly("resources\\caulandmap.p") + daytime_map = { + "dawn": (6, 9), + "day": (9, 18), + "dusk": (18, 21), + "night": (0, 5), + } soganlug = ControlPoint.from_airport(caucasus.Soganlug, ALL_RADIALS, SIZE_SMALL, IMPORTANCE_LOW) kutaisi = ControlPoint.from_airport(caucasus.Kutaisi, ALL_RADIALS, SIZE_SMALL, IMPORTANCE_LOW) @@ -32,7 +40,7 @@ class CaucasusTheater(ConflictTheater): mineralnye = ControlPoint.from_airport(caucasus.Mineralnye_Vody, ALL_RADIALS, SIZE_BIG, IMPORTANCE_MEDIUM) mozdok = ControlPoint.from_airport(caucasus.Mozdok, ALL_RADIALS, SIZE_BIG, IMPORTANCE_MEDIUM) - carrier_1 = ControlPoint.carrier("Carrier", mapping.Point(-355810.6875, 516399.1875)) + carrier_1 = ControlPoint.carrier("Carrier", mapping.Point(-305810.6875, 406399.1875)) def __init__(self): super(CaucasusTheater, self).__init__() @@ -63,3 +71,9 @@ class CaucasusTheater(ConflictTheater): self.carrier_1.captured = True self.soganlug.captured = True + self.sukhumi.captured = True + self.gudauta.base.strength = 0.5 + + self.kobuleti.captured = True + self.batumi.base.strength = 0.15 + diff --git a/theater/conflicttheater.py b/theater/conflicttheater.py index 82b120cf..b2242d4f 100644 --- a/theater/conflicttheater.py +++ b/theater/conflicttheater.py @@ -3,6 +3,7 @@ import itertools import dcs +from .landmap import ray_tracing from .controlpoint import * SIZE_TINY = 150 @@ -31,6 +32,8 @@ class ConflictTheater: controlpoints = None # type: typing.Collection[ControlPoint] reference_points = None # type: typing.Dict overview_image = None # type: str + landmap_poly = None + daytime_map = None # type: typing.Dict[str, typing.Tuple[int, int]] def __init__(self): self.controlpoints = [] @@ -41,6 +44,15 @@ class ConflictTheater: self.controlpoints.append(point) + def is_on_land(self, point: Point) -> bool: + if not self.landmap_poly: + return True + + for poly in self.landmap_poly: + return ray_tracing(point.x, point.y, poly) + + return False + def player_points(self) -> typing.Collection[ControlPoint]: return [point for point in self.controlpoints if point.captured] diff --git a/theater/landmap.py b/theater/landmap.py new file mode 100644 index 00000000..8c413162 --- /dev/null +++ b/theater/landmap.py @@ -0,0 +1,24 @@ +import pickle + + +def load_poly(filename: str): + with open(filename, "rb") as f: + return pickle.load(f) + + +def ray_tracing(x, y, poly): + n = len(poly) + inside = False + xints = 0.0 + p1x, p1y = poly[0] + for i in range(n+1): + p2x, p2y = poly[i % n] + if y > min(p1y, p2y): + if y <= max(p1y, p2y): + if x <= max(p1x, p2x): + if p1y != p2y: + xints = (y-p1y)*(p2x-p1x)/(p2y-p1y)+p1x + if p1x == p2x or x <= xints: + inside = not inside + p1x, p1y = p2x, p2y + return inside diff --git a/theater/nevada.py b/theater/nevada.py index dc386977..2239977a 100644 --- a/theater/nevada.py +++ b/theater/nevada.py @@ -10,6 +10,12 @@ 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), } + daytime_map = { + "dawn": (4, 6), + "day": (6, 17), + "dusk": (17, 19), + "night": (0, 5), + } mina = ControlPoint.from_airport(nevada.Mina_Airport_3Q0, ALL_RADIALS, SIZE_SMALL, IMPORTANCE_LOW) tonopah = ControlPoint.from_airport(nevada.Tonopah_Airport, ALL_RADIALS, SIZE_SMALL, IMPORTANCE_LOW) @@ -45,3 +51,5 @@ class NevadaTheater(ConflictTheater): self.add_controlpoint(self.laughlin, connected_to=[self.jean, self.las_vegas]) self.mina.captured = True + self.pahute_mesa.captured = True + self.groom_lake.captured = True diff --git a/theater/persiangulf.py b/theater/persiangulf.py index 2beaf1cb..11eb7aca 100644 --- a/theater/persiangulf.py +++ b/theater/persiangulf.py @@ -3,6 +3,7 @@ from dcs import mapping from .conflicttheater import * from .base import * +from .landmap import load_poly class PersianGulfTheater(ConflictTheater): @@ -10,6 +11,13 @@ class PersianGulfTheater(ConflictTheater): overview_image = "persiangulf.gif" reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (351, 115), (persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (389, 22), } + landmap_poly = load_poly("resources\\gulflandmap.p") + daytime_map = { + "dawn": (5, 7), + "day": (7, 17), + "dusk": (17, 19), + "night": (0, 5), + } al_dhafra = ControlPoint.from_airport(persiangulf.Al_Dhafra_AB, ALL_RADIALS, SIZE_BIG, IMPORTANCE_LOW) al_maktoum = ControlPoint.from_airport(persiangulf.Al_Maktoum_Intl, ALL_RADIALS, SIZE_BIG, IMPORTANCE_LOW) diff --git a/theater/start_generator.py b/theater/start_generator.py index b03cb739..035b3b21 100644 --- a/theater/start_generator.py +++ b/theater/start_generator.py @@ -1,11 +1,20 @@ +import math + from theater.base import * from theater.conflicttheater import * UNIT_VARIETY = 3 UNIT_AMOUNT_FACTOR = 16 +COUNT_BY_TASK = { + CAP: 12, + FighterSweep: 16, + CAS: 8, + AirDefence: 0.5, +} -def generate_initial(theater: ConflictTheater, enemy: str): + +def generate_initial(theater: ConflictTheater, enemy: str, sams: bool): for cp in theater.enemy_points(): if cp.captured: continue @@ -15,10 +24,14 @@ def generate_initial(theater: ConflictTheater, enemy: str): assert cp.importance >= IMPORTANCE_LOW, "invalid importance {}".format(cp.importance) importance_factor = (cp.importance - IMPORTANCE_LOW) / (IMPORTANCE_HIGH - IMPORTANCE_LOW) - variety = int(UNIT_VARIETY + UNIT_VARIETY * importance_factor / 2) + variety = int(UNIT_VARIETY) unittypes = db.choose_units(task, importance_factor, variety, enemy) - count = max(int(importance_factor * UNIT_AMOUNT_FACTOR), 1) + if not sams: + unittypes = [x for x in unittypes if x not in db.SAM_BAN] + + count = max(COUNT_BY_TASK[task] * importance_factor, 1) count_per_type = max(int(float(count) / len(unittypes)), 1) for unit_type in unittypes: + print("{} - {} {}".format(cp.name, db.unit_type_name(unit_type), count_per_type)) cp.base.commision_units({unit_type: count_per_type}) diff --git a/ui/configurationmenu.py b/ui/configurationmenu.py index 7b62236d..dff72f8e 100644 --- a/ui/configurationmenu.py +++ b/ui/configurationmenu.py @@ -7,3 +7,26 @@ from ui.window import * class ConfigurationMenu(Menu): def __init__(self, window: Window, parent, game: Game): super(ConfigurationMenu, self).__init__(window, parent, game) + self.frame = window.right_pane + self.player_skill_var = StringVar() + self.player_skill_var.set(self.game.player_skill) + + self.enemy_skill_var = StringVar() + self.enemy_skill_var.set(self.game.enemy_skill) + + def dismiss(self): + self.game.player_skill = self.player_skill_var.get() + self.game.enemy_skill = self.enemy_skill_var.get() + super(ConfigurationMenu, self).dismiss() + + def display(self): + self.window.clear_right_pane() + + Label(self.frame, text="Player coalition skill").grid(row=0, column=0) + Label(self.frame, text="Enemy coalition skill").grid(row=1, column=0) + + OptionMenu(self.frame, self.player_skill_var, "Average", "Good", "High", "Excellent").grid(row=0, column=1) + OptionMenu(self.frame, self.enemy_skill_var, "Average", "Good", "High", "Excellent").grid(row=1, column=1) + + Button(self.frame, text="Back", command=self.dismiss).grid(row=2, column=0, columnspan=1) + diff --git a/ui/eventmenu.py b/ui/eventmenu.py index d19eb889..160f9f1c 100644 --- a/ui/eventmenu.py +++ b/ui/eventmenu.py @@ -30,9 +30,9 @@ class EventMenu(Menu): self.window.clear_right_pane() row = 0 - def label(text, _row=None, _column=None): + def label(text, _row=None, _column=None, sticky=None): nonlocal row - Label(self.frame, text=text).grid(row=_row and _row or row, column=_column and _column or 0) + Label(self.frame, text=text).grid(row=_row and _row or row, column=_column and _column or 0, sticky=sticky) if _row is None: row += 1 @@ -62,7 +62,12 @@ class EventMenu(Menu): row += 1 - Checkbutton(self.frame, text="AWACS", var=self.awacs).grid(row=row, column=2) + Button(self.frame, text="Commit", command=self.start).grid(column=1, row=row, sticky=E) + Button(self.frame, text="Back", command=self.dismiss).grid(column=2, row=row, sticky=E) + + awacs_enabled = self.game.budget >= AWACS_BUDGET_COST and NORMAL or DISABLED + Checkbutton(self.frame, text="AWACS ({}m)".format(AWACS_BUDGET_COST), var=self.awacs, state=awacs_enabled).grid(row=row, column=0, sticky=W) + row += 1 label("Aircraft") @@ -76,17 +81,14 @@ class EventMenu(Menu): scrable_row(unit_type, count) if not self.base.total_planes: - label("None") + label("None", sticky=W) label("Armor") for unit_type, count in self.base.armor.items(): scramble_armor_row(unit_type, count) if not self.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) + label("None", sticky=W) def start(self): if self.awacs.get() == 1: diff --git a/ui/mainmenu.py b/ui/mainmenu.py index 9bceb0ee..4f96a69f 100644 --- a/ui/mainmenu.py +++ b/ui/mainmenu.py @@ -2,6 +2,7 @@ import pickle from ui.basemenu import * from ui.overviewcanvas import * +from ui.configurationmenu import * from game.game import * from userdata import persistency @@ -41,7 +42,8 @@ class MainMenu(Menu): row += 1 Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1 - Button(self.frame, text="Pass turn", command=self.pass_turn).grid(column=0, row=0, sticky=NE) + Button(self.frame, text="Configuration", command=self.configuration_menu).grid(column=0, row=0, sticky=NE) + Button(self.frame, text="Pass turn", command=self.pass_turn).grid(column=0, row=0, sticky=N) Label(self.frame, text="Budget: {}m (+{}m)".format(self.game.budget, self.game.budget_reward_amount)).grid(column=0, row=0, sticky=NW) Separator(self.frame, orient='horizontal').grid(row=row, sticky=EW); row += 1 @@ -58,10 +60,16 @@ class MainMenu(Menu): self.game.pass_turn(no_action=True) self.display() + def configuration_menu(self): + ConfigurationMenu(self.window, self, self.game).display() + def start_event(self, event) -> typing.Callable: return lambda: EventMenu(self.window, self, self.game, event).display() def go_cp(self, cp: ControlPoint): + if not cp.captured: + return + if self.basemenu: self.basemenu.dismiss() self.basemenu = None diff --git a/ui/newgamemenu.py b/ui/newgamemenu.py index 8f9be887..cf87e9a9 100644 --- a/ui/newgamemenu.py +++ b/ui/newgamemenu.py @@ -7,6 +7,7 @@ from ui.window import * class NewGameMenu(Menu): selected_country = None # type: IntVar selected_terrain = None # type: IntVar + sams = True def __init__(self, window: Window, callback: typing.Callable): super(NewGameMenu, self).__init__(window, None, None) @@ -19,6 +20,9 @@ class NewGameMenu(Menu): self.selected_terrain = IntVar() self.selected_terrain.set(0) + self.sams = BooleanVar() + self.sams.set(1) + @property def player_country_name(self): if self.selected_country.get() == 0: @@ -53,7 +57,11 @@ class NewGameMenu(Menu): Radiobutton(self.frame, text="Caucasus", variable=self.selected_terrain, value=0).grid(row=1, column=1) Radiobutton(self.frame, text="Nevada", variable=self.selected_terrain, value=1).grid(row=2, column=1) Radiobutton(self.frame, text="Persian Gulf", variable=self.selected_terrain, value=2).grid(row=3, column=1) - Button(self.frame, text="Proceed", command=self.proceed).grid(row=4, column=0, columnspan=2) + + Label(self.frame, text="Options").grid(row=1, column=2) + Checkbutton(self.frame, text="SAMs", variable=self.sams).grid(row=1, column=2) + + Button(self.frame, text="Proceed", command=self.proceed).grid(row=4, column=0, columnspan=3) def proceed(self): - self.callback(self.player_country_name, self.enemy_country_name, self.terrain_name) + self.callback(self.player_country_name, self.enemy_country_name, self.terrain_name, bool(self.sams.get()))