mirror of
https://github.com/dcs-retribution/dcs-retribution.git
synced 2025-11-10 15:41:24 +00:00
Generate CAP on whole map, for 2 hours. Added China.
This commit is contained in:
parent
2167953b87
commit
7a14376765
83
game/db.py
83
game/db.py
@ -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:
|
||||
|
||||
@ -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))
|
||||
|
||||
|
||||
@ -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
|
||||
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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"]
|
||||
|
||||
@ -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)
|
||||
|
||||
|
||||
116
ui/basemenu.py
116
ui/basemenu.py
@ -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
|
||||
@ -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
|
||||
@ -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)
|
||||
219
ui/eventmenu.py
219
ui/eventmenu.py
@ -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()
|
||||
|
||||
@ -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
|
||||
@ -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()
|
||||
|
||||
|
||||
|
||||
|
||||
@ -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()])
|
||||
@ -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)
|
||||
52
ui/styles.py
52
ui/styles.py
@ -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}
|
||||
164
ui/window.py
164
ui/window.py
@ -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
|
||||
Loading…
x
Reference in New Issue
Block a user