Merge pull request #43 from Khopa/sdl_map

Scrollable & Zoomable Map view
This commit is contained in:
Vasyl Horbachenko 2018-11-04 00:58:14 +02:00 committed by GitHub
commit 97be483624
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
22 changed files with 524 additions and 144 deletions

View File

@ -1,21 +1,16 @@
#!/usr/bin/env python3
import logging
import os
import re
import sys
import dcs
import logging
import theater.caucasus
import theater.persiangulf
import theater.nevada
import ui.window
import ui.corruptedsavemenu
import ui.mainmenu
import ui.newgamemenu
import ui.corruptedsavemenu
import ui.window
from game.game import Game
from theater import start_generator
from userdata import persistency, logging as logging_module
assert len(sys.argv) >= 3, "__init__.py should be started with two mandatory arguments: %UserProfile% location and application version"
@ -53,39 +48,12 @@ def is_version_compatible(save_version):
w = ui.window.Window()
try:
game = persistency.restore_game()
if not game or not is_version_compatible(game.settings.version):
new_game_menu = None # type: NewGameMenu
def start_new_game(player_name: str, enemy_name: str, terrain: str, sams: bool, midgame: bool, multiplier: float):
if terrain == "persiangulf":
conflicttheater = theater.persiangulf.PersianGulfTheater()
elif terrain == "nevada":
conflicttheater = theater.nevada.NevadaTheater()
else:
conflicttheater = theater.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)
start_generator.generate_groundobjects(conflicttheater)
game = Game(player_name=player_name,
enemy_name=enemy_name,
theater=conflicttheater)
game.budget = int(game.budget * multiplier)
game.settings.multiplier = multiplier
game.settings.sams = sams
game.settings.version = VERSION_STRING
if midgame:
game.budget = game.budget * 4 * len(list(conflicttheater.conflicts()))
proceed_to_main_menu(game)
new_game_menu = ui.newgamemenu.NewGameMenu(w, start_new_game)
new_game_menu = ui.newgamemenu.NewGameMenu(w, w.start_new_game)
new_game_menu.display()
else:
game.settings.version = VERSION_STRING

Binary file not shown.

Before

Width:  |  Height:  |  Size: 157 KiB

After

Width:  |  Height:  |  Size: 247 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 KiB

After

Width:  |  Height:  |  Size: 467 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 62 KiB

After

Width:  |  Height:  |  Size: 260 KiB

BIN
resources/ui/aa.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 229 B

BIN
resources/ui/ammo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 197 B

BIN
resources/ui/cleared.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

BIN
resources/ui/comms.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 196 B

BIN
resources/ui/factory.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 220 B

BIN
resources/ui/farp.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

BIN
resources/ui/fob.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 213 B

BIN
resources/ui/fuel.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 224 B

BIN
resources/ui/oil.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 227 B

BIN
resources/ui/power.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 235 B

BIN
resources/ui/target.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 B

BIN
resources/ui/warehouse.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 B

@ -1 +1 @@
Subproject commit 54eab60f228847f2d92e344e6885eca855354f41
Subproject commit fae126689132d643d317252adfb03184042a0ded

View File

@ -11,8 +11,8 @@ from .base import *
class CaucasusTheater(ConflictTheater):
terrain = caucasus.Caucasus()
overview_image = "caumap.gif"
reference_points = {(-317948.32727306, 635639.37385346): (278.5, 319),
(-355692.3067714, 617269.96285781): (263, 352), }
reference_points = {(-317948.32727306, 635639.37385346): (278.5*2, 319*2),
(-355692.3067714, 617269.96285781): (263*2, 352*2), }
landmap = load_landmap("resources\\caulandmap.p")
daytime_map = {
"dawn": (6, 9),

View File

@ -9,8 +9,8 @@ from .base import *
class NevadaTheater(ConflictTheater):
terrain = dcs.terrain.Nevada()
overview_image = "nevada.gif"
reference_points = {(nevada.Mina_Airport_3Q0.position.x, nevada.Mina_Airport_3Q0.position.y): (45, -360),
(nevada.Laughlin_Airport.position.x, nevada.Laughlin_Airport.position.y): (440, 80), }
reference_points = {(nevada.Mina_Airport_3Q0.position.x, nevada.Mina_Airport_3Q0.position.y): (45*2, -360*2),
(nevada.Laughlin_Airport.position.x, nevada.Laughlin_Airport.position.y): (440*2, 80*2), }
landmap = load_landmap("resources\\nev_landmap.p")
daytime_map = {
"dawn": (4, 6),

View File

@ -9,8 +9,8 @@ from .landmap import load_landmap
class PersianGulfTheater(ConflictTheater):
terrain = dcs.terrain.PersianGulf()
overview_image = "persiangulf.gif"
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321, 145),
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347, 82), }
reference_points = {(persiangulf.Sir_Abu_Nuayr.position.x, persiangulf.Sir_Abu_Nuayr.position.y): (321*4, 145*4),
(persiangulf.Sirri_Island.position.x, persiangulf.Sirri_Island.position.y): (347*4, 82*4), }
landmap = load_landmap("resources\\gulflandmap.p")
daytime_map = {
"dawn": (6, 8),

View File

@ -1,25 +1,427 @@
import os
from tkinter import *
import platform
from threading import Thread
from tkinter.ttk import *
from ui.window import *
import pygame
from game.game import *
from gen.conflictgen import Conflict
from theater.conflicttheater import *
from theater.theatergroundobject import CATEGORY_MAP
from ui.styles import STYLES
from ui.window import *
class OverviewCanvas:
mainmenu = None # type: ui.mainmenu.MainMenu
BRIGHT_RED = (200, 64, 64)
BRIGHT_GREEN = (64, 200, 64)
RED = (255, 125, 125)
BLUE = (164, 164, 255)
DARK_BLUE = (45, 62, 80)
WHITE = (255, 255, 255)
GREEN = (128, 186, 128)
BLACK = (0, 0, 0)
BACKGROUND = pygame.Color(0, 64, 64)
ANTIALIASING = True
WIDTH = 1066
HEIGHT = 600
started = None
def __init__(self, frame: Frame, parent, game: Game):
self.parent = parent
self.game = game
self.image = PhotoImage(file=os.path.join("resources", game.theater.overview_image))
self.canvas = Canvas(frame, width=self.image.width(), height=self.image.height())
self.canvas.grid(column=0, row=0, sticky=NSEW)
# Remove any previously existing pygame instance
pygame.quit()
# Pygame objects
self.map = 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.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=self.WIDTH, height=self.HEIGHT, borderwidth=2, **STYLES["frame-wrapper"])
self.embed.grid(column=0, row=0, sticky=NSEW) # Adds grid
self.options = Frame(self.wrapper, borderwidth=2, **STYLES["frame-wrapper"])
self.options.grid(column=0, row=1, sticky=NSEW)
self.build_map_options_panel()
self.init_sdl_layer()
self.init_sdl_thread()
def build_map_options_panel(self):
def force_redraw():
if self.screen:
self.redraw_required = True
self.draw()
col = 0
Label(self.options, text="Bases", **STYLES["widget"]).grid(row=0, column=col, sticky=W)
Checkbutton(self.options, variable=self.display_bases, **STYLES["radiobutton"]).grid(row=0, column=col + 1,
sticky=E)
Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS)
col += 3
Label(self.options, text="Roads", **STYLES["widget"]).grid(row=0, column=col, sticky=W)
Checkbutton(self.options, variable=self.display_road, **STYLES["radiobutton"]).grid(row=0, column=col + 1,
sticky=E)
Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS)
col += 3
Label(self.options, text="Strike targets", **STYLES["widget"]).grid(row=0, column=col, sticky=W)
Checkbutton(self.options, variable=self.display_ground_targets, **STYLES["radiobutton"]).grid(row=0,
column=col + 1,
sticky=E)
Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS)
col += 3
Label(self.options, text="Forces", **STYLES["widget"]).grid(row=0, column=col, sticky=W)
Checkbutton(self.options, variable=self.display_forces, **STYLES["radiobutton"]).grid(row=0, column=col + 1,
sticky=E)
Separator(self.options, orient=VERTICAL).grid(row=0, column=col + 2, sticky=NS)
col += 4
Button(self.options, text="Toggle size", command=lambda: self.map_size_toggle(), **STYLES["btn-primary"]).grid(row=0, column=col, sticky=E, padx=(10,10))
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=self.WIDTH)
self.options.configure(width=self.WIDTH)
self.expanded = True
def on_close(self):
self.exited = True
if self.thread is not None:
self.thread.join()
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((self.WIDTH, self.HEIGHT), pygame.DOUBLEBUF | pygame.HWSURFACE)
self.screen.fill(pygame.Color(*self.BLACK))
# Load icons resources
self.icons = {}
self.icons["target"] = pygame.image.load(os.path.join("resources", "ui", "target.png"))
self.icons["cleared"] = pygame.image.load(os.path.join("resources", "ui", "cleared.png"))
for category in CATEGORY_MAP.keys():
try:
self.icons[category] = pygame.image.load(os.path.join("resources", "ui", category + ".png"))
except:
print("Couldn't load icon for : " + category)
# Load the map image
self.map = pygame.image.load(os.path.join("resources", self.game.theater.overview_image)).convert()
pygame.draw.rect(self.map, self.BLACK, (0, 0, self.map.get_width(), self.map.get_height()), 10)
pygame.draw.rect(self.map, self.WHITE, (0, 0, self.map.get_width(), self.map.get_height()), 5)
# Create surfaces for drawing
self.surface = pygame.Surface((self.map.get_width(), self.map.get_height()))
self.surface.set_alpha(None)
self.overlay = pygame.Surface((self.WIDTH, self.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(60)
self.draw()
i += 1
if i == 300:
self.frontline_vector_cache = {}
i = 0
print("Stopped SDL app")
def draw(self):
try:
#self.parent.window.tk.winfo_ismapped()
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:
# 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
# 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 > 10:
self.zoom = 10
if self.redraw_required:
# Fill
self.screen.fill(self.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)):
self.surface.blit(self.map, (0, 0))
# Display zoom level on overlay
zoom_lvl = self.font.render(" x " + str(self.zoom) + " ", self.ANTIALIASING, self.WHITE, self.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_ground_targets.get():
if cp.captured:
color = self._player_color()
else:
color = self._enemy_color()
for ground_object in cp.ground_objects:
x, y = self.transform_point(ground_object.position)
pygame.draw.line(surface, color, coords, (x + 8, y + 8), 1)
self.draw_ground_object(ground_object, surface, color, mouse_pos)
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 = self.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):
# Cache mechanism to avoid performing frontline vector computation on every frame
key = str(cp.id) + "_" + str(connected_cp.id)
if key in self.frontline_vector_cache:
frontline = self.frontline_vector_cache[key]
else:
frontline = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
self.frontline_vector_cache[key] = frontline
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_bases.get():
for cp in self.game.theater.controlpoints:
coords = self.transform_point(cp.position)
radius = 12 * math.pow(cp.importance, 1)
radius_m = radius * cp.base.strength - 2
if cp.captured:
color = self._player_color()
else:
color = self._enemy_color()
pygame.draw.circle(surface, self.BLACK, (int(coords[0]), int(coords[1])), int(radius))
pygame.draw.circle(surface, color, (int(coords[0]), int(coords[1])), int(radius_m))
label = self.font.render(cp.name, self.ANTIALIASING, (225, 225, 225), self.BLACK)
labelHover = self.font.render(cp.name, self.ANTIALIASING, (255, 255, 255), (128, 186, 128))
labelClick = self.font.render(cp.name, self.ANTIALIASING, (255, 255, 255), (122, 122, 255))
rect = pygame.Rect(coords[0] - label.get_width() / 2 + 1, coords[1] + 1, label.get_width(),
label.get_height())
if rect.collidepoint(mouse_pos):
if (mouse_down[0]):
surface.blit(labelClick, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
self.parent.go_cp(cp)
else:
surface.blit(labelHover, (coords[0] - label.get_width() / 2 + 1, coords[1] + 1))
self.draw_base_info(overlay, cp, (0, 0))
else:
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, self.ANTIALIASING, color, (30, 30, 30))
surface.blit(label2, (coords[0] - label2.get_width() / 2, coords[1] + label.get_height() + 1))
def draw_base_info(self, surface: pygame.Surface, controlPoint: ControlPoint, pos):
title = self.font.render(controlPoint.name, self.ANTIALIASING, self.BLACK, self.GREEN)
hp = self.font.render("Strength : ", self.ANTIALIASING, (225, 225, 225), self.BLACK)
armor_txt = "ARMOR > "
for key, value in controlPoint.base.armor.items():
armor_txt += key.id + " x " + str(value) + " | "
armor = self.font.render(armor_txt, self.ANTIALIASING, (225, 225, 225), self.BLACK)
aircraft_txt = "AIRCRAFT > "
for key, value in controlPoint.base.aircraft.items():
aircraft_txt += key.id + " x " + str(value) + " | "
aircraft = self.font.render(aircraft_txt, self.ANTIALIASING, (225, 225, 225), self.BLACK)
aa_txt = "AA/SAM > "
for key, value in controlPoint.base.aa.items():
aa_txt += key.id + " x " + str(value) + " | "
aa = self.font.render(aa_txt, self.ANTIALIASING, (225, 225, 225), self.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, self.GREEN, (pos[0], pos[1], w + 8, h + 8))
pygame.draw.rect(surface, self.BLACK, (pos[0] + 2, pos[1] + 2, w + 4, h + 4))
pygame.draw.rect(surface, self.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, self.WHITE,
(pos[0] + hp.get_width() + 3, 4 + pos[1] + lineheight + 5, 54, lineheight))
pygame.draw.rect(surface, self.BRIGHT_RED,
(pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50, lineheight - 4))
pygame.draw.rect(surface, self.BRIGHT_GREEN, (
pos[0] + hp.get_width() + 5, 4 + pos[1] + lineheight + 5 + 2, 50 * controlPoint.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_ground_object(self, ground_object: TheaterGroundObject, surface: pygame.Surface, color, mouse_pos):
x, y = self.transform_point(ground_object.position)
rect = pygame.Rect(x, y, 16, 16)
if ground_object.is_dead:
surface.blit(self.icons["cleared"], (x, y))
else:
if ground_object.category in self.icons.keys():
icon = self.icons[ground_object.category]
else:
icon = self.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), self.ANTIALIASING, color, self.BLACK)
surface.blit(lb, (pos[0] + 18, pos[1]))
def transform_point(self, p: Point, treshold=30) -> (int, int):
point_a = list(self.game.theater.reference_points.keys())[0]
@ -46,99 +448,20 @@ class OverviewCanvas:
return X > treshold and X or treshold, Y > treshold and Y or treshold
def create_cp_title(self, coords, cp: ControlPoint):
title = cp.name
font = ("Helvetica", 10)
id = self.canvas.create_text(coords[0], coords[1], text=title, font=font)
self.canvas.tag_bind(id, "<Button-1>", self.display(cp))
id = self.canvas.create_text(coords[0]+1, coords[1]+1, text=title, fill='white', font=font)
self.canvas.tag_bind(id, "<Button-1>", self.display(cp))
def _player_color(self):
return self.game.player == "USA" and "blue" or "red"
return self.game.player == "USA" and self.BLUE or self.RED
def _enemy_color(self):
return self.game.player == "USA" and "red" or "blue"
return self.game.player == "USA" and self.RED or self.BLUE
def update(self):
self.canvas.delete(ALL)
self.canvas.create_image((self.image.width()/2, self.image.height()/2), image=self.image)
self.draw()
for cp in self.game.theater.controlpoints:
for ground_object in cp.ground_objects:
x, y = self.transform_point(ground_object.position)
self.canvas.create_text(x,
y,
text=".",
fill="black" if ground_object.is_dead else self._enemy_color(),
font=("Helvetica", 18))
coords = self.transform_point(cp.position)
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"
self.canvas.create_line((coords[0], coords[1], connected_coords[0], connected_coords[1]), width=2, fill=color)
if cp.captured and not connected_cp.captured and Conflict.has_frontline_between(cp, connected_cp):
frontline = Conflict.frontline_vector(cp, connected_cp, self.game.theater)
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)
self.canvas.create_line((*start_coords, *end_coords), width=2, fill=color)
for cp in self.game.theater.controlpoints:
coords = self.transform_point(cp.position)
arc_size = 16 * math.pow(cp.importance, 1)
extent = max(cp.base.strength * 180, 10)
start = (180 - extent) / 2
if cp.captured:
color = self._player_color()
else:
color = self._enemy_color()
cp_id = self.canvas.create_arc((coords[0] - arc_size/2, coords[1] - arc_size/2),
(coords[0] + arc_size/2, coords[1] + arc_size/2),
fill=color,
style=PIESLICE,
start=start,
extent=extent)
"""
#For debugging purposes
for r in cp.radials:
p = self.transform_point(cp.position.point_from_heading(r, 20000))
self.canvas.create_text(p[0], p[1], text="{}".format(r))
continue
"""
self.canvas.tag_bind(cp_id, "<Button-1>", self.display(cp))
self.create_cp_title((coords[0] + arc_size/4, coords[1] + arc_size/4), cp)
units_title = "{}/{}/{}".format(cp.base.total_planes, cp.base.total_armor, cp.base.total_aa)
self.canvas.create_text(coords[0]+1, coords[1] - arc_size / 1.5 +1, text=units_title, font=("Helvetica", 8), fill=color)
self.canvas.create_text(coords[0], coords[1] - arc_size / 1.5, text=units_title, font=("Helvetica", 8), fill="white")
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

View File

@ -1,8 +1,14 @@
from tkinter import *
from tkinter import Menu as TkMenu
from tkinter import messagebox
from game.game import *
from theater import persiangulf, nevada, caucasus, start_generator
from .styles import BG_COLOR,BG_TITLE_COLOR
import sys
import webbrowser
class Window:
image = None
left_pane = None # type: Frame
right_pane = None # type: Frame
@ -15,6 +21,33 @@ class Window:
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=lambda: webbrowser.open_new_tab("https://github.com/shdwp/dcs_liberation/issues"))
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)
@ -29,8 +62,6 @@ class Window:
self.right_pane = Frame(self.frame, bg=BG_COLOR)
self.right_pane.grid(row=0, column=1, sticky=NSEW)
self.tk.focus()
def clear_right_pane(self):
for i in range(100):
self.right_pane.grid_columnconfigure(1, weight=0)
@ -40,10 +71,68 @@ class Window:
x.grid_remove()
def clear(self):
for x in self.left_pane.winfo_children():
x.grid_remove()
for x in self.right_pane.winfo_children():
x.grid_remove()
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):
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)
start_generator.generate_groundobjects(conflicttheater)
game = Game(player_name=player_name,
enemy_name=enemy_name,
theater=conflicttheater)
game.budget = int(game.budget * multiplier)
game.settings.multiplier = multiplier
game.settings.sams = sams
game.settings.version = "1.4.0"
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 exit(self):
self.tk.destroy()
sys.exit(0)
def run(self):
self.tk.mainloop()