Generate CAP on whole map, for 2 hours. Added China.

This commit is contained in:
Khopa 2019-10-07 01:57:36 +02:00
parent 2167953b87
commit 7a14376765
17 changed files with 132 additions and 1802 deletions

View File

@ -45,7 +45,10 @@ PRICES = {
Su_33: 22,
MiG_29A: 18,
MiG_29S: 20,
MiG_29G: 18,
MiG_31: 30,
J_11A: 26,
Su_30: 24,
F_5E_3: 8,
MiG_15bis: 4,
@ -62,6 +65,8 @@ PRICES = {
F_15C: 26,
F_16C_bl_52d: 20,
F_14B: 22,
Tornado_IDS: 24,
# Tornado_GR4: 24,
# bomber
Su_17M4: 10,
@ -200,7 +205,10 @@ UNIT_BY_TASK = {
M_2000C,
Mirage_2000_5,
P_51D_30_NA,
P_51D
P_51D,
MiG_29G,
Su_30,
J_11A
],
CAS: [
F_86F_Sabre,
@ -224,6 +232,8 @@ UNIT_BY_TASK = {
OH_58D,
B_52H,
B_1B,
Tornado_IDS,
# Tornado_GR4,
],
Transport: [
IL_76MD,
@ -379,8 +389,12 @@ CARRIER_TAKEOFF_BAN = [
AirDefense units that will be spawned at control points not related to the current operation
"""
EXTRA_AA = {
"Russia": AirDefence.SAM_SA_8_Osa_9A33,
"Russia": AirDefence.SAM_SA_9_Strela_1_9P31,
"USA": AirDefence.SAM_Linebacker_M6,
"France": AirDefence.SPAAA_Gepard,
"Germany": AirDefence.SPAAA_Gepard,
"China": AirDefence.SPAAA_ZSU_23_4_Shilka,
"UK": AirDefence.AAA_Vulcan_M163,
"Russia 1955": AirDefence.AAA_ZU_23_Closed,
"USA 1955": AirDefence.AAA_Vulcan_M163,
"Russia 1965": AirDefence.AAA_ZU_23_Closed,
@ -389,7 +403,9 @@ EXTRA_AA = {
"Russia 1988": AirDefence.AAA_ZU_23_Closed,
"USA 1990": AirDefence.AAA_Vulcan_M163,
"France 1990": AirDefence.AAA_Vulcan_M163,
"Germany 1990": AirDefence.AAA_Vulcan_M163
"Germany 1990": AirDefence.AAA_Vulcan_M163,
"Iran 2015": AirDefence.SPAAA_ZSU_23_4_Shilka,
"China 2015": AirDefence.SPAAA_ZSU_23_4_Shilka
}
"""
@ -596,13 +612,9 @@ FACTIONS = {
Mi_8MT,
AirDefence.SPAAA_ZSU_23_4_Shilka,
AirDefence.SAM_SA_9_Strela_1_9P31,
AirDefence.SAM_SA_8_Osa_9A33,
AirDefence.AAA_ZU_23_Closed,
AirDefence.SAM_SA_19_Tunguska_2S6,
AirDefence.SAM_SA_6_Kub_LN_2P25,
AirDefence.SAM_SA_3_S_125_LN_5P73,
AirDefence.SAM_SA_11_Buk_LN_9A310M1,
AirDefence.SAM_SA_10_S_300PS_LN_5P85C,
Armor.APC_BTR_80,
Armor.MBT_T_90,
@ -620,7 +632,7 @@ FACTIONS = {
},
"Iran 2015": {
"country": "Russia",
"country": "Iran",
"side": "red",
"units": [
@ -667,6 +679,46 @@ FACTIONS = {
]
},
"China 2000": {
"country": "China",
"side": "red",
"units": [
MiG_21Bis, # Standing as J-7
Su_30,
J_11A,
IL_76MD,
IL_78M,
An_26B,
An_30M,
Yak_40,
A_50,
Mi_8MT,
AirDefence.AAA_ZU_23_Closed,
AirDefence.Rapier_FSA_Launcher, # Standing as PL-9C Shorad
AirDefence.SAM_SA_10_S_300PS_LN_5P85C, # Standing as HQ-9+
AirDefence.SAM_SA_6_Kub_LN_2P25,
# TODO : ADD HQ-7 (need pydcs support)
Armor.MBT_T_55,
Armor.ZBD_04A,
Armor.IFV_BMP_1,
Unarmed.Transport_Ural_375,
Unarmed.Transport_UAZ_469,
Infantry.Soldier_AK,
CV_1143_5_Admiral_Kuznetsov,
Bulk_cargo_ship_Yakushev,
Dry_cargo_ship_Ivanov,
Tanker_Elnya_160
]
},
"USA 1955": {
"country": "USA",
"side": "blue",
@ -803,7 +855,7 @@ FACTIONS = {
},
"France 1990": {
"country": "USA",
"country": "France",
"side": "blue",
"units":[
M_2000C,
@ -820,8 +872,7 @@ FACTIONS = {
Unarmed.Transport_M818,
Infantry.Infantry_M4,
AirDefence.AAA_Vulcan_M163,
AirDefence.SAM_Linebacker_M6,
AirDefence.SAM_Roland_ADS,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
@ -830,10 +881,12 @@ FACTIONS = {
},
"Germany 1990": {
"country": "USA",
"country": "Germany",
"side": "blue",
"units":[
MiG_29G,
Tornado_IDS,
F_4E,
KC_135,
S_3B_Tanker,
@ -850,7 +903,7 @@ FACTIONS = {
Infantry.Infantry_M4,
AirDefence.SPAAA_Gepard,
AirDefence.SAM_Linebacker_M6,
AirDefence.SAM_Roland_ADS,
CVN_74_John_C__Stennis,
LHA_1_Tarawa,
@ -1153,7 +1206,7 @@ def _validate_db():
for country_units_list in FACTIONS.values():
if unit_type in country_units_list["units"]:
did_find = True
assert did_find, "{} not in country list".format(unit_type)
print("WARN : {} not in country list".format(unit_type))
# check prices
for unit_type in total_set:

View File

@ -125,6 +125,13 @@ class Operation:
# air support
self.airsupportgen.generate(self.is_awacs_enabled)
for cp in self.game.theater.controlpoints:
if not cp.captured:
self.airgen.generate_patrol_group(cp, self.current_mission.country(self.game.enemy_country))
else:
self.airgen.generate_patrol_group(cp, self.current_mission.country(self.game.player_country))
for i, tanker_type in enumerate(self.airsupportgen.generated_tankers):
self.briefinggen.append_frequency("Tanker {} ({})".format(TANKER_CALLSIGNS[i], tanker_type), "{}X/{} MHz AM".format(97+i, 130+i))

View File

@ -10,7 +10,7 @@ from dcs.mission import *
from dcs.unitgroup import *
from dcs.unittype import *
from dcs.task import *
from dcs.terrain.terrain import NoParkingSlotError
from dcs.terrain.terrain import NoParkingSlotError, RunwayOccupiedError
SPREAD_DISTANCE_FACTOR = 1, 2
ESCORT_ENGAGEMENT_MAX_DIST = 100000
@ -125,10 +125,13 @@ class AircraftConflictGenerator:
else:
group.set_frequency(251.0)
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None) -> FlyingGroup:
def _generate_at_airport(self, name: str, side: Country, unit_type: FlyingType, count: int, client_count: int, airport: Airport = None, start_type = None) -> FlyingGroup:
assert count > 0
assert unit is not None
if start_type is None:
start_type = self._start_type()
logging.info("airgen: {} for {} at {}".format(unit_type, side.id, airport))
return self.m.flight_group_from_airport(
country=side,
@ -136,7 +139,7 @@ class AircraftConflictGenerator:
aircraft_type=unit_type,
airport=self.m.terrain.airport_by_id(airport.id),
maintask=None,
start_type=self._start_type(),
start_type=start_type,
group_size=count,
parking_slots=None)
@ -293,6 +296,51 @@ class AircraftConflictGenerator:
self.escort_targets.append((group, group.points.index(waypoint)))
self._rtb_for(group, self.conflict.from_cp, at)
def generate_patrol_group(self, cp: ControlPoint, country):
aircraft = dict({k:v for k,v in cp.base.aircraft.items() if k in [u for u in db.UNIT_BY_TASK[CAP]]})
delta = random.randint(10, 20)
for i in range(12):
if(len(aircraft.keys())) > 0:
print(aircraft.keys())
type = random.choice(list(aircraft.keys()))
number = random.choice([2, 4])
if(number > aircraft[type]):
del aircraft[type]
else:
aircraft[type] = aircraft[type] - number
try:
group = self._generate_at_airport(
name=namegen.next_unit_name(country, type),
side=country,
unit_type=type,
count=number,
client_count=0,
airport=self.m.terrain.airport_by_id(cp.at.id),
start_type=StartType.Runway)
except RunwayOccupiedError:
group = self._generate_group(
name=namegen.next_unit_name(country, type),
side=country,
unit_type=type,
count=number,
client_count=0,
at=cp.position)
patrol_alt = random.randint(3600, 7000)
group.points[0].alt = patrol_alt
group.points[0].ETA = delta*60 + i*10*60
patrolled = []
for ground_object in cp.ground_objects:
if not ground_object.group_id in patrolled:
group.add_waypoint(ground_object.position, patrol_alt)
patrolled.append(ground_object.group_id)
def generate_ground_attack_strikegroup(self, strikegroup: db.PlaneDict, clients: db.PlaneDict, targets: typing.List[typing.Tuple[str, Point]], at: db.StartingPosition = None, escort=True):
assert not escort or len(self.escort_targets) == 0

View File

@ -91,7 +91,7 @@ class TriggersGenerator:
push_by_trigger.append(group)
if not group.units[0].is_human():
"""if not group.units[0].is_human():
regroup_heading = self.conflict.to_cp.position.heading_between_point(player_cp.position)
pos1 = group.position.point_from_heading(regroup_heading, REGROUP_ZONE_DISTANCE)
@ -112,7 +112,7 @@ class TriggersGenerator:
w2.tasks.append(switch_waypoint_task)
group.points[3].tasks.append(Silence(False))
group.add_trigger_action(SwitchWaypoint(to_waypoint=4))
group.add_trigger_action(SwitchWaypoint(to_waypoint=4))"""
push_trigger = TriggerOnce(Event.NoEvent, "Push trigger")
@ -187,7 +187,7 @@ class TriggersGenerator:
self.mission.triggerrules.triggers.append(trigger_two)
def generate(self, player_cp: ControlPoint, is_quick: bool, activation_trigger_radius: int, awacs_enabled: bool):
player_coalition = self.game.player_country == "USA" and "blue" or "red"
player_coalition = self.game.player_country in ["USA", "France", "Germany", "Uk"] and "blue" or "red"
enemy_coalition = player_coalition == "blue" and "red" or "blue"
self.mission.coalition[player_coalition].bullseye = {"x": self.conflict.position.x,

View File

@ -81,14 +81,14 @@ class QMapControlPoint(QGraphicsRectItem):
@property
def brush_color(self)->QColor:
if self.parent.game.player_country == "USA":
if self.parent.game.player_country in ["USA", "France", "Germany", "UK"]:
return self.model.captured and CONST.COLORS["blue"] or CONST.COLORS["red"]
else:
return self.model.captured and CONST.COLORS["red"] or CONST.COLORS["blue"]
@property
def pen_color(self) -> QColor:
if self.parent.game.player_country == "USA":
if self.parent.game.player_country in ["USA", "France", "Germany", "UK"]:
return self.model.captured and CONST.COLORS["dark_blue"] or CONST.COLORS["bright_red"]
else:
return self.model.captured and CONST.COLORS["bright_red"] or CONST.COLORS["dark_blue"]

View File

@ -25,7 +25,7 @@ class QMapGroundObject(QGraphicsRectItem):
tooltip = ""
for unit in units.keys():
tooltip = tooltip + str(unit) + "x" + str(units[unit]) + "\n"
self.setToolTip(tooltip + str(model.groups[0].id) + str(model.groups[0].name))
self.setToolTip(tooltip[:-1])
else:
self.setToolTip(cp.name + "'s " + self.model.category)

View File

View File

@ -1,116 +0,0 @@
from ui.eventmenu import *
from game.game import *
from .styles import STYLES
class BaseMenu(Menu):
bought_amount_labels = None # type: typing.Collection[Label]
budget_label = None # type: Label
def __init__(self, window: Window, parent, game: Game, cp: ControlPoint):
super(BaseMenu, self).__init__(window, parent, game)
self.cp = cp
self.base = cp.base
self.frame = window.right_pane
self.event = self.game.units_delivery_event(cp)
self.bought_amount_labels = {}
def display(self):
self.window.clear_right_pane()
units = {
CAP: db.find_unittype(CAP, self.game.player_name),
Embarking: db.find_unittype(Embarking, self.game.player_name),
AirDefence: db.find_unittype(AirDefence, self.game.player_name),
CAS: db.find_unittype(CAS, self.game.player_name),
PinpointStrike: db.find_unittype(PinpointStrike, self.game.player_name),
}
# Header
head = Frame(self.frame, **STYLES["header"])
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)
self.budget_label = Label(self.frame, text="Budget: {}m".format(self.game.budget), **STYLES["widget"])
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))
tasks = list(units.keys())
tasks_per_column = 3
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:
self.game.units_delivery_remove(self.event)
super(BaseMenu, self).dismiss()
def _update_count_label(self, unit_type: UnitType):
self.bought_amount_labels[unit_type]["text"] = "({}{})".format(
self.cp.base.total_units_of_type(unit_type),
unit_type in self.event.units and ", bought {}".format(self.event.units[unit_type]) or ""
)
self.budget_label["text"] = "Budget: {}m".format(self.game.budget)
def buy(self, unit_type):
def action():
price = db.PRICES[unit_type]
if self.game.budget >= price:
self.event.deliver({unit_type: 1})
self.game.budget -= price
self._update_count_label(unit_type)
return action
def sell(self, unit_type):
def action():
if self.event.units.get(unit_type, 0) > 0:
price = db.PRICES[unit_type]
self.game.budget += price
self.event.units[unit_type] = self.event.units[unit_type] - 1
if self.event.units[unit_type] == 0:
del self.event.units[unit_type]
elif self.base.total_units_of_type(unit_type) > 0:
price = db.PRICES[unit_type]
self.game.budget += price
self.base.commit_losses({unit_type: 1})
self._update_count_label(unit_type)
return action

View File

@ -1,125 +0,0 @@
import webbrowser
from tkinter import *
from tkinter.ttk import *
from .styles import STYLES
from userdata.logging import ShowLogsException
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.settings.player_skill)
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.map_coalition_visibility_var = StringVar()
self.map_coalition_visibility_var.set(self.game.settings.map_coalition_visibility)
self.labels_var = StringVar()
self.labels_var.set(self.game.settings.labels)
self.takeoff_var = BooleanVar()
self.takeoff_var.set(self.game.settings.only_player_takeoff)
self.night_var = BooleanVar()
self.night_var.set(self.game.settings.night_disabled)
self.cold_start_var = BooleanVar()
self.cold_start_var.set(self.game.settings.cold_start)
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.map_coalition_visibility = self.map_coalition_visibility_var.get()
self.game.settings.labels = self.labels_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()
super(ConfigurationMenu, self).dismiss()
def display(self):
self.window.clear_right_pane()
# Header
head = Frame(self.frame, **STYLES["header"])
head.grid(row=0, column=0, sticky=NSEW)
head.grid_columnconfigure(0, weight=100)
Label(head, text="Configuration", **STYLES["title"]).grid(row=0, sticky=W)
Button(head, text="Back", command=self.dismiss, **STYLES["btn-primary"]).grid(row=0, column=1, sticky=E)
# Body
body = Frame(self.frame, **STYLES["body"])
body.grid(row=1, column=0, sticky=NSEW)
row = 0
Label(body, text="Player coalition skill", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
p_skill = OptionMenu(body, self.player_skill_var, "Average", "Good", "High", "Excellent")
p_skill.grid(row=row, column=1, sticky=E, pady=5)
p_skill.configure(**STYLES["btn-primary"])
row += 1
Label(body, text="Enemy coalition skill", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
e_skill = OptionMenu(body, self.enemy_skill_var, "Average", "Good", "High", "Excellent")
e_skill.grid(row=row, column=1, sticky=E)
e_skill.configure(**STYLES["btn-primary"])
row += 1
Label(body, text="Enemy AA and vehicle skill", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
e_skill = OptionMenu(body, self.enemy_vehicle_var, "Average", "Good", "High", "Excellent")
e_skill.grid(row=row, column=1, sticky=E)
e_skill.configure(**STYLES["btn-primary"])
row += 1
Label(body, text="F10 Map Coalition Visibility", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
map_vis = OptionMenu(body, self.map_coalition_visibility_var, "All Units", "Allied Units", "Own Aircraft", "None")
map_vis.grid(row=row, column=1, sticky=E)
map_vis.configure(**STYLES["btn-primary"])
row += 1
Label(body, text="In Game Labels", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
g_labels = OptionMenu(body, self.labels_var, "Full", "Abbreviated", "Dot Only", "Off")
g_labels.grid(row=row, column=1, sticky=E)
g_labels.configure(**STYLES["btn-primary"])
row += 1
Label(body, text="Aircraft cold start", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
Checkbutton(body, variable=self.cold_start_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E)
row += 1
Label(body, text="Takeoff only for player group", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
Checkbutton(body, variable=self.takeoff_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E)
row += 1
Label(body, text="Disable night missions", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
Checkbutton(body, variable=self.night_var, **STYLES["radiobutton"]).grid(row=row, column=1, sticky=E)
row += 1
Label(body, text="Contributors: ", **STYLES["strong"]).grid(row=row, column=0, columnspan=2, sticky=EW)
row += 1
Label(body, text="shdwp - author, maintainer", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/shdwp"), **STYLES["widget"]).grid(row=row, column=1, sticky=E)
row += 1
Label(body, text="Khopa - contributions", **STYLES["widget"]).grid(row=row, column=0, sticky=W)
Button(body, text="[github]", command=lambda: webbrowser.open_new_tab("http://github.com/Khopa"), **STYLES["widget"]).grid(row=row, column=1, sticky=E)
row += 1
Button(body, text="Display logs", command=self.display_logs, **STYLES["btn-primary"]).grid(row=row, column=0, pady=5)
Button(body, text="Cheat +200m", command=self.cheat_money, **STYLES["btn-danger"]).grid(row=row, column=1)
def display_logs(self):
raise ShowLogsException()
def cheat_money(self):
self.game.budget += 200

View File

@ -1,18 +0,0 @@
from tkinter import *
from tkinter.ttk import *
from .styles import STYLES
from ui.window import *
class CorruptedSaveMenu(Menu):
def __init__(self, window: Window):
super(CorruptedSaveMenu, self).__init__(window, None, None)
self.frame = window.right_pane
def display(self):
self.window.clear_right_pane()
Label(text="Your save game is either incompatible or was corrupted!", **STYLES["widget"]).grid(row=0, column=0)
Label(text="Please restore it by replacing \"liberation_save\" file with \"liberation_save_tmp\" to restore last saved copy.", **STYLES["widget"]).grid(row=1, column=0)
Label(text="You can find those files under user Saved Games\\DCS directory.", **STYLES["widget"]).grid(row=2, column=0)

View File

@ -1,219 +0,0 @@
from dcs.helicopters import helicopter_map
from ui.eventresultsmenu import *
from game import *
from game.event import *
from .styles import STYLES, RED
class EventMenu(Menu):
scramble_entries = None # type: typing.Dict[typing.Type[Task], typing.Dict[typing.Type[UnitType], typing.Tuple[Entry, Entry]]]
ca_slot_entry = None # type: Entry
error_label = None # type: Label
awacs = None # type: IntVar
def __init__(self, window: Window, parent, game: Game, event: event.Event):
super(EventMenu, self).__init__(window, parent, game)
self.event = event
self.scramble_entries = {k: {} for k in self.event.tasks}
if self.event.attacker_name == self.game.player_name:
self.base = self.event.departure_cp.base
else:
self.base = self.event.to_cp.base
self.frame = self.window.right_pane
self.awacs = IntVar()
def display(self):
self.window.clear_right_pane()
row = 0
def header(text, style="strong"):
nonlocal row
head = Frame(self.frame, **STYLES["header"])
head.grid(row=row, column=0, sticky=N+EW, columnspan=5)
Label(head, text=text, **STYLES[style]).grid()
row += 1
def label(text, _row=None, _column=None, columnspan=None, sticky=None):
nonlocal row
new_label = Label(self.frame, text=text, **STYLES["widget"])
new_label.grid(row=_row and _row or row, column=_column and _column or 0, columnspan=columnspan, sticky=sticky)
if _row is None:
row += 1
return new_label
def scrable_row(task_type, unit_type, unit_count, client_slots: bool):
nonlocal row
Label(self.frame, text="{} ({})".format(db.unit_type_name(unit_type), unit_count), **STYLES["widget"]).grid(row=row, sticky=W)
scramble_entry = Entry(self.frame, width=2)
scramble_entry.grid(column=1, row=row, sticky=E, padx=5)
scramble_entry.insert(0, "0")
Button(self.frame, text="+", command=self.scramble_half(task_type, unit_type), **STYLES["btn-primary"]).grid(column=2, row=row)
if client_slots:
client_entry = Entry(self.frame, width=2)
client_entry.grid(column=3, row=row, sticky=E, padx=5)
client_entry.insert(0, "0")
Button(self.frame, text="+", command=self.client_one(task_type, unit_type), **STYLES["btn-primary"]).grid(column=4, row=row)
else:
client_entry = None
self.scramble_entries[task_type][unit_type] = scramble_entry, client_entry
row += 1
# Header
header("Mission Menu", "title")
# Mission Description
Label(self.frame, text="{}".format(self.event), **STYLES["mission-preview"]).grid(row=row, column=0, columnspan=5, sticky=S+EW, padx=5, pady=5)
row += 1
Label(self.frame, text="Amount", **STYLES["widget"]).grid(row=row, column=1, columnspan=2)
Label(self.frame, text="Client slots", **STYLES["widget"]).grid(row=row, column=3, columnspan=2)
row += 1
for flight_task in self.event.tasks:
header("{}:".format(self.event.flight_name(flight_task)))
if flight_task == PinpointStrike:
if not self.base.armor:
label("No units")
for t, c in self.base.armor.items():
scrable_row(flight_task, t, c, client_slots=False)
else:
if not self.base.aircraft:
label("No units")
for t, c in self.base.aircraft.items():
scrable_row(flight_task, t, c, client_slots=True)
header("Support:")
# Options
awacs_enabled = self.game.budget >= AWACS_BUDGET_COST and NORMAL or DISABLED
Label(self.frame, text="AWACS ({}m)".format(AWACS_BUDGET_COST), **STYLES["widget"]).grid(row=row, column=0, sticky=W, pady=5)
Checkbutton(self.frame, var=self.awacs, state=awacs_enabled, **STYLES["radiobutton"]).grid(row=row, column=4, sticky=E)
row += 1
Label(self.frame, text="Combined Arms Slots", **STYLES["widget"]).grid(row=row, sticky=W)
self.ca_slot_entry = Entry(self.frame, width=2)
self.ca_slot_entry.insert(0, "0")
self.ca_slot_entry.grid(column=3, row=row, sticky=E, padx=5)
Button(self.frame, text="+", command=self.add_ca_slot, **STYLES["btn-primary"]).grid(column=4, row=row, padx=5, sticky=W)
row += 1
header("Ready?")
self.error_label = label("", columnspan=4)
self.error_label["fg"] = RED
Button(self.frame, text="Commit", command=self.start, **STYLES["btn-primary"]).grid(column=0, row=row, sticky=E, padx=5, pady=(10,10))
Button(self.frame, text="Back", command=self.dismiss, **STYLES["btn-warning"]).grid(column=3, row=row, sticky=E, padx=5, pady=(10,10))
row += 1
def scramble_half(self, task: typing.Type[UnitType], unit_type: UnitType) -> typing.Callable:
def action():
entry = self.scramble_entries[task][unit_type][0] # type: Entry
value = entry.get()
total_units = self.base.total_units_of_type(unit_type)
amount = int(value and value or "0")
entry.delete(0, END)
entry.insert(0, str(amount + int(math.ceil(total_units/2))))
return action
def add_ca_slot(self):
value = self.ca_slot_entry.get()
amount = int(value and value or "0")
self.ca_slot_entry.delete(0, END)
self.ca_slot_entry.insert(0, str(amount+1))
def client_one(self, task: typing.Type[Task], unit_type: UnitType) -> typing.Callable:
def action():
entry = self.scramble_entries[task][unit_type][1] # type: Entry
value = entry.get()
amount = int(value and value or "0")
entry.delete(0, END)
entry.insert(0, str(amount+1))
return action
def start(self):
if self.awacs.get() == 1:
self.event.is_awacs_enabled = True
self.game.awacs_expense_commit()
else:
self.event.is_awacs_enabled = False
ca_slot_entry_value = self.ca_slot_entry.get()
try:
ca_slots = int(ca_slot_entry_value and ca_slot_entry_value or "0")
except:
ca_slots = 0
self.event.ca_slots = ca_slots
flights = {k: {} for k in self.event.tasks} # type: db.TaskForceDict
units_scramble_counts = {} # type: typing.Dict[typing.Type[UnitType], int]
tasks_scramble_counts = {} # type: typing.Dict[typing.Type[Task], int]
tasks_clients_counts = {} # type: typing.Dict[typing.Type[Task], int]
def dampen_count(for_task: typing.Type[Task], unit_type: typing.Type[UnitType], count: int) -> int:
nonlocal units_scramble_counts
total_count = self.base.total_units_of_type(unit_type)
total_scrambled = units_scramble_counts.get(unit_type, 0)
dampened_value = count if count + total_scrambled < total_count else total_count - total_scrambled
units_scramble_counts[unit_type] = units_scramble_counts.get(unit_type, 0) + dampened_value
return dampened_value
for task_type, dict in self.scramble_entries.items():
for unit_type, (count_entry, clients_entry) in dict.items():
try:
count = int(count_entry.get())
except:
count = 0
try:
clients_count = int(clients_entry and clients_entry.get() or 0)
except:
clients_count = 0
dampened_count = dampen_count(task_type, unit_type, count)
tasks_clients_counts[task_type] = tasks_clients_counts.get(task_type, 0) + clients_count
tasks_scramble_counts[task_type] = tasks_scramble_counts.get(task_type, 0) + dampened_count
flights[task_type][unit_type] = dampened_count, clients_count
for task in self.event.ai_banned_tasks:
if tasks_clients_counts.get(task, 0) == 0 and tasks_scramble_counts.get(task, 0) > 0:
self.error_label["text"] = "Need at least one player in flight {}".format(self.event.flight_name(task))
return
for task in self.event.player_banned_tasks:
if tasks_clients_counts.get(task, 0) != 0:
self.error_label["text"] = "Players are not allowed on flight {}".format(self.event.flight_name(task))
return
if self.game.is_player_attack(self.event):
if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent):
if self.event.from_cp.base.total_armor == 0:
self.error_label["text"] = "No ground vehicles available to attack!"
return
self.event.player_attacking(flights)
else:
if isinstance(self.event, FrontlineAttackEvent) or isinstance(self.event, FrontlinePatrolEvent):
if self.event.to_cp.base.total_armor == 0:
self.error_label["text"] = "No ground vehicles available to defend!"
return
self.event.player_defending(flights)
self.game.initiate_event(self.event)
EventResultsMenu(self.window, self.parent, self.game, self.event).display()

View File

@ -1,180 +0,0 @@
from tkinter.ttk import *
from ui.window import *
from game.game import *
from userdata.debriefing import *
from .styles import STYLES
class EventResultsMenu(Menu):
debriefing = None # type: Debriefing
player_losses = {} # type: typing.Dict[UnitType, int]
enemy_losses = {} # type: typing.Dict[UnitType, int]
def __init__(self, window: Window, parent, game: Game, event: Event):
super(EventResultsMenu, self).__init__(window, parent, game)
self.frame = window.right_pane
self.frame.grid_rowconfigure(0, weight=0)
self.event = event
self.finished = False
wait_for_debriefing(callback=self.process_debriefing)
def display(self):
self.window.clear_right_pane()
row = 0
def header(text, style="strong"):
nonlocal row
head = Frame(self.frame, **STYLES["header"])
head.grid(row=row, column=0, sticky=N + EW, columnspan=2, pady=(0, 10))
Label(head, text=text, **STYLES[style]).grid()
row += 1
def label(text, style="widget"):
nonlocal row
Label(self.frame, text=text, **STYLES[style]).grid(row=row, column=0, sticky=NW, columnspan=2)
row += 1
if not self.finished:
header("You are clear for takeoff!")
label("In DCS, open and play the mission:")
label("liberation_nextturn", "italic")
label("or")
label("liberation_nextturn_quick", "italic")
header("Then save the debriefing to the folder:")
label(debriefing_directory_location(), "italic")
header("Waiting for results...")
pg = Progressbar(self.frame, orient="horizontal", length=200, mode="determinate")
pg.grid(row=row, column=0, columnspan=2, sticky=EW, pady=5, padx=5)
pg.start(10)
row += 1
"""
Label(self.frame, text="Cheat operation results: ", **STYLES["strong"]).grid(column=0, row=row,
columnspan=2, sticky=NSEW,
pady=5)
row += 1
Button(self.frame, text="full enemy losses", command=self.simulate_result(0, 1),
**STYLES["btn-warning"]).grid(column=0, row=row, padx=5, pady=5)
Button(self.frame, text="full player losses", command=self.simulate_result(1, 0),
**STYLES["btn-warning"]).grid(column=1, row=row, padx=5, pady=5)
row += 1
Button(self.frame, text="some enemy losses", command=self.simulate_result(0, 0.8),
**STYLES["btn-warning"]).grid(column=0, row=row, padx=5, pady=5)
Button(self.frame, text="some player losses", command=self.simulate_result(0.8, 0),
**STYLES["btn-warning"]).grid(column=1, row=row, padx=5, pady=5)
row += 1
"""
else:
row = 0
if self.event.is_successfull(self.debriefing):
header("Operation success", "title-green")
else:
header("Operation failed", "title-red")
header("Player losses")
for unit_type, count in self.player_losses.items():
Label(self.frame, text=db.unit_type_name(unit_type), **STYLES["widget"]).grid(row=row)
Label(self.frame, text="{}".format(count), **STYLES["widget"]).grid(column=1, row=row)
row += 1
header("Enemy losses")
if self.debriefing.destroyed_objects:
Label(self.frame, text="Ground assets", **STYLES["widget"]).grid(row=row)
Label(self.frame, text="{}".format(len(self.debriefing.destroyed_objects)), **STYLES["widget"]).grid(column=1, row=row)
row += 1
for unit_type, count in self.enemy_losses.items():
if count == 0:
continue
Label(self.frame, text=db.unit_type_name(unit_type), **STYLES["widget"]).grid(row=row)
Label(self.frame, text="{}".format(count), **STYLES["widget"]).grid(column=1, row=row)
row += 1
Button(self.frame, text="Okay", command=self.dismiss, **STYLES["btn-primary"]).grid(columnspan=1, row=row)
row += 1
def process_debriefing(self, debriefing: Debriefing):
self.debriefing = debriefing
debriefing.calculate_units(regular_mission=self.event.operation.regular_mission,
quick_mission=self.event.operation.quick_mission,
player_country=self.game.player_country,
enemy_country=self.game.enemy_country)
self.game.finish_event(event=self.event, debriefing=debriefing)
self.game.pass_turn(ignored_cps=[self.event.to_cp, ])
self.finished = True
self.player_losses = debriefing.destroyed_units.get(self.game.player_country, {})
self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy_country, {})
self.display()
def simulate_result(self, player_factor: float, enemy_factor: float):
def action():
debriefing = Debriefing({}, [])
def count(country: Country) -> typing.Dict[UnitType, int]:
result = {}
for g in country.plane_group + country.vehicle_group + country.helicopter_group + country.ship_group:
group = g # type: Group
for unit in group.units:
unit_type = None
if isinstance(unit, Vehicle):
unit_type = vehicle_map[unit.type]
elif isinstance(unit, Ship):
unit_type = ship_map[unit.type]
else:
unit_type = unit.unit_type
if unit_type in db.EXTRA_AA.values():
continue
result[unit_type] = result.get(unit_type, 0) + 1
return result
player = self.event.operation.mission.country(self.game.player_country)
enemy = self.event.operation.mission.country(self.game.enemy_country)
alive_player_units = count(player)
alive_enemy_units = count(enemy)
destroyed_player_units = db.unitdict_restrict_count(alive_player_units, math.ceil(
sum(alive_player_units.values()) * player_factor))
destroyed_enemy_units = db.unitdict_restrict_count(alive_enemy_units, math.ceil(
sum(alive_enemy_units.values()) * enemy_factor))
alive_player_units = {k: v - destroyed_player_units.get(k, 0) for k, v in alive_player_units.items()}
alive_enemy_units = {k: v - destroyed_enemy_units.get(k, 0) for k, v in alive_enemy_units.items()}
debriefing.alive_units = {
enemy.name: alive_enemy_units,
player.name: alive_player_units,
}
debriefing.destroyed_units = {
player.name: destroyed_player_units,
enemy.name: destroyed_enemy_units,
}
self.finished = True
self.debriefing = debriefing
self.player_losses = debriefing.destroyed_units.get(self.game.player_country, {})
self.enemy_losses = debriefing.destroyed_units.get(self.game.enemy_country, {})
self.game.finish_event(self.event, debriefing)
self.display()
self.game.pass_turn()
return action

View File

@ -1,58 +0,0 @@
from game.game import *
from ui.basemenu import *
from ui.configurationmenu import *
from ui.overviewcanvas import *
from userdata import persistency
from .styles import STYLES
import tkinter as tk
from tkinter import ttk
class MainMenu(Menu):
basemenu = None # type: BaseMenu
def __init__(self, window: Window, parent, game: Game):
super(MainMenu, self).__init__(window, parent, game)
self.upd = OverviewCanvas(self.window.left_pane, self, game)
self.upd.update()
self.frame = self.window.right_pane
self.frame.rowconfigure(0, weight=0)
self.frame.rowconfigure(1, weight=1)
def display(self):
persistency.save_game(self.game)
self.window.clear_right_pane()
self.upd.update()
header = Frame(self.frame, **STYLES["header"])
header.grid(column=0, row=0, sticky=NSEW)
def pass_turn(self):
self.game.pass_turn(no_action=True)
self.upd.update()
self.display()
def configuration_menu(self):
ConfigurationMenu(self.window, self, self.game).display()
def start_event(self, event) -> typing.Callable:
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
self.basemenu = BaseMenu(self.window, self, self.game, cp)
self.basemenu.display()

View File

@ -1,171 +0,0 @@
import os
from tkinter import *
from tkinter.ttk import *
from ui.window import *
from .styles import STYLES
from game import db
class NewGameMenu(Menu):
selected_country = None # type: IntVar
selected_terrain = None # type: IntVar
sams = None
midgame = None
multiplier = None
def __init__(self, window: Window, callback: typing.Callable):
super(NewGameMenu, self).__init__(window, None, None)
self.frame = window.right_pane
window.left_pane.configure(background="black")
self.callback = callback
self.selected_country = IntVar()
self.selected_country.set(0)
self.selected_terrain = IntVar()
self.selected_terrain.set(0)
self.selected_time_period = StringVar()
self.selected_blue_faction = StringVar()
self.selected_red_faction = StringVar()
self.sams = BooleanVar()
self.sams.set(1)
self.multiplier = StringVar()
self.multiplier.set("1")
self.midgame = BooleanVar()
self.midgame.set(0)
@property
def player_country_name(self):
if self.selected_country.get() == 0:
return self.selected_blue_faction.get()
else:
return self.selected_red_faction.get()
@property
def enemy_country_name(self):
if self.selected_country.get() == 1:
return self.selected_blue_faction.get()
else:
return self.selected_red_faction.get()
@property
def terrain_name(self) -> str:
if self.selected_terrain.get() == 0:
return "caucasus"
elif self.selected_terrain.get() == 1:
return "nevada"
else:
return "persiangulf"
def display(self):
self.window.clear_right_pane()
# Header
head = Frame(self.frame, **STYLES["header"])
head.grid(row=0, column=0, sticky=NSEW)
Label(head, text="Start a new game", **STYLES["title"]).grid()
# Body
body = Frame(self.frame, **STYLES["body"])
body.grid(row=1, column=0, sticky=NSEW)
# Side Selection
side = LabelFrame(body, text="Player Side", **STYLES["label-frame"])
side.grid(row=2, column=0, sticky=NW, padx=5)
Radiobutton(side, variable=self.selected_country, value=0, **STYLES["radiobutton"]).grid(row=0, column=0,
sticky=W)
Label(side, text="BLUEFOR", **STYLES["widget"]).grid(row=0, column=1, sticky=W)
Radiobutton(side, variable=self.selected_country, value=1, **STYLES["radiobutton"]).grid(row=1, column=0,
sticky=W)
Label(side, text="REDFOR", **STYLES["widget"]).grid(row=1, column=1, sticky=W)
# Country Selection
blues = [c for c in db.FACTIONS if db.FACTIONS[c]["side"] == "blue"]
reds = [c for c in db.FACTIONS if db.FACTIONS[c]["side"] == "red"]
factions = LabelFrame(body, text="Factions", **STYLES["label-frame"])
factions.grid(row=0, column=0, sticky=NW, padx=5)
Label(factions, text="Blue Faction", **STYLES["widget"]).grid(row=1, column=0, sticky=SE)
self.selected_blue_faction.set(blues[0])
blue_select = OptionMenu(factions, self.selected_blue_faction, *blues)
blue_select.configure(**STYLES["btn-primary"])
blue_select.grid(row=1, column=1, sticky=W)
self.selected_red_faction.set(reds[1])
Label(factions, text="Red Faction", **STYLES["widget"]).grid(row=2, column=0, sticky=W)
red_select = OptionMenu(factions, self.selected_red_faction, *reds)
red_select.configure(**STYLES["btn-primary"])
red_select.grid(row=2, column=1, sticky=W)
# Terrain Selection
terrain = LabelFrame(body, text="Terrain", **STYLES["label-frame"])
terrain.grid(row=0, column=1, sticky=N, padx=5)
Radiobutton(terrain, variable=self.selected_terrain, value=0, **STYLES["radiobutton"]) \
.grid(row=0, column=0, sticky=W)
Label(terrain, text="Caucasus", **STYLES["widget"]).grid(row=0, column=1, sticky=W)
self.create_label_image(terrain, "terrain_caucasus.gif").grid(row=0, column=2, padx=5)
Radiobutton(terrain, variable=self.selected_terrain, value=1, **STYLES["radiobutton"]) \
.grid(row=1, column=0, sticky=W)
Label(terrain, text="Nevada", **STYLES["widget"]).grid(row=1, column=1, sticky=W)
self.create_label_image(terrain, "terrain_nevada.gif").grid(row=1, column=2, padx=5)
Radiobutton(terrain, variable=self.selected_terrain, value=2, **STYLES["radiobutton"]) \
.grid(row=2, column=0, sticky=W)
Label(terrain, text="Persian Gulf", **STYLES["widget"]).grid(row=2, column=1, sticky=W)
self.create_label_image(terrain, "terrain_pg.gif").grid(row=2, column=2, padx=5)
# Period selection
period = LabelFrame(body, text="Time Period", **STYLES["label-frame"])
period.grid(row=0, column=2, sticky=N, padx=5)
vals = list(db.TIME_PERIODS)
self.selected_time_period.set(vals[21])
period_select = OptionMenu(period, self.selected_time_period, *vals)
period_select.configure(**STYLES["btn-primary"])
period_select.grid(row=0, column=0, sticky=W)
#Label(terrain, text="Caucasus", **STYLES["widget"]).grid(row=0, column=1, sticky=W)
# Misc Options
options = LabelFrame(body, text="Misc Options", **STYLES["label-frame"])
options.grid(row=0, column=3, sticky=NE, padx=5)
Checkbutton(options, variable=self.sams, **STYLES["radiobutton"]).grid(row=0, column=0, sticky=W)
Label(options, text="SAMs", **STYLES["widget"]).grid(row=0, column=1, sticky=W)
Checkbutton(options, variable=self.midgame, **STYLES["radiobutton"]).grid(row=1, column=0, sticky=W)
Label(options, text="Mid Game", **STYLES["widget"]).grid(row=1, column=1, sticky=W)
Label(options, text="Multiplier", **STYLES["widget"]).grid(row=2, column=0, sticky=W)
Entry(options, textvariable=self.multiplier).grid(row=2, column=1, sticky=W)
# Footer with Proceed Button
footer = Frame(self.frame, **STYLES["header"])
footer.grid(row=2, sticky=N + E + W)
Button(footer, text="Proceed", command=self.proceed, **STYLES["btn-primary"]).grid(row=0, column=0, sticky=SE,
padx=5, pady=5)
@staticmethod
def create_label_image(parent, image):
im = PhotoImage(file=os.path.join("resources", "ui", image))
label = Label(parent, image=im)
label.image = im
return label
def proceed(self):
self.callback(self.player_country_name,
self.enemy_country_name,
self.terrain_name,
bool(self.sams.get()),
bool(self.midgame.get()),
float(self.multiplier.get()),
db.TIME_PERIODS[self.selected_time_period.get()])

View File

@ -1,675 +0,0 @@
import os
import platform
from threading import Thread
from tkinter.ttk import *
import pygame
from theater.theatergroundobject import CATEGORY_MAP
from ui.styles import STYLES
from ui.window import *
EVENT_COLOR_ATTACK = (100, 100, 255)
EVENT_COLOR_DEFENSE = (255, 100, 100)
RED = (255, 125, 125)
BRIGHT_RED = (200, 64, 64)
BLUE = (164, 164, 255)
DARK_BLUE = (45, 62, 80)
WHITE = (255, 255, 255)
GREEN = (128, 186, 128)
BRIGHT_GREEN = (64, 200, 64)
BLACK = (0, 0, 0)
BACKGROUND = pygame.Color(0, 64, 64)
ANTIALIASING = True
WIDTH = 1000
HEIGHT = 700
MAP_PADDING = 100
class OverviewCanvas:
mainmenu = None # type: ui.mainmenu.MainMenu
budget_label = None # type: Label
started = None
ground_assets_icons = None # type: typing.Dict[str, pygame.Surface]
event_icons = None # type: typing.Dict[typing.Type, pygame.Surface]
selected_event_info = None # type: typing.Tuple[Event, typing.Tuple[int, int]]
frontline_vector_cache = None # type: typing.Dict[str, typing.Tuple[Point, int, int]]
DAWN_ICON = None
DAY_ICON = None
DUSK_ICON = None
NIGHT_ICON = None
def __init__(self, frame: Frame, parent, game: Game):
self.parent = parent
self.game = game
self.load_icons()
# Remove any previously existing pygame instance
pygame.quit()
# Pygame objects
self.map = None
self.map_dusk_dawn = None
self.map_night = None
self.screen = None
self.surface: pygame.Surface = None
self.thread: Thread = None
self.clock = pygame.time.Clock()
self.expanded = True
pygame.font.init()
self.font: pygame.font.SysFont = pygame.font.SysFont("arial", 15)
self.fontsmall: pygame.font.SysFont = pygame.font.SysFont("arial", 10)
self.ground_assets_icons = {}
# Frontline are too heavy on performance to compute in realtime, so keep them in a cache
self.frontline_vector_cache = {}
# Map state
self.redraw_required = True
self.zoom = 1
self.scroll = [0, 0]
self.exited = False
# Display options
self.display_ground_targets = BooleanVar(value=True)
self.display_forces = BooleanVar(value=True)
self.display_bases = BooleanVar(value=True)
self.display_road = BooleanVar(value=True)
self.display_rules = self.compute_display_rules()
parent.window.tk.protocol("<WM_DELETE_WINDOW>", self.on_close)
self.wrapper = Frame(frame, **STYLES["frame-wrapper"])
self.wrapper.grid(column=0, row=0, sticky=NSEW) # Adds grid
self.wrapper.pack(side=LEFT) # packs window to the left
self.embed = Frame(self.wrapper, width=WIDTH, height=HEIGHT, borderwidth=2, **STYLES["frame-wrapper"])
self.embed.grid(column=0, row=1, sticky=NSEW) # Adds grid
self.options = Frame(self.wrapper, borderwidth=2, **STYLES["frame-wrapper"])
self.options.grid(column=0, row=0, sticky=NSEW)
self.options.grid_columnconfigure(1, weight=1)
self.build_map_options_panel()
self.init_sdl_layer()
self.init_sdl_thread()
def build_map_options_panel(self):
col = 0
Button(self.options, text="Configuration", command=self.parent.configuration_menu,
**{**STYLES["btn-primary"],**{ "pady": 4}}).grid(column=col, row=0, sticky=NW)
col += 1
money_icon = Label(self.options, image=self.MONEY_ICON, **STYLES["widget-big"])
money_icon.grid(column=col, row=0, sticky=NE)
col += 1
self.current_budget = StringVar()
self.budget_label = Label(self.options, textvariable=self.current_budget, **STYLES["widget-big"])
self.budget_label.grid(column=col, row=0, sticky=NW)
col += 1
self.daytime_icon = Label(self.options, image=self.DAWN_ICON, **STYLES["widget-big"])
self.daytime_icon.grid(column=col, row=0, sticky=NE)
col += 1
self.current_turn = StringVar()
self.turn_label = Label(self.options, textvariable=self.current_turn, **STYLES["widget-big"])
self.turn_label.grid(column=col, row=0, sticky=NE)
col += 1
Button(self.options, text="Pass turn", command=self.parent.pass_turn,
**{**STYLES["btn-primary"],**{ "pady": 4}}).grid(column=col, row=0, sticky=NE)
col += 1
def map_size_toggle(self):
if self.expanded:
self.embed.configure(width=0)
self.options.configure(width=0)
self.expanded = False
else:
self.embed.configure(width=WIDTH)
self.options.configure(width=WIDTH)
self.expanded = True
def on_close(self):
self.exited = True
if self.thread is not None:
self.thread.join()
def load_icons(self):
if self.DAWN_ICON is None :
self.DAWN_ICON = PhotoImage(file="./resources/ui/daytime/dawn.png")
self.DAY_ICON = PhotoImage(file="./resources/ui/daytime/day.png")
self.DUSK_ICON = PhotoImage(file="./resources/ui/daytime/dusk.png")
self.NIGHT_ICON = PhotoImage(file="./resources/ui/daytime/night.png")
self.MONEY_ICON = PhotoImage(file="./resources/ui/misc/money_icon.png")
self.ORDNANCE_ICON = PhotoImage(file="./resources/ui/misc/ordnance_icon.png")
def init_sdl_layer(self):
# Setup pygame to run in tk frame
os.environ['SDL_WINDOWID'] = str(self.embed.winfo_id())
if platform.system == "Windows":
os.environ['SDL_VIDEODRIVER'] = 'windib'
# Create pygame 'screen'
self.screen = pygame.display.set_mode((WIDTH, HEIGHT), pygame.DOUBLEBUF | pygame.HWSURFACE)
self.screen.fill(pygame.Color(*BLACK))
# Load icons resources
self.ground_assets_icons = {}
self.ground_assets_icons["target"] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", "target.png"))
self.ground_assets_icons["cleared"] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", "cleared.png"))
for category in CATEGORY_MAP.keys():
self.ground_assets_icons[category] = pygame.image.load(os.path.join("resources", "ui", "ground_assets", category + ".png"))
self.event_icons = {}
for category, image in {BaseAttackEvent: "capture",
FrontlinePatrolEvent: "attack",
FrontlineAttackEvent: "attack",
InfantryTransportEvent: "infantry",
InsurgentAttackEvent: "insurgent_attack",
ConvoyStrikeEvent: "convoy",
InterceptEvent: "air_intercept",
NavalInterceptEvent: "naval_intercept",
StrikeEvent: "strike",
UnitsDeliveryEvent: "delivery"}.items():
self.event_icons[category] = pygame.image.load(os.path.join("resources", "ui", "events", image + ".png"))
# Load the map image
self.map = pygame.image.load(os.path.join("resources", self.game.theater.overview_image)).convert()
pygame.draw.rect(self.map, BLACK, (0, 0, self.map.get_width(), self.map.get_height()), 10)
pygame.draw.rect(self.map, WHITE, (0, 0, self.map.get_width(), self.map.get_height()), 5)
# Generate map for night and dusk/dawn
self.map_night = self.map.copy()
self.map_night.fill((100, 100, 110, 128), special_flags=pygame.BLEND_MULT)
self.map_dusk_dawn = self.map.copy()
self.map_dusk_dawn.fill((220, 150, 125, 128), special_flags=pygame.BLEND_MULT)
# Create surfaces for drawing
self.surface = pygame.Surface((self.map.get_width() + MAP_PADDING * 2,
self.map.get_height() + MAP_PADDING * 2))
self.surface.set_alpha(None)
self.overlay = pygame.Surface((WIDTH, HEIGHT), pygame.SRCALPHA)
# Init pygame display
pygame.display.init()
pygame.display.update()
def init_sdl_thread(self):
if OverviewCanvas.started is not None:
OverviewCanvas.started.exited = True
self.thread = Thread(target=self.sdl_thread)
self.thread.start()
OverviewCanvas.started = self
print("Started SDL app")
def sdl_thread(self):
self.redraw_required = True
i = 0
while not self.exited:
self.clock.tick(30)
self.updateOptions()
self.draw()
i += 1
if i == 600:
self.frontline_vector_cache = {}
i = 0
print("Stopped SDL app")
def draw(self):
try:
self.embed.winfo_ismapped()
self.embed.winfo_manager()
except:
self.exited = True
right_down = False
left_down = False
# Detect changes on display rules
r = self.compute_display_rules()
if r != self.display_rules:
self.display_rules = r
self.redraw_required = True
for event in pygame.event.get():
if event.type == pygame.MOUSEMOTION:
self.redraw_required = True
elif event.type == pygame.MOUSEBUTTONDOWN:
"""
Due to rendering not really supporting the zoom this is currently disabled.
@TODO: improve rendering so zoom would actually make sense
# Scroll wheel"""
if event.button == 4:
self.zoom += 0.25
self.redraw_required = True
elif event.button == 5:
self.zoom -= 0.25
self.redraw_required = True
""""""
if event.button == 3:
right_down = True
pygame.mouse.get_rel()
if event.button == 1:
left_down = True
self.redraw_required = True
elif event.type == pygame.KEYDOWN:
if event.key == pygame.K_F2:
self.display_bases.set(not self.display_bases.get())
elif event.key == pygame.K_F3:
self.display_forces.set(not self.display_forces.get())
elif event.key == pygame.K_F4:
self.display_ground_targets.set(not self.display_ground_targets.get())
elif event.key == pygame.K_F5:
self.display_road.set(not self.display_road.get())
# If Right click pressed
if pygame.mouse.get_pressed()[2] == 1 and not right_down:
scr = pygame.mouse.get_rel()
self.scroll[0] += scr[0]
self.scroll[1] += scr[1]
self.redraw_required = True
if self.zoom <= 0.5:
self.zoom = 0.5
elif self.zoom > 3:
self.zoom = 3
if self.redraw_required:
# Fill
self.screen.fill(BACKGROUND)
self.surface.fill(BACKGROUND)
self.overlay.fill(pygame.Color(0, 0, 0, 0))
# Surface
cursor_pos = pygame.mouse.get_pos()
cursor_pos = (
cursor_pos[0] / self.zoom - self.scroll[0], cursor_pos[1] / self.zoom - self.scroll[1])
self.draw_map(self.surface, self.overlay, cursor_pos, [left_down, right_down])
# Scaling
scaled = pygame.transform.scale(self.surface, (
int(self.surface.get_width() * self.zoom), int(self.surface.get_height() * self.zoom)))
self.screen.blit(scaled, (self.scroll[0]*self.zoom, self.scroll[1]*self.zoom))
self.screen.blit(self.overlay, (0, 0))
pygame.display.flip()
self.redraw_required = False
def draw_map(self, surface: pygame.Surface, overlay: pygame.Surface, mouse_pos: (int, int), mouse_down: [bool, bool]):
daytime = self.game.current_turn_daytime
if daytime == "day":
self.surface.blit(self.map, (MAP_PADDING, MAP_PADDING))
elif daytime == "night":
self.surface.blit(self.map_night, (MAP_PADDING, MAP_PADDING))
else:
self.surface.blit(self.map_dusk_dawn, (MAP_PADDING, MAP_PADDING))
# Display zoom level on overlay
zoom_lvl = self.font.render(" x " + str(self.zoom) + " ", ANTIALIASING, WHITE, DARK_BLUE)
self.overlay.blit(zoom_lvl, (self.overlay.get_width()-zoom_lvl.get_width()-5,
self.overlay.get_height()-zoom_lvl.get_height()-5))
# Debug
# pygame.draw.rect(surface, (255, 0, 255), (mouse_pos[0], mouse_pos[1], 5, 5), 2)
for cp in self.game.theater.controlpoints:
coords = self._transform_point(cp.position)
if self.display_road.get():
for connected_cp in cp.connected_points:
connected_coords = self._transform_point(connected_cp.position)
if connected_cp.captured != cp.captured:
color = self._enemy_color()
elif connected_cp.captured and cp.captured:
color = self._player_color()
else:
color = BLACK
pygame.draw.line(surface, color, coords, connected_coords, 2)
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
frontline = self._frontline_vector(cp, connected_cp)
if not frontline:
continue
frontline_pos, heading, distance = frontline
if distance < 10000:
frontline_pos = frontline_pos.point_from_heading(heading + 180, 5000)
distance = 10000
start_coords = self._transform_point(frontline_pos, treshold=10)
end_coords = self._transform_point(frontline_pos.point_from_heading(heading, distance),
treshold=60)
pygame.draw.line(surface, color, start_coords, end_coords, 4)
if self.display_ground_targets.get():
for ground_object in cp.ground_objects:
pygame.draw.line(surface, RED, self._transform_point(cp.position), self._transform_point(ground_object.position), 1)
self.draw_ground_object(ground_object, surface, cp.captured, mouse_pos)
if self.display_bases.get():
mouse_down = self.draw_bases(mouse_pos, mouse_down)
mouse_down = self.draw_events(self.surface, mouse_pos, mouse_down)
if mouse_down[0]:
self.selected_event_info = None
def draw_bases(self, mouse_pos, mouse_down):
for cp in self.game.theater.controlpoints:
coords = self._transform_point(cp.position)
radius = 12 * math.pow(cp.importance, 1)
radius_m = max(radius * cp.base.strength - 2, 0)
if cp.captured:
color = self._player_color()
else:
color = self._enemy_color()
pygame.draw.circle(self.surface, BLACK, (int(coords[0]), int(coords[1])), int(radius))
pygame.draw.circle(self.surface, color, (int(coords[0]), int(coords[1])), int(radius_m))
label = self.font.render(cp.name, ANTIALIASING, (225, 225, 225), BLACK)
labelHover = self.font.render(cp.name, ANTIALIASING, (255, 255, 255), (128, 186, 128))
labelClick = self.font.render(cp.name, ANTIALIASING, (255, 255, 255), (122, 122, 255))
point = coords[0] - label.get_width() / 2 + 1, coords[1] + 1
rect = pygame.Rect(*point, label.get_width(), label.get_height())
if rect.collidepoint(*mouse_pos):
if mouse_down[0]:
self.surface.blit(labelClick, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
self._selected_cp(cp)
mouse_down[0] = False
else:
self.surface.blit(labelHover, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
self.draw_base_info(self.overlay, cp, (0, 0))
if self.selected_event_info:
if self._cp_available_for_selected_event(cp):
pygame.draw.line(self.surface, WHITE, rect.center, self.selected_event_info[1])
else:
self.surface.blit(label, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
if self.display_forces.get():
units_title = " {} / {} / {} ".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa)
label2 = self.fontsmall.render(units_title, ANTIALIASING, color, (30, 30, 30))
self.surface.blit(label2, (coords[0] - label2.get_width() / 2, coords[1] + label.get_height() + 1))
return mouse_down
def draw_base_info(self, surface: pygame.Surface, control_point: ControlPoint, pos):
title = self.font.render(control_point.name, ANTIALIASING, BLACK, GREEN)
hp = self.font.render("Strength : ", ANTIALIASING, (225, 225, 225), BLACK)
armor_txt = "ARMOR > "
for key, value in control_point.base.armor.items():
armor_txt += key.id + " x " + str(value) + " | "
armor = self.font.render(armor_txt, ANTIALIASING, (225, 225, 225), BLACK)
aircraft_txt = "AIRCRAFT > "
for key, value in control_point.base.aircraft.items():
aircraft_txt += key.id + " x " + str(value) + " | "
aircraft = self.font.render(aircraft_txt, ANTIALIASING, (225, 225, 225), BLACK)
aa_txt = "AA/SAM > "
for key, value in control_point.base.aa.items():
aa_txt += key.id + " x " + str(value) + " | "
aa = self.font.render(aa_txt, ANTIALIASING, (225, 225, 225), BLACK)
lineheight = title.get_height()
w = max([max([a.get_width() for a in [title, armor, aircraft, aa]]), 150])
h = 5 * lineheight + 4 * 5
# Draw frame
pygame.draw.rect(surface, GREEN, (pos[0], pos[1], w + 8, h + 8))
pygame.draw.rect(surface, BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
pygame.draw.rect(surface, GREEN, (pos[0] + 2, pos[1], w + 4, lineheight + 4))
# Title
surface.blit(title, (pos[0] + 4, 4 + pos[1]))
surface.blit(hp, (pos[0] + 4, 4 + pos[1] + lineheight + 5))
# Draw gauge
pygame.draw.rect(surface, WHITE,
(pos[0] + hp.get_width() + 3, 4 + pos[1] + lineheight + 5, 54, lineheight))
pygame.draw.rect(surface, BRIGHT_RED,
(pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50, lineheight - 4))
pygame.draw.rect(surface, BRIGHT_GREEN, (
pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50 * control_point.base.strength, lineheight - 4))
# Text
surface.blit(armor, (pos[0] + 4, 4 + pos[1] + lineheight * 2 + 10))
surface.blit(aircraft, (pos[0] + 4, 4 + pos[1] + lineheight * 3 + 15))
surface.blit(aa, (pos[0] + 4, 4 + pos[1] + lineheight * 4 + 20))
def draw_selected_event_info(self):
event = self.selected_event_info[0]
title = self.font.render(str(event), ANTIALIASING, BLACK, GREEN)
hint = self.font.render("Select CP to depart from.", ANTIALIASING, (225, 225, 225), BLACK)
w = hint.get_width()
h = title.get_height() + hint.get_height() + 20
pos = self.overlay.get_width() / 2 - w / 2, self.overlay.get_height() - h
# Draw frame
pygame.draw.rect(self.overlay, GREEN, (pos[0], pos[1], w + 8, h + 8))
pygame.draw.rect(self.overlay, BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
pygame.draw.rect(self.overlay, GREEN, (pos[0] + 2, pos[1], w + 4, title.get_height() + 4))
# Title
self.overlay.blit(title, (pos[0] + 4, 4 + pos[1]))
self.overlay.blit(hint, (pos[0] + 4, 4 + pos[1] + title.get_height() + 5))
def draw_ground_object(self, ground_object: TheaterGroundObject, surface: pygame.Surface, captured: bool, mouse_pos):
if captured:
color = self._player_color()
else:
color = self._enemy_color()
x, y = self._transform_point(ground_object.position)
rect = pygame.Rect(x, y, 16, 16)
if ground_object.is_dead or captured:
surface.blit(self.ground_assets_icons["cleared"], (x, y))
else:
if ground_object.category in self.ground_assets_icons.keys():
icon = self.ground_assets_icons[ground_object.category]
else:
icon = self.ground_assets_icons["target"]
surface.blit(icon, (x, y))
if rect.collidepoint(*mouse_pos):
self.draw_ground_object_info(ground_object, (x, y), color, surface)
def draw_ground_object_info(self, ground_object: TheaterGroundObject, pos, color, surface: pygame.Surface):
lb = self.font.render(str(ground_object), ANTIALIASING, color, BLACK)
surface.blit(lb, (pos[0] + 18, pos[1]))
def draw_events(self, surface: pygame.Surface, mouse_pos, mouse_down):
occupied_rects = []
for cp in self.game.theater.controlpoints:
point = self._transform_point(cp.position)
occupied_rects.append(pygame.Rect(point[0] - 16, point[1] - 16, 32, 48))
def _location_to_rect(location: Point) -> pygame.Rect:
nonlocal occupied_rects
point = self._transform_point(location)
rect = pygame.Rect(point[0] - 16, point[1] - 16, 32, 32)
i = 0
while True:
result = True
for occupied_rect in occupied_rects:
if rect.colliderect(occupied_rect):
i += 1
if i % 2:
rect.y += occupied_rect.height
else:
rect.x += occupied_rect.width
result = False
break
if result:
break
occupied_rects.append(rect)
return rect
def _events_priority_key(event: Event) -> int:
priority_list = [InfantryTransportEvent, StrikeEvent, BaseAttackEvent, UnitsDeliveryEvent]
if type(event) not in priority_list:
return 0
else:
return priority_list.index(type(event)) + 1
events = self.game.events
events.sort(key=_events_priority_key, reverse=True)
label_to_draw = None
for event in self.game.events:
location = event.location
if type(event) in [FrontlineAttackEvent, FrontlinePatrolEvent, ConvoyStrikeEvent]:
location = self._frontline_center(event.from_cp, event.to_cp)
rect = _location_to_rect(location)
pygame.draw.rect(surface, EVENT_COLOR_ATTACK if event.is_player_attacking else EVENT_COLOR_DEFENSE, rect)
self.surface.blit(self.event_icons[event.__class__], rect.topleft)
if rect.collidepoint(*mouse_pos) or self.selected_event_info == (event, rect.center):
if not label_to_draw:
label_to_draw = self.font.render(str(event), ANTIALIASING, WHITE, BLACK), rect.center
if rect.collidepoint(*mouse_pos):
if mouse_down[0]:
self.selected_event_info = event, rect.center
mouse_down[0] = False
if label_to_draw:
surface.blit(*label_to_draw)
if self.selected_event_info:
self.draw_selected_event_info()
return mouse_down
def _selected_cp(self, cp):
if self.selected_event_info:
if self. _cp_available_for_selected_event(cp):
event = self.selected_event_info[0]
event.departure_cp = cp
self.selected_event_info = None
self.parent.start_event(event)
else:
return
else:
self.parent.go_cp(cp)
def _transform_point(self, p: Point, treshold=30) -> (int, int):
point_a = list(self.game.theater.reference_points.keys())[0]
point_a_img = self.game.theater.reference_points[point_a]
point_b = list(self.game.theater.reference_points.keys())[1]
point_b_img = self.game.theater.reference_points[point_b]
Y_dist = point_a_img[0] - point_b_img[0]
lon_dist = point_a[1] - point_b[1]
X_dist = point_a_img[1] - point_b_img[1]
lat_dist = point_b[0] - point_a[0]
Y_scale = float(Y_dist) / float(lon_dist)
X_scale = float(X_dist) / float(lat_dist)
# ---
Y_offset = p.x - point_a[0]
X_offset = p.y - point_a[1]
X = point_b_img[1] + X_offset * X_scale
Y = point_a_img[0] - Y_offset * Y_scale
X += MAP_PADDING
Y += MAP_PADDING
return X > treshold and X or treshold, Y > treshold and Y or treshold
def _frontline_vector(self, from_cp: ControlPoint, to_cp: ControlPoint):
# Cache mechanism to avoid performing frontline vector computation on every frame
key = str(from_cp.id) + "_" + str(to_cp.id)
if key in self.frontline_vector_cache:
return self.frontline_vector_cache[key]
else:
frontline = Conflict.frontline_vector(from_cp, to_cp, self.game.theater)
self.frontline_vector_cache[key] = frontline
return frontline
def _frontline_center(self, from_cp: ControlPoint, to_cp: ControlPoint) -> typing.Optional[Point]:
frontline_vector = self._frontline_vector(from_cp, to_cp)
if frontline_vector:
return frontline_vector[0].point_from_heading(frontline_vector[1], frontline_vector[2]/2)
else:
return None
def _cp_available_for_selected_event(self, cp: ControlPoint) -> bool:
event = self.selected_event_info[0]
return event.is_departure_available_from(cp)
def _player_color(self):
return self.game.player_country == "USA" and BLUE or RED
def _enemy_color(self):
return self.game.player_country == "USA" and RED or BLUE
def update(self):
self.redraw_required = True
self.draw()
def compute_display_rules(self):
return sum([1 if a.get() else 0 for a in [self.display_forces, self.display_road, self.display_bases, self.display_ground_targets]])
def display(self, cp: ControlPoint):
def action(_):
return self.parent.go_cp(cp)
return action
def updateOptions(self):
self.current_turn.set("Turn : {} - {}".format(self.game.turn, self.game.current_day.strftime("%d %b %Y")))
self.current_budget.set("{}M $ (+{}M $)".format(self.game.budget, self.game.budget_reward_amount))
daytime = self.game.current_turn_daytime
if daytime == "dawn":
self.daytime_icon.configure(image=self.DAWN_ICON)
elif daytime == "day":
self.daytime_icon.configure(image=self.DAY_ICON)
elif daytime == "dusk":
self.daytime_icon.configure(image=self.DUSK_ICON)
elif daytime == "night":
self.daytime_icon.configure(image=self.NIGHT_ICON)

View File

@ -1,52 +0,0 @@
# Style for UI
# Padding
PADDING_X = 5
PADDING_Y = 5
# Colors
FG_COLOR = "white"
FG_COLOR_LIGHT = "#dddddd"
BG_COLOR = "#4E5760"
GREEN = "#699245"
YELLOW = "#BF9A46"
RED = "#D0232E"
BG_TITLE_COLOR = "#2D3E50"
BG_SUBTITLE_COLOR = "#3E4F61"
# Fonts
FONT_FAMILY = "Trebuchet MS"
DEFAULT_FONT = (FONT_FAMILY, 8)
FONT_BIG = (FONT_FAMILY, 12)
ITALIC = (FONT_FAMILY, 8, "italic")
BOLD_FONT = (FONT_FAMILY, 10, "bold italic")
TITLE_FONT = (FONT_FAMILY, 16, "bold italic")
# List of styles
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": 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["supstrong"] = {"font": BOLD_FONT, "bg": RED, "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}
STYLES["widget"] = {"bg": BG_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": DEFAULT_FONT}
STYLES["widget-big"] = {"bg": BG_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": FONT_BIG, "relief": "ridge", "borderwidth": 1, "highlightcolor": "white"}
STYLES["italic"] = {"bg": BG_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": ITALIC}
STYLES["radiobutton"] = {"bg": BG_COLOR, "fg": "black", "padx": PADDING_X, "pady": PADDING_Y, "font": DEFAULT_FONT,
"activebackground": BG_COLOR, "highlightbackground": BG_COLOR, "selectcolor": "white"}
STYLES["title"] = {"bg": BG_TITLE_COLOR, "fg": FG_COLOR, "padx": PADDING_X, "pady": PADDING_Y, "font": TITLE_FONT}
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-primary-big"] = {"bg": GREEN, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": FONT_BIG}
STYLES["btn-danger"] = {"bg": RED, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": DEFAULT_FONT}
STYLES["btn-warning"] = {"bg": YELLOW, "fg": FG_COLOR, "padx": PADDING_X, "pady": 2, "font": DEFAULT_FONT}

View File

@ -1,164 +0,0 @@
from tkinter import *
from tkinter import Menu as TkMenu
from tkinter import messagebox
from .styles import BG_COLOR,BG_TITLE_COLOR
from game.game import *
from theater import persiangulf, nevada, caucasus, start_generator
from userdata import logging as logging_module
import sys
import webbrowser
class Window:
image = None
left_pane = None # type: Frame
right_pane = None # type: Frame
def __init__(self):
self.tk = Tk()
self.tk.title("DCS Liberation")
self.tk.iconbitmap("resources/icon.ico")
self.tk.resizable(False, False)
self.tk.grid_columnconfigure(0, weight=1)
self.tk.grid_rowconfigure(0, weight=1)
self.frame = None
self.right_pane = None
self.left_pane = None
self.build()
menubar = TkMenu(self.tk)
filemenu = TkMenu(menubar, tearoff=0)
filemenu.add_command(label="New Game", command=lambda: self.new_game_confirm())
filemenu.add_separator()
filemenu.add_command(label="Exit", command=lambda: self.exit())
menubar.add_cascade(label="File", menu=filemenu)
helpmenu = TkMenu(menubar, tearoff=0)
helpmenu.add_command(label="Online Manual", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Manual"))
helpmenu.add_command(label="Troubleshooting Guide", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Troubleshooting"))
helpmenu.add_command(label="Modding Guide", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/wiki/Modding-tutorial"))
helpmenu.add_separator()
helpmenu.add_command(label="Contribute", command=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation"))
helpmenu.add_command(label="Forum Thread", command=lambda: webbrowser.open_new_tab("https://forums.eagle.ru/showthread.php?t=214834"))
helpmenu.add_command(label="Report an issue", command=self.report_issue)
menubar.add_cascade(label="Help", menu=helpmenu)
self.tk.config(menu=menubar)
self.tk.focus()
def build(self):
self.frame = Frame(self.tk, bg=BG_COLOR)
self.frame.grid(column=0, row=0, sticky=NSEW)
self.frame.grid_columnconfigure(0)
self.frame.grid_columnconfigure(1)
self.frame.grid_columnconfigure(0, weight=0)
self.frame.grid_columnconfigure(1, weight=1)
self.frame.grid_rowconfigure(0, weight=1)
self.left_pane = Frame(self.frame, bg=BG_TITLE_COLOR)
self.left_pane.grid(row=0, column=0, sticky=NSEW)
self.right_pane = Frame(self.frame, bg=BG_COLOR)
self.right_pane.grid(row=0, column=1, sticky=NSEW)
def clear_right_pane(self):
for i in range(100):
self.right_pane.grid_columnconfigure(1, weight=0)
self.right_pane.grid_rowconfigure(1, weight=0)
for x in self.right_pane.winfo_children():
x.grid_remove()
def clear(self):
def clear_recursive(x, n=50):
if n < 0:
return
for y in x.winfo_children():
clear_recursive(y, n-1)
x.grid_forget()
clear_recursive(self.frame, 50)
self.left_pane.grid_remove()
self.right_pane.grid_remove()
self.build()
def start_new_game(self, player_name: str, enemy_name: str, terrain: str, sams: bool, midgame: bool, multiplier: float, period:datetime):
player_country = db.FACTIONS[player_name]["country"]
enemy_country = db.FACTIONS[enemy_name]["country"]
if terrain == "persiangulf":
conflicttheater = persiangulf.PersianGulfTheater()
elif terrain == "nevada":
conflicttheater = nevada.NevadaTheater()
else:
conflicttheater = caucasus.CaucasusTheater()
if midgame:
for i in range(0, int(len(conflicttheater.controlpoints) / 2)):
conflicttheater.controlpoints[i].captured = True
start_generator.generate_inital_units(conflicttheater, enemy_name, sams, multiplier)
game = Game(player_name=player_name,
enemy_name=enemy_name,
theater=conflicttheater,
start_date=period)
start_generator.generate_groundobjects(conflicttheater, game)
game.budget = int(game.budget * multiplier)
game.settings.multiplier = multiplier
game.settings.sams = sams
game.settings.version = logging_module.version_string()
if midgame:
game.budget = game.budget * 4 * len(list(conflicttheater.conflicts()))
self.proceed_to_main_menu(game)
def proceed_to_main_menu(self, game: Game):
from ui.mainmenu import MainMenu
self.clear()
m = MainMenu(self, None, game)
m.display()
def proceed_to_new_game_menu(self):
from ui.newgamemenu import NewGameMenu
self.clear()
new_game_menu = NewGameMenu(self, self.start_new_game)
new_game_menu.display()
def new_game_confirm(self):
result = messagebox.askquestion("Start a new game", "Are you sure you want to start a new game ? Your current campaign will be overriden and there is no going back !", icon='warning')
if result == 'yes':
self.proceed_to_new_game_menu()
else:
pass
def report_issue(self):
raise logging_module.ShowLogsException()
def exit(self):
self.tk.destroy()
sys.exit(0)
def run(self):
self.tk.mainloop()
class Menu:
parent = None # type: Menu
def __init__(self, window: Window, parent, game: Game):
self.window = window
self.parent = parent
self.game = game
def dismiss(self):
self.parent.display()
def display(self):
pass