diff --git a/-Client_Launch.sh b/-Client_Launch.sh
deleted file mode 100644
index 6498fe8..0000000
--- a/-Client_Launch.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-python -m pip install pygame
-python -m pip install pygame_gui
-python client.py
\ No newline at end of file
diff --git a/-Server_Launch.sh b/-Server_Launch.sh
deleted file mode 100644
index 8252d24..0000000
--- a/-Server_Launch.sh
+++ /dev/null
@@ -1,5 +0,0 @@
-#!/bin/bash
-
-python -m pip install pygame
-python -m pip install pygame_gui
-python server.py
diff --git a/.gitignore b/.gitignore
index 05acda6..b0d2be2 100644
--- a/.gitignore
+++ b/.gitignore
@@ -183,4 +183,5 @@ fabric.properties
.idea/caches/build_file_checksums.ser
# idea folder, uncomment if you don't need it
-# .idea
\ No newline at end of file
+# .idea
+/output/
diff --git a/XML/aircrafts.xml b/Python/XML/aircraft.xml
similarity index 100%
rename from XML/aircrafts.xml
rename to Python/XML/aircraft.xml
diff --git a/XML/mapAPS.xml b/Python/XML/carteSecteur.xml
similarity index 87%
rename from XML/mapAPS.xml
rename to Python/XML/carteSecteur.xml
index b8cbf92..c2944ed 100644
--- a/XML/mapAPS.xml
+++ b/Python/XML/carteSecteur.xml
@@ -6,6 +6,43 @@
245 # plancher de notre secteur
365 # plafond
+ # liste des secteurs adjacents
+
+ 125.830
+
+
+ 127.840
+
+
+ 128.050
+
+
+ 131.175
+
+
+ 132.740
+
+
+ 119.275
+
+
+ 124.2
+
+
+ 124.080
+
+
+ 134.610
+
+
+ 130.275
+ True
+
+
+ 127.675
+ True
+
+
@@ -162,8 +199,8 @@
-
-
+
+
1109
915
@@ -220,13 +257,14 @@
1107
1266
-
-
+
+
DEPART
SO
+ LSGG
LSGG
120
@@ -298,7 +336,7 @@
TRANSIT
- SE
+ E-SE
N
BURGO
@@ -393,6 +431,7 @@
DEPART
SO
+ LIML
LIML
20
@@ -455,7 +494,7 @@
JUVEN
- 260
+ 260
M2
@@ -489,7 +528,7 @@
SEVET
- 250
+ 250
G2
@@ -517,7 +556,7 @@
SEVET
- 250
+ 250
G2
@@ -653,6 +692,7 @@
DEPART
+ LFVB
S
BST
@@ -683,6 +723,7 @@
DEPART
+ LFVB
SE
BST
@@ -713,6 +754,7 @@
DEPART
+ LFVB
SO
BST
@@ -745,34 +787,83 @@
- LOWI
+ EDDT
+ EDDF
+ EKCH
+ EHAM
+ ESSB
+ ENGM
+ EPWA
+ EGLL
EGBB
+ EGLL
EIDW
+ EGCC
+ EGGD
+ EGKK
+ LFPG
+ LFPO
+ LFQQ
EGBB
+ LFPG
+ LFPO
+ LFQQ
+ LFRS
+ EIDW
+ EGCC
+ EGGD
+ EGKK
+ BIRK
DAAG
+ DRRN
+ DTTA
+ GCTS
LPPT
+ LPPR
+ LFBD
+ LFBO
+ LFBP
+ LFBT
+ LFBZ
+ LEIB
+ LEMD
+ LEMG
+ LEPA
+ LFKC
+ LFKJ
+ DAAG
+ DRRN
+ DTTA
+
+
+
LGAT
LGAV
LGIR
-
+ LIMF
+ LIPZ
+
- LIML
+ LOWW
+ LOWI
+ LOWS
+ LHBP
diff --git a/XML/simu.xml b/Python/XML/simu.xml
similarity index 62%
rename from XML/simu.xml
rename to Python/XML/simu.xml
index 3858b0a..fb9033f 100644
--- a/XML/simu.xml
+++ b/Python/XML/simu.xml
@@ -2,14 +2,14 @@
1002
-
+
FCACA
- DA42
- GAI-JUVEN
+ DH8D
+ ABISA-BERNI
30000
False
- 720
- 1339
+ 1400
+ 1300
300
diff --git a/Python/__init__.py b/Python/__init__.py
new file mode 100644
index 0000000..b111089
--- /dev/null
+++ b/Python/__init__.py
@@ -0,0 +1,2 @@
+
+__package__ = "Python"
diff --git a/client.py b/Python/client.py
similarity index 72%
rename from client.py
rename to Python/client.py
index df9604c..2c61f36 100644
--- a/client.py
+++ b/Python/client.py
@@ -1,12 +1,18 @@
-import pygame
-import horloge
-from network import Network
-import server_browser
-from player import *
-import pygame_gui
-import interface
-from paquets_avion import *
+
+# Native import
+import time
import math
+from pathlib import Path
+import os
+
+# fichiers
+import Python.horloge as horloge
+from Python.network import Network
+import Python.server_browser as server_browser
+from Python.player import *
+import Python.interface as interface
+from Python.paquets_avion import *
+import Python.outils_radar as outils_radar
# recherche de tous les serveurs sur le réseau
address = server_browser.serverBrowser()
@@ -18,7 +24,8 @@
height = 1000
win = pygame.display.set_mode((width, height))
-manager = pygame_gui.UIManager((width, height), 'theme.json')
+path = Path(os.getcwd())
+manager = pygame_gui.UIManager((width, height), path / 'ressources' / 'theme.json')
pygame.display.set_caption("Client")
temps = pygame.time.get_ticks()
@@ -34,10 +41,13 @@ def main(server_ip: str):
distance = 10
# menus
+ conflitBool = False
+ conflitGen = None
menuAvion = None
menuATC = None
menuValeurs = None
flightDataWindow = None
+ menuRadar = interface.menuRadar()
# on se connecte au serveur
n = Network(server_ip)
@@ -48,6 +58,7 @@ def main(server_ip: str):
while packet is None and i < 200:
n = Network(server_ip)
packet = n.getP()
+ time.sleep(0.3)
i +=1
perfos = packet.perfos
@@ -64,6 +75,14 @@ def main(server_ip: str):
alidadPos = (0, 0)
curseur_alidad = False
+ # cercles
+ curseur_cercles = False
+ cerclePos = None
+
+ # alisep
+ curseur_aliSep = False
+ sepDict = {'A': outils_radar.aliSep('A'), 'B': outils_radar.aliSep('B'), 'C': outils_radar.aliSep('C')}
+
# scroll and zoom
zoomDef = 0.5
scrollDef = [width / 4, height/4]
@@ -76,7 +95,6 @@ def main(server_ip: str):
# vecteurs et type
vecteurs = False
- affichage_type_avion = False
vecteurSetting = 6
# fenêtre nouvel avion
@@ -118,6 +136,9 @@ def main(server_ip: str):
game = packet.game
dictAvions = packet.dictAvions
+ for sep in sepDict.values():
+ sep.calculation(carte)
+
for event in pygame.event.get():
if event.type == pygame.QUIT:
@@ -149,8 +170,41 @@ def main(server_ip: str):
avion.dragOffset = calculateEtiquetteOffset(avion.etiquette.container)
# on vérifie que l'alidade n'est pas actif
- elif event.type == pygame_gui.UI_BUTTON_PRESSED and not curseur_alidad:
+ elif event.type == pygame_gui.UI_BUTTON_PRESSED and curseur_alidad:
+ empecherDragging = False
+
+ elif event.type == pygame_gui.UI_BUTTON_PRESSED:
empecherDragging = False
+
+ if menuRadar.checkActive():
+ action = menuRadar.checkEvent(event)
+
+ if action is not None:
+
+ if type(action) in [list, tuple]:
+
+ if action[0] == 'VecteursToggle':
+ if vecteurSetting == action[1]:
+ vecteurs = not vecteurs
+ else:
+ vecteurs = True
+ vecteurSetting = action[1]
+
+ elif action[0] == 'Vecteurs':
+ vecteurSetting = action[1]
+
+ elif action[0] == 'Sep':
+ sepDict[action[1]].kill()
+ curseur_aliSep = action[1]
+ pygame.mouse.set_cursor(pygame.cursors.diamond)
+ elif action == 'Alidade':
+ curseur_alidad = True
+ pygame.mouse.set_cursor(pygame.cursors.broken_x)
+
+ elif action == 'Cercles':
+ cerclePos = None
+ curseur_cercles = True
+ pygame.mouse.set_cursor(pygame.cursors.broken_x)
# on regarde si notre menu pour le pilote est actif
if menuAvion is not None:
@@ -175,6 +229,19 @@ def main(server_ip: str):
if type(action) in [list, tuple]: # si c'est un tuple alors cela correspond à une requête
localRequests.append(action)
+ if conflitGen is not None:
+ action = conflitGen.checkEvent(event)
+
+ if action:
+ if type(action) is tuple:
+ localRequests.append((len(dictAvions), "DelayedAdd", action))
+ else:
+ localRequests.append((len(dictAvions), "Add", action))
+ for avion in dictAvionsAff.values():
+ avion.conflitSelected = False
+ conflitBool = False
+ conflitGen = None
+
if menuValeurs is not None:
# si on valide les modifs, alors la fonction checkEvent retourne les modifs
@@ -185,10 +252,21 @@ def main(server_ip: str):
elif action in ['HDG', 'DCT']:
menuValeurs = interface.menuValeurs(menuValeurs.avion, pygame.mouse.get_pos(), action)
+ if curseur_aliSep:
+ for sep in sepDict:
+ if sep == curseur_aliSep:
+ for avion in dictAvionsAff.values():
+ if avion.checkClicked(event):
+ if sepDict[sep].linkAvion(avion, carte):
+ curseur_aliSep = False
+ pygame.mouse.set_cursor(pygame.cursors.arrow)
+
else:
for avion in dictAvionsAff.values(): # pour chaque avion
- action = avion.checkEvent(event, pilote) # on vérifie si l'event est associé avec ses boutons
+ action = avion.checkEvent(event, pilote, conflitBool)
+
+ # on vérifie si l'event est associé avec ses boutons
if type(action) in [list, tuple]: # si c'est un tuple alors cela correspond à une requête
localRequests.append(action)
@@ -211,6 +289,7 @@ def main(server_ip: str):
# on vérifie que newPlane n'est pas None (les valeurs ont été renvoyés)
if newPlaneData:
+
# on crée alors un nouvel avion
FL = None
PFL = None
@@ -230,7 +309,11 @@ def main(server_ip: str):
FL=FL,
PFL=PFL)
- localRequests.append((len(dictAvions), "Add", newPlane))
+ if newPlaneData['conflit']:
+ conflitGen = outils_radar.conflictGenerator(win, newPlane, carte)
+ conflitBool = True
+ else:
+ localRequests.append((len(dictAvions), "Add", newPlane))
elif event.type == pygame_gui.UI_TEXT_ENTRY_CHANGED:
nouvelAvionWin.checkFields(event)
@@ -262,17 +345,36 @@ def main(server_ip: str):
if curseur_alidad:
alidad = True
alidadPos = pygame.mouse.get_pos()
+ elif curseur_cercles:
+ souris = pygame.mouse.get_pos()
+ cerclePos = ((souris[0] - scroll[0]) / zoom, (souris[1] - scroll[1]) / zoom)
- elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 3 and curseur_alidad:
+ elif event.type == pygame.MOUSEBUTTONDOWN and event.button == 3 and (curseur_alidad or curseur_cercles or curseur_aliSep):
alidad = False
curseur_alidad = False
+ curseur_cercles = False
+ curseur_aliSep = False
pygame.mouse.set_cursor(pygame.cursors.arrow)
+ elif (event.type == pygame.MOUSEBUTTONUP and event.button == 1 and not empecherDragging
+ and not (curseur_aliSep or curseur_alidad) and mouseDownTime + 150 >= pygame.time.get_ticks()):
+ if curseur_cercles:
+ curseur_cercles = False
+ else:
+ menuRadar.show()
+
+ elif event.type == pygame.MOUSEBUTTONUP and event.button == 2 and not empecherDragging and conflitGen:
+ mouse = pygame.mouse.get_pos()
+ conflitGen.computeSpawn(((mouse[0] - scroll[0]) / zoom, (mouse[1] - scroll[1]) / zoom), carte)
+
manager.process_events(event)
if menuAvion is not None:
menuAvion.checkSliders()
+ if conflitGen is not None:
+ conflitGen.checkScrollBar(carte)
+
"""Dragging"""
if (pygame.mouse.get_pressed()[0] and not empecherDragging and
@@ -284,7 +386,7 @@ def main(server_ip: str):
drag = [pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]]
else:
drag = [pygame.mouse.get_pos()[0], pygame.mouse.get_pos()[1]]
- if not curseur_alidad:
+ if not curseur_alidad and not curseur_aliSep and not curseur_cercles:
pygame.mouse.set_cursor(pygame.cursors.arrow)
"""Keys"""
@@ -298,38 +400,12 @@ def main(server_ip: str):
scroll = scrollDef
pressing = True
delaiPressage = pygame.time.get_ticks()
- if keys[pygame.K_t]: # type avions
- affichage_type_avion = not affichage_type_avion
- pressing = True
- delaiPressage = pygame.time.get_ticks()
- if keys[pygame.K_a]: # alidad start
- curseur_alidad = True
- pygame.mouse.set_cursor(pygame.cursors.broken_x)
- pressing = True
- delaiPressage = pygame.time.get_ticks()
+
if keys[pygame.K_f] and flightDataWindow is None: # Flight Data Window
flightDataWindow = interface.flightDataWindow()
pressing = True
delaiPressage = pygame.time.get_ticks()
- # commandes vecteurs
- if keys[pygame.K_3]:
- vecteurSetting = 3
- pressing = True
- delaiPressage = pygame.time.get_ticks()
- if keys[pygame.K_6]:
- vecteurSetting = 6
- pressing = True
- delaiPressage = pygame.time.get_ticks()
- if keys[pygame.K_9]:
- vecteurSetting = 9
- pressing = True
- delaiPressage = pygame.time.get_ticks()
- if keys[pygame.K_v]:
- vecteurs = not vecteurs
- pressing = True
- delaiPressage = pygame.time.get_ticks()
-
# commandes pilote
if keys[pygame.K_n] and nouvelAvionWin is None and pilote:
nouvelAvionWin = interface.nouvelAvionWindow(carte['routes'], perfos)
@@ -354,6 +430,7 @@ def main(server_ip: str):
if keys[pygame.K_s]:
localRequests.append((0, 'Save'))
delaiPressage = pygame.time.get_ticks()
+
elif True not in pygame.key.ScancodeWrapper() and pygame.time.get_ticks() - delaiPressage >= 150:
# on vérifie que plus aucune touche n'est pressée et on remet la variable à son état initial
pressing = False
@@ -379,24 +456,38 @@ def main(server_ip: str):
if not nouvelAvionWin.checkAlive():
nouvelAvionWin = None
+ if menuRadar.checkActive():
+ menuRadar.checkMenuHovered()
+
'''partie affichage'''
# on remplit d'abord avec une couleur
win.fill((90, 90, 90))
# on dessine les secteurs
- for secteur in carte['secteurs']:
+ for zone in carte['zones']:
liste_affichage_secteurs = []
- for point in secteur['contour']:
+ for point in zone['contour']:
pos = positionAffichage(point[0], point[1], zoom, scroll[0], scroll[1])
liste_affichage_secteurs.append((pos[0], pos[1]))
- pygame.draw.polygon(win, secteur['couleur'], liste_affichage_secteurs)
+ pygame.draw.polygon(win, zone['couleur'], liste_affichage_secteurs)
# on dessine les routes
for segment in carte['segments']['TRANSIT']:
pygame.draw.line(win, (150, 150, 150), (segment[0][0]*zoom + scroll[0], segment[0][1]*zoom + scroll[1]),
(segment[1][0]*zoom + scroll[0], segment[1][1]*zoom + scroll[1]), 1)
+ # dessin des cercles concentriques
+ if cerclePos is not None:
+
+ for i in range(15):
+
+ pygame.draw.circle(
+ win, (120, 120, 120),
+ (cerclePos[0] * zoom + scroll[0], cerclePos[1] * zoom + scroll[1]),
+ 10 * i / mapScale * zoom, 1
+ )
+
# on dessine les points
for nom, point in carte['points'].items():
pygame.draw.polygon(win, (155, 155, 155), ((point[0]*zoom + scroll[0], point[1]*zoom - 2 + scroll[1]), (point[0]*zoom + 2 + scroll[0], point[1]*zoom+2 + scroll[1]), (point[0]*zoom-2 + scroll[0], point[1]*zoom+2 + scroll[1])), 1)
@@ -404,12 +495,23 @@ def main(server_ip: str):
# win.blit(img, (point[0]*zoom + 10 + scroll[0], point[1]*zoom+10 + scroll[1]))
# on affiche les avions
- if pilote:
- for avion in dictAvionsAff.values():
- avion.draw(win, zoom, scroll, vecteurs, vecteurSetting, carte['points'])
- else:
+
+ if conflitGen is not None:
+ conflitGen.draw(win, zoom, scroll)
+ color = [10, 10, 10]
for avion in dictAvionsAff.values():
- avion.draw(win, zoom, scroll, vecteurs, vecteurSetting, carte['points'])
+ if color[0] <= 255 - 40:
+ color[0] += 70
+ elif color[1] <= 255 - 40:
+ color[0] = 255
+ color[1] += 70
+ elif color[2] <= 255 - 40:
+ color[1] = 255
+ color[2] += 70
+ avion.drawEstimatedRoute(carte['points'], conflitGen.temps, color, win, zoom, scroll)
+
+ for avion in dictAvionsAff.values():
+ avion.draw(win, zoom, scroll, vecteurs, vecteurSetting, carte['points'])
# on affiche les boutons
manager.update(time_delta)
diff --git a/geometry.py b/Python/geometry.py
similarity index 55%
rename from geometry.py
rename to Python/geometry.py
index 16b2819..48a6641 100644
--- a/geometry.py
+++ b/Python/geometry.py
@@ -1,5 +1,10 @@
+
+# Native imports
import math
+
+# Module imports
import numpy as np
+from scipy.optimize import minimize
def calculateHeading(x: int, y: int, xPoint: int, yPoint: int):
@@ -20,17 +25,21 @@ def calculateHeading(x: int, y: int, xPoint: int, yPoint: int):
return heading
-def calculateDistance(x1: int, y1: int, x2: int, y2: int):
+def calculateDistance(x1: int | float, y1: int | float, x2: int | float, y2: int | float):
return math.sqrt((x1 - x2) ** 2 + (y1 - y2) ** 2)
-def calculateShortestPoint(pointDroite1: list[float, float], pointDroite2: list[float, float],
- point: list[float, float]):
+def calculateShortestPoint(pointDroite1: list[float, float] | tuple[float, float],
+ pointDroite2: list[float, float] | tuple[float, float],
+ point: list[float, float] | tuple[float, float],
+ segment=False
+ ):
"""
Calcule le point sur une droite où la distance ets la plus courte à un point
:param pointDroite1: 1er point pour définir la droite, vecteur2 (x, y)
:param pointDroite2: 2em point pour définir la droite, vecteur2 (x, y)
:param point: point avec lequel on fait le calcul, vecteur2 (x, y)
+ :param segment: si on doit considérer comme un objet ou comme une droite
:return: retourne le point d'intersection du segment et de la droite (x, y)
"""
@@ -47,13 +56,40 @@ def calculateShortestPoint(pointDroite1: list[float, float], pointDroite2: list[
left_side = np.array([[-coeffdroite1, 1], [-coeffdroitePerp, 1]])
right_side = np.array([ordonnee1, ordonnee2])
else: # si la droite est verticale alors c'est super simple de trouver les coords du point
- return pointDroite1[0], point[1]
+
+ solution = pointDroite1[0], point[1]
+
+ if segment:
+
+ if not pointDroite1[1] <= pointDroite2[1]: # on trie les points pour que le 1 ai le y le plus petit
+ pointDroite1, pointDroite2 = pointDroite2, pointDroite1
+
+ if point[1] <= pointDroite1[1]: # on vérifie ensuite si le y est ou non compris dans le segment
+ solution = pointDroite1
+
+ elif point[1] >= pointDroite2[1]:
+ solution = pointDroite2
+
+ return solution
# solve for x and y
- return np.linalg.inv(left_side).dot(right_side)
+ solution = np.linalg.inv(left_side).dot(right_side)
+ if segment:
+ if not pointDroite1[0] <= pointDroite2[0]: # on trie les points pour que le 1 ai le x le plus petit
+ pointDroite1, pointDroite2 = pointDroite2, pointDroite1
-def calculateIntersection(point1Droite, point2Droite, point1Droite2, point2Droite2) -> tuple[float, float]:
+ if solution[0] <= pointDroite1[0]: # on vérifie ensuite si le x est ou non compris dans le segment
+ solution = pointDroite1
+
+ elif solution[0] >= pointDroite2[0]:
+ solution = pointDroite2
+
+ return solution
+
+
+def calculateIntersection(point1Droite, point2Droite,
+ point1Droite2, point2Droite2) -> tuple[float, float]:
"""
Calcule l'intersection entre deux droites
@@ -124,3 +160,67 @@ def calculateAngle(principal, secondaire):
return [principal - secondaire, 180 - principal + secondaire]
else:
return [secondaire - principal + 360, principal - 180 - secondaire]
+
+
+def distanceMinie(pos1: tuple[float, float], vitesse1: float, heading1: float,
+ pos2: tuple[float, float], vitesse2: float, heading2: float) -> float:
+ """
+ Calcule la position future de deux avions où la distance sera minimale entre les deux.
+ :param pos1: Position actuelle de l'avion 1 en px
+ :param vitesse1: Vitesse actuelle de l'avion 1 en px/sec
+ :param heading1: Heading actuelle de l'avion 1 en radiants
+ :param pos2: Position actuelle de l'avion 2 en px
+ :param vitesse2: Vitesse actuelle de l'avion 2 en px/sec
+ :param heading2: Heading actuelle de l'avion 2 en radiants
+ :return: Temps dans lequel la distance sera la plus faible
+ """
+
+ x0 = 1.0
+
+ caca = minimize(
+ distanceMiniEnFduTemps, x0, args=(pos1, vitesse1, heading1, pos2, vitesse2, heading2),
+ method='Nelder-Mead', tol=1e-4
+ )
+ return caca.x[0]
+
+
+def distanceMiniEnFduTemps(temps, pos1: tuple[float, float], vitesse1: float, heading1: float,
+ pos2: tuple[float, float], vitesse2: float, heading2: float):
+
+ return math.sqrt(
+ ((pos1[0] + vitesse1 * temps * math.cos(heading1)) - (pos2[0] + vitesse2 * temps * math.cos(heading2))) ** 2 +
+ ((pos1[1] + vitesse1 * temps * math.sin(heading1)) - (pos2[1] + vitesse2 * temps * math.sin(heading2))) ** 2
+ )
+
+
+def findClosestSegment(route: list, position: tuple[float, float], points: dict) -> tuple:
+ """
+ Retourne les 2 points du segment de la route le plus proche de notre position dans l'ordre de la route.
+ :param route: La route qu'on analyse
+ :param position: La position qu'on veut comparer
+ :param points: La carte du jeu
+ :return:
+ """
+
+ start = route[0]
+ end = route[0]
+ distance = 99999999
+
+ for index in range(len(route) - 1): # dans cette boucle, on cherche à quel segment on est le plus proche
+ point = route[index]
+ point2 = route[index + 1]
+ coords1 = (points[point['name']][0], points[point['name']][1])
+ coords2 = (points[point2['name']][0], points[point2['name']][1])
+
+ # d'abord, on trouve le point le plus proche de notre pos sur ce segment
+ intersection = calculateShortestPoint(
+ coords1, coords2, position, True)
+
+ disancte = calculateDistance(position[0], position[1], intersection[0], intersection[1])
+
+ if disancte <= distance:
+ start = point
+ end = point2
+ distance = disancte
+
+ return start, end
diff --git a/horloge.py b/Python/horloge.py
similarity index 100%
rename from horloge.py
rename to Python/horloge.py
diff --git a/interface.py b/Python/interface.py
similarity index 84%
rename from interface.py
rename to Python/interface.py
index 8c13821..7a8a304 100644
--- a/interface.py
+++ b/Python/interface.py
@@ -1,7 +1,9 @@
+
+# Module imports
import pygame
import pygame_gui
-import horloge
-from valeurs_config import *
+import Python.horloge as horloge
+from Python.valeurs_config import *
def selectButtonInList(liste: list, event):
@@ -12,13 +14,14 @@ def selectButtonInList(liste: list, event):
bouton.unselect()
-def scrollListGen(valueList, rect, container, sliderBool=True, sliderDroite=False):
+def scrollListGen(valueList, rect, container, sliderBool=True, sliderDroite=False, objectID=None):
"""Fonction qui construit une liste de boutons, avec une scrollbar width 17
:arg valueList: liste des valeurs en txt ou autre peu importe
:arg rect: taille des boutons
:arg container: conteneur Pygame_gui dans lequel on met les boutons
:parameter sliderBool: Bool, si on veut un slider ou non. True par defaut
:parameter sliderDroite: Bool, si on veut le slider à droite ou non
+ :parameter objectID: Une option de thème optionnelle pour les boutons.
:return (slider, listeBoutons)"""
valueList = list(valueList)
@@ -41,7 +44,7 @@ def scrollListGen(valueList, rect, container, sliderBool=True, sliderDroite=Fals
liste = [pygame_gui.elements.UIButton(
relative_rect=rect,
text=str(valueList[0]),
- container=container, anchors=boutonAnchor)]
+ container=container, anchors=boutonAnchor, object_id=objectID)]
for value in valueList[1:]:
boutonAnchor.update({'top': 'top', 'top_target': liste[-1]})
@@ -49,7 +52,8 @@ def scrollListGen(valueList, rect, container, sliderBool=True, sliderDroite=Fals
relative_rect=rect,
text=str(value),
container=container,
- anchors=boutonAnchor))
+ anchors=boutonAnchor,
+ object_id=objectID))
if sliderDroite:
decalage = liste[0].get_abs_rect()[2]
@@ -63,7 +67,7 @@ class nouvelAvionWindow:
def __init__(self, routes, avions):
# le dictionnaire utilisé pour renvoyer les valeurs sélectionnées par nos boutons
- self.returnValues = {'indicatif': 'FCACA', 'avion': 'B738', 'arrival': False}
+ self.returnValues = {'indicatif': 'FCACA', 'avion': 'B738', 'arrival': False, 'conflit': False}
# la fenêtre du menu
self.window = pygame_gui.elements.UIWindow(pygame.Rect((250, 250), (600, 400)))
@@ -77,7 +81,10 @@ def __init__(self, routes, avions):
# la liste des types avion
self.typeAvionContainer = pygame_gui.elements.UIScrollingContainer(pygame.Rect((0, 34), (150, 200)),
- container=self.window, anchors={'left': 'left', 'left_target': self.routeContainer}, allow_scroll_x=False)
+ container=self.window,
+ anchors={'left': 'left',
+ 'left_target': self.routeContainer},
+ allow_scroll_x=False)
self.typeAvionBoutonListe = scrollListGen(
list(avions.keys()), pygame.Rect((0, 0), (125, 17)), self.typeAvionContainer, False)[1]
@@ -88,7 +95,8 @@ def __init__(self, routes, avions):
relative_rect=pygame.Rect((0, 20), (200, 17)),
text='Générateur de conflits',
container=self.window,
- anchors={'top': 'top', 'top_target': self.typeAvionContainer, 'left': 'left', 'left_target': self.routeContainer})
+ anchors={'top': 'top', 'top_target': self.typeAvionContainer, 'left': 'left',
+ 'left_target': self.routeContainer})
self.arrivalBouton = pygame_gui.elements.UIButton(
relative_rect=pygame.Rect((0, 5), (200, 17)),
@@ -101,7 +109,8 @@ def __init__(self, routes, avions):
relative_rect=pygame.Rect((0, 5), (200, 17)),
text='Ok',
container=self.window,
- anchors={'top': 'top', 'top_target': self.arrivalBouton, 'left': 'left', 'left_target': self.routeContainer})
+ anchors={'top': 'top', 'top_target': self.arrivalBouton, 'left': 'left',
+ 'left_target': self.routeContainer})
self.indicatiflabel = pygame_gui.elements.UILabel(
relative_rect=pygame.Rect((0, 0), (200, 17)),
@@ -112,12 +121,14 @@ def __init__(self, routes, avions):
self.indicatifinput = pygame_gui.elements.UITextEntryBox(
relative_rect=pygame.Rect((0, 0), (200, 30)),
container=self.window,
- anchors={'left': 'left', 'left_target': self.typeAvionContainer, 'top': 'top', 'top_target': self.indicatiflabel})
+ anchors={'left': 'left', 'left_target': self.typeAvionContainer, 'top': 'top',
+ 'top_target': self.indicatiflabel})
self.FLlabel = pygame_gui.elements.UILabel(
relative_rect=pygame.Rect((0, 0), (200, 17)),
container=self.window,
- anchors={'left': 'left', 'left_target': self.typeAvionContainer, 'top': 'top', 'top_target': self.indicatifinput},
+ anchors={'left': 'left', 'left_target': self.typeAvionContainer, 'top': 'top',
+ 'top_target': self.indicatifinput},
text='FL')
self.FLinput = pygame_gui.elements.UITextEntryBox(
@@ -161,6 +172,13 @@ def checkEvent(self, event):
else:
self.arrivalBouton.unselect()
+ elif event.ui_element == self.conflitsBouton:
+ self.returnValues['conflit'] = not self.returnValues['conflit']
+ if not self.conflitsBouton.is_selected:
+ self.conflitsBouton.select()
+ else:
+ self.conflitsBouton.unselect()
+
elif event.ui_element == self.validerBouton:
self.window.kill()
return self.returnValues
@@ -249,8 +267,9 @@ def __init__(self, avion):
container=self.window,
anchors={'left': 'left', 'left_target': self.altiContainer})
- tempo = scrollListGen(range(round(avion.papa.speedIAS / 10) * 10 - 50, round(avion.papa.speedIAS / 10) * 10 + 60, 10),
- pygame.Rect((0, 0), (75, 17)), self.speedContainer)
+ tempo = scrollListGen(
+ range(round(avion.papa.speedIAS / 10) * 10 - 50, round(avion.papa.speedIAS / 10) * 10 + 60, 10),
+ pygame.Rect((0, 0), (75, 17)), self.speedContainer)
self.speedBoutonListe = tempo[1]
self.speedSlider = tempo[0]
@@ -261,7 +280,7 @@ def __init__(self, avion):
self.pointContainer = pygame_gui.elements.UIScrollingContainer(
pygame.Rect((0, 0), (100, 200)),
container=self.window,
- anchors={'left': 'left','left_target': self.speedContainer})
+ anchors={'left': 'left', 'left_target': self.speedContainer})
tempo = scrollListGen([point['name'] for point in avion.papa.route['points']],
pygame.Rect((0, 0), (75, 17)),
@@ -425,7 +444,7 @@ def __init__(self, avion):
self.AFL = pygame_gui.elements.UIButton(
relative_rect=pygame.Rect((0, 0), (-1, -1)),
- text=str(round(avion.papa.altitude/100)),
+ text=str(round(avion.papa.altitude / 100)),
generate_click_events_from=clicks,
object_id=pygame_gui.core.ObjectID('@etiquetteBold', 'rose'),
anchors={'top': 'top', 'top_target': self.indicatif},
@@ -530,9 +549,10 @@ def update(self, avion):
self.DCT.set_text("h")
self.XPT.set_text(avion.papa.XPT)
+ self.nextSector.set_text(avion.papa.nextSector)
# alti
- self.AFL.set_text(str(round(avion.papa.altitude/100)) + " " + avion.papa.altitudeEvoTxt)
+ self.AFL.set_text(str(round(avion.papa.altitude / 100)) + " " + avion.papa.altitudeEvoTxt)
self.CFL.set_text(str(avion.papa.CFL)[:2])
@@ -618,7 +638,7 @@ def __init__(self, avion, pos):
width = 80
height = 120
- x = pos[0] - width/2
+ x = pos[0] - width / 2
y = pos[1] - 35
if avion.papa.etatFrequence == 'previousFreq':
@@ -760,14 +780,17 @@ def __init__(self, avion, pos: list[float, float], valeur: str):
self.headingDCT = None
self.listeGauche = None
self.listeDroite = None
+ objectID = None
if valeur == 'DCT': # TODO boutons directs en blanc
self.liste = [point['name'] for point in self.avion.papa.route['points']]
self.listeAff = self.liste[self.liste.index(avion.papa.nextPoint['name']):]
+ objectID = pygame_gui.core.ObjectID('@menuLabel', 'menuBlanc')
elif valeur == 'XPT': # la diff de liste est qu'on ne prend pas en compte les points de notre secteur ici
self.liste = [point['name'] for point in self.avion.papa.route['points']]
self.listeAff = self.liste[self.liste.index(avion.papa.defaultXPT):]
+ objectID = pygame_gui.core.ObjectID('@menuLabel', 'menuBlanc')
elif valeur == 'C_IAS':
self.liste = [*range(0, 60)]
@@ -792,11 +815,12 @@ def __init__(self, avion, pos: list[float, float], valeur: str):
elif valeur == 'HDG':
- cap = round(avion.papa.heading // 5) * 5
+ cap = round(avion.papa.heading // 5) * 5 + 5
- self.liste = [*range(0, 365, 5)]
+ self.liste = [*range(5, 365, 5)]
indexDuCap = self.liste.index(cap)
- self.listeAff = self.liste[indexDuCap - 3: indexDuCap + 4]
+ self.listeAff = [self.liste[(indexDuCap - 3 + i) % len(self.liste)] for i in range(7)]
+ objectID = pygame_gui.core.ObjectID('@menuLabel', 'menuBlanc')
self.window = pygame_gui.elements.UIWindow(pygame.Rect((x, y), (width, height)),
window_display_title=valeur,
@@ -817,9 +841,9 @@ def __init__(self, avion, pos: list[float, float], valeur: str):
if valeur == 'C_IAS':
self.noeud = pygame_gui.elements.UIButton(
- pygame.Rect((0, 0), (width / 2, -1)),
- container=self.topContainer,
- text="K")
+ pygame.Rect((0, 0), (width / 2, -1)),
+ container=self.topContainer,
+ text="K")
self.mach = pygame_gui.elements.UIButton(
pygame.Rect((0, 0), (width / 2, -1)),
@@ -883,7 +907,7 @@ def __init__(self, avion, pos: list[float, float], valeur: str):
self.topContainer.set_dimensions((width, self.headingDCT.get_abs_rect()[3] * 2))
- if self.valeur == 'HDG': # TODO boutons cap abs en blanc
+ if self.valeur == 'HDG':
listeDroite = []
listeGauche = []
@@ -918,7 +942,7 @@ def __init__(self, avion, pos: list[float, float], valeur: str):
self.listeGauche = scrollListGen(
listeGauche,
- pygame.Rect((0, 0), (width / 3, -1)),
+ pygame.Rect((1, 0), (width / 3, -1)),
self.containerHdgGauche,
sliderBool=False)[1]
@@ -931,17 +955,16 @@ def __init__(self, avion, pos: list[float, float], valeur: str):
'left': 'left', 'left_target': self.containerHdgGauche}
)
- tempo = scrollListGen(
+ self.listeBoutons = scrollListGen(
self.listeAff,
- pygame.Rect((0, 0), (width / 3, -1)),
+ pygame.Rect((1, 0), (width / 3, -1)),
self.listeContainer,
- sliderBool=False)
-
- self.listeBoutons = tempo[1]
+ sliderBool=False,
+ objectID=objectID)[1]
self.containerHdgDroite = pygame_gui.elements.UIScrollingContainer(
container=self.window,
- relative_rect=pygame.Rect((0, 0), (width / 3, height)),
+ relative_rect=pygame.Rect((1, 0), (width / 3, height)),
allow_scroll_x=False,
allow_scroll_y=False,
anchors={'top': 'top', 'top_target': self.topContainer,
@@ -950,7 +973,7 @@ def __init__(self, avion, pos: list[float, float], valeur: str):
self.listeDroite = scrollListGen(
listeDroite,
- pygame.Rect((0, 0), (width / 3, -1)),
+ pygame.Rect((1, 0), (width / 3, -1)),
self.containerHdgDroite,
sliderBool=False)[1]
else:
@@ -964,19 +987,22 @@ def __init__(self, avion, pos: list[float, float], valeur: str):
tempo = scrollListGen(
self.listeAff,
- pygame.Rect((0, 0), (width, -1)),
+ pygame.Rect((1, 0), (width, -1)),
self.listeContainer,
- sliderBool=False)
+ sliderBool=False,
+ objectID=objectID)
self.listeBoutons = tempo[1]
if valeur in ['XFL', 'PFL', 'CFL']: # on ramène la fenêtre au bon endroit pour la souris (sur le PFL)
- y = y - (self.topContainer.get_abs_rect()[3] + self.listeBoutons[0].get_abs_rect()[3] * 2.5 + self.window.title_bar_height)
+ y = y - (self.topContainer.get_abs_rect()[3] + self.listeBoutons[0].get_abs_rect()[
+ 3] * 2.5 + self.window.title_bar_height)
self.window.set_position((x, y))
widthListeContainer = self.listeContainer.get_abs_rect()[2]
- self.listeContainer.set_dimensions((widthListeContainer, len(self.listeAff) * self.listeBoutons[0].get_abs_rect()[3]))
+ self.listeContainer.set_dimensions(
+ (widthListeContainer, len(self.listeAff) * self.listeBoutons[0].get_abs_rect()[3]))
self.window.set_minimum_dimensions((width, width))
height = (self.listeContainer.get_abs_rect()[3] +
self.topContainer.rect[3] +
@@ -1080,17 +1106,17 @@ def checkScrolled(self, event):
if not rect[0] <= mouse[0] <= rect[0] + rect[2] and rect[1] <= mouse[1] <= rect[1] + rect[3]:
return True
- if event.y >= 0: # on regarde dans quel sens on scroll
- indexDebut = self.liste.index(self.listeAff[0]) # on regarde où commence la liste dans l'autre
- if indexDebut - 1 >= 0: # cela vérifie qu'on n'est pas en butée de liste
- self.listeAff = self.liste[indexDebut - 1: indexDebut - 1 + len(self.listeAff)]
- self.scrollUpdate()
+ indexDebut = self.liste.index(self.listeAff[0])
+
+ if event.y >= 0: # on regarde dans quel sens on scroll
+ newIndex = (indexDebut - 1) % len(self.liste)
else:
- indexDebut = self.liste.index(self.listeAff[0]) # on regarde où commence la liste dans l'autre
- if indexDebut + len(self.listeAff) <= len(self.liste) - 1: # cela vérifie qu'on n'est pas en butée de liste
- self.listeAff = self.liste[indexDebut + 1: indexDebut + 1 + len(self.listeAff)]
- self.scrollUpdate()
+ newIndex = (indexDebut + 1) % len(self.liste)
+
+ self.listeAff = [self.liste[(newIndex + i) % len(self.liste)] for i in range(len(self.listeAff))]
+
+ self.scrollUpdate()
def scrollUpdate(self) -> None:
"""
@@ -1119,7 +1145,13 @@ def checkUnHovered(self, event) -> None:
"""
if self.valeur in ['DCT', 'XPT']:
if event.ui_element in self.listeBoutons:
- self.avion.pointDessinDirect = None
+ dessin = False
+ for element in self.listeBoutons:
+ if element.hovered:
+ dessin = True
+ break
+ if not dessin:
+ self.avion.pointDessinDirect = None
def checkMenuHovered(self) -> None:
"""
@@ -1224,7 +1256,7 @@ def linkAvion(self, avion, points: list, heure) -> None:
self.ligneDeux.set_text(text)
text = (avion.papa.provenance + ' ' + avion.papa.destination + ' R' + str(avion.papa.PFL)
- + ' ' + str(round(avion.papa.altitude/100)) + ' ' + str(avion.papa.PFL)
+ + ' ' + str(round(avion.papa.altitude / 100)) + ' ' + str(avion.papa.PFL)
+ ' ' + avion.papa.EPT + ' ' + avion.papa.XPT + ' X' + str(avion.papa.XFL))
self.ligneTrois.set_text(text)
@@ -1248,4 +1280,110 @@ def checkAlive(self):
return self.window.alive()
+class menuRadar:
+
+ def __init__(self):
+
+ width = 100
+ height = 200
+
+ self.window = pygame_gui.elements.UIWindow(
+ pygame.Rect((500, 500), (width, height)),
+ window_display_title="Flight Data Window",
+ object_id=pygame_gui.core.ObjectID('@menuRadar', 'blanc'),
+
+ )
+ ancres = {}
+ self.listeVecteurs = []
+ indice = 0
+
+ for b in range(3):
+
+ for bb in range(3):
+ indice += 1
+ self.listeVecteurs.append(
+ pygame_gui.elements.UIButton(pygame.Rect((0, 0), (width / 3, width / 3)),
+ text=str(indice) + "'",
+ container=self.window,
+ anchors=ancres,
+ object_id=pygame_gui.core.ObjectID('@menuRadar', 'blanc'),
+ generate_click_events_from=frozenset(
+ [pygame.BUTTON_LEFT, pygame.BUTTON_RIGHT])
+ ))
+ ancres.update({'left': 'left', 'left_target': self.listeVecteurs[-1]})
+ ancres.pop('left')
+ ancres.pop('left_target')
+ ancres.update({'top': 'top', 'top_target': self.listeVecteurs[-1]})
+
+ self.alidade = pygame_gui.elements.UIButton(pygame.Rect((0, 0), (width, width / 3)),
+ text="ALIDADE",
+ container=self.window,
+ anchors={'top': 'top', 'top_target': self.listeVecteurs[-1]},
+ object_id=pygame_gui.core.ObjectID('@menuRadar', 'blanc'))
+
+ self.cercles = pygame_gui.elements.UIButton(pygame.Rect((0, 0), (width, width / 3)),
+ text="@",
+ container=self.window,
+ anchors={'top': 'top', 'top_target': self.alidade},
+ object_id=pygame_gui.core.ObjectID('@menuRadar', 'blanc'))
+
+ ancres = {'top': 'top', 'top_target': self.cercles}
+ self.listeSep = []
+ for lettre in ['A', 'B', 'C']:
+ self.listeSep.append(
+ pygame_gui.elements.UIButton(pygame.Rect((0, 0), (width / 3, width / 3)),
+ text=lettre,
+ container=self.window,
+ anchors=ancres,
+ object_id=pygame_gui.core.ObjectID('@menuRadar', 'blanc')
+ ))
+ ancres.update({'left': 'left', 'left_target': self.listeSep[-1]})
+
+ self.lastHovered = pygame.time.get_ticks()
+ self.window.hide()
+
+ def show(self):
+
+ pos = pygame.mouse.get_pos()
+ rect = self.window.get_abs_rect()
+ self.window.set_position((pos[0] - rect[2] / 2, pos[1] - rect[3] / 2))
+ self.window.show()
+
+ def checkActive(self):
+ return self.window.visible
+
+ def checkEvent(self, event):
+
+ if event.ui_element in self.listeVecteurs:
+ if event.mouse_button == 1:
+ return 'VecteursToggle', int(event.ui_element.text[0])
+
+ elif event.mouse_button == 3:
+ return 'Vecteurs', int(event.ui_element.text[0])
+
+ elif event.ui_element in self.listeSep:
+ self.window.hide()
+ return 'Sep', event.ui_element.text
+
+ elif event.ui_element == self.alidade:
+ self.window.hide()
+ return 'Alidade'
+
+ elif event.ui_element == self.cercles:
+ self.window.hide()
+ return 'Cercles'
+
+ def checkMenuHovered(self) -> None:
+ """
+ Commence le compteur si n'est plus survolé
+ :return:
+ """
+ rect = self.window.get_abs_rect()
+ mouse = pygame.mouse.get_pos()
+
+ if rect[0] <= mouse[0] <= rect[0] + rect[2] and rect[1] <= mouse[1] <= rect[1] + rect[3]:
+ self.lastHovered = pygame.time.get_ticks()
+
+ elif pygame.time.get_ticks() - self.lastHovered > temps_disparition_menus:
+ self.window.hide()
diff --git a/network.py b/Python/network.py
similarity index 97%
rename from network.py
rename to Python/network.py
index f36d5cf..25f0a26 100644
--- a/network.py
+++ b/Python/network.py
@@ -1,3 +1,5 @@
+
+# Native imports
import socket
import pickle
diff --git a/Python/outils_radar.py b/Python/outils_radar.py
new file mode 100644
index 0000000..f030315
--- /dev/null
+++ b/Python/outils_radar.py
@@ -0,0 +1,266 @@
+
+# Native imports
+import math
+
+# Module imports
+import pygame
+import pygame_gui
+
+# Imports fichiers
+import Python.geometry as geometry
+from Python.valeurs_config import *
+
+
+class aliSep:
+
+ def __init__(self, lettre):
+ self.lettre = lettre
+ self.avion1 = None
+ self.avion2 = None
+ self.distance = None
+ self.temps = None
+
+ def linkAvion(self, avion, carte) -> bool:
+
+ """
+ Associe un avion. Si un avion est déjà associé alors celà déclenche le calcul de la sep
+ :param avion: l'avion à associer
+ :param carte: les données de la carte
+ :return: si les deux avions ont été associés, renvoies True
+ """
+
+ if self.avion2:
+ return True
+
+ if not self.avion1:
+ self.avion1 = avion
+ return False
+
+ self.avion2 = avion
+
+ self.avion1.sep = True
+ self.avion2.sep = True
+
+ self.calculation(carte)
+
+ return True
+
+ def calculation(self, carte):
+
+ if not self.avion2:
+ return None
+
+ self.temps = geometry.distanceMinie(
+ (self.avion1.papa.x, self.avion1.papa.y), self.avion1.papa.speedPx / radarRefresh,
+ self.avion1.papa.headingRad,
+ (self.avion2.papa.x, self.avion2.papa.y), self.avion2.papa.speedPx / radarRefresh,
+ self.avion2.papa.headingRad,
+ )
+
+ self.distance = math.sqrt(
+ ((self.avion1.papa.x + self.avion1.papa.speedPx / radarRefresh * self.temps * math.cos(
+ self.avion1.papa.headingRad)) -
+ (self.avion2.papa.x + self.avion2.papa.speedPx / radarRefresh * self.temps * math.cos(
+ self.avion2.papa.headingRad))) ** 2 +
+ ((self.avion1.papa.y + self.avion1.papa.speedPx / radarRefresh * self.temps * math.sin(
+ self.avion1.papa.headingRad)) -
+ (self.avion2.papa.y + self.avion2.papa.speedPx / radarRefresh * self.temps * math.sin(
+ self.avion2.papa.headingRad))) ** 2)
+
+ self.avion1.sepSetting.update({self.lettre: [self.temps, self.distance * carte['mapScale']]})
+ self.avion2.sepSetting.update({self.lettre: [self.temps, self.distance * carte['mapScale']]})
+
+ def kill(self):
+
+ if not self.avion2 and self.avion1:
+ self.avion1 = None
+ return None
+
+ if not self.avion1:
+ return None
+
+ self.avion1.sepSetting.pop(self.lettre)
+ self.avion2.sepSetting.pop(self.lettre)
+
+ if len(self.avion1.sepSetting) == 0:
+ self.avion1.sep = False
+ if len(self.avion2.sepSetting) == 0:
+ self.avion2.sep = False
+
+ self.avion1 = None
+ self.avion2 = None
+
+
+class conflictGenerator:
+
+ def __init__(self, win, avion, carte):
+
+ size = win.get_size()
+ width = size[0] / 3
+ height = 40
+
+ self.carte = carte
+
+ self.slider = pygame_gui.elements.UIHorizontalScrollBar(
+ pygame.Rect((size[0] / 2 - width / 2, 10), (width, height)),
+ visible_percentage=0.3,
+
+ )
+ self.valider = pygame_gui.elements.UIButton(
+ pygame.Rect((size[0] / 2 + width / 2 + 10, 10), (height, height)),
+ text='OK'
+ )
+
+ self.avion = None
+ self.temps = 0
+ self.maxTemps = 60 * 60 # temps en sec, donc 40 min au max
+ self.avion = avion
+ self.x = None
+ self.y = None
+ self.spawnDelay = None
+ self.drawListe = None
+
+ def checkScrollBar(self, carte):
+
+ """Ajuste la valeur de temps en fonction du slider s'il est scrollé"""
+
+ if self.slider.has_moved_recently:
+ self.temps = self.slider.start_percentage * self.maxTemps
+ if self.x:
+ self.increaseTime(self.temps, carte)
+
+ def checkEvent(self, event):
+
+ """
+ Vérifies si les boutons sont appuyés et prend les actions nécessaires.
+ :param event:
+ :return:
+ """
+ if event.ui_element == self.valider:
+ return self.kill()
+
+ def computeSpawn(self, pos: list[float, float] | tuple[float, float], carte):
+ """
+ Change la position, ou le delay de spawn de l'avion en fonction de la position voulue et des perfos
+ :param pos: La position de conflit choisie
+ :param carte: La carte du jeu
+ :return:
+ """
+
+ points = carte['points']
+ route = self.avion.route['points']
+ distance = self.temps * self.avion.speedPx / radarRefresh # quelle distance va parcourir l'avion en ce temps
+ distance_calcule = 0
+
+ point2 = points[route[0]['name']]
+
+ p = geometry.findClosestSegment(self.avion.route['points'], pos, carte['points'])[0]
+
+ for index in range(len(route[:route.index(p)])):
+ point1 = points[route[index]['name']]
+ point2 = points[route[index + 1]['name']]
+ distance_calcule += geometry.calculateDistance(point1[0], point1[1], point2[0], point2[1])
+
+ offroadDistance = geometry.calculateDistance(pos[0], pos[1], point2[0], point2[1])
+ distance_calcule += offroadDistance
+
+ a = list(range(len(route[:route.index(p) + 1])))
+ self.drawListe = []
+
+ if distance_calcule <= distance: # si on doit parcourir plus que ce qu'on a calculé au spawn
+ # alors on delay le spawn, temps ici en sec
+ self.spawnDelay = int((distance - distance_calcule) / self.avion.speedPx * radarRefresh)
+ self.x = points[route[0]['name']][0]
+ self.y = points[route[0]['name']][1]
+
+ for index in a:
+ self.drawListe.append(points[route[index]['name']])
+
+ else: # si on doit parcourir moins, alors on fait apparaître l'avion plus proche du secteur
+ self.spawnDelay = None
+ distanceAparcourir = distance_calcule - distance
+ index = 0
+ found = False
+ for index in a:
+ point1 = points[route[index]['name']]
+ point2 = points[route[index + 1]['name']]
+ legDistance = geometry.calculateDistance(point1[0], point1[1], point2[0], point2[1])
+
+ if found:
+ self.drawListe.append(point1)
+
+ elif legDistance >= distanceAparcourir: # si on doit faire apparaître sur cette branche
+ ratio = distanceAparcourir / legDistance
+ self.x = ratio * (point2[0] - point1[0]) + point1[0]
+ self.y = ratio * (point2[1] - point1[1]) + point1[1]
+ self.drawListe.append((self.x, self.y))
+ self.avion.x = self.x
+ self.avion.y = self.y
+ found = True
+
+ else:
+ distanceAparcourir -= legDistance
+
+ self.drawListe.append(pos)
+
+ def draw(self, win, zoom, scroll) -> None:
+ if self.x is None:
+ return None
+ pygame.draw.circle(win, (255, 255, 0), (self.x * zoom + scroll[0], self.y * zoom + scroll[1]), 3)
+ for index in range(len(self.drawListe) - 1):
+ point1 = self.drawListe[index]
+ point2 = self.drawListe[index + 1]
+ pygame.draw.line(win, (255, 255, 0),
+ (point1[0] * zoom + scroll[0], point1[1] * zoom + scroll[1]),
+ (point2[0] * zoom + scroll[0], point2[1] * zoom + scroll[1]))
+
+ if self.spawnDelay:
+ font = pygame.font.SysFont('arial', 15)
+ img = font.render("Délai à l'apparition: " + str(self.spawnDelay) + "s", True, (170, 170, 255))
+ win.blit(img, (self.drawListe[0][0] * zoom + scroll[0], self.drawListe[0][1] * zoom + scroll[1]))
+
+ def increaseTime(self, temps, carte):
+
+ """
+ Augmente le temps de dessin sans changer le point de spawn
+ :param carte: La carte du jeu
+ :param temps: Le nouveau temps de dessin
+ :return:
+ """
+ points = carte['points']
+ self.temps = temps
+ delay = 0
+ if self.spawnDelay:
+ delay = self.spawnDelay
+ distance = (self.temps - delay) * self.avion.speedPx / radarRefresh # quelle distance va parcourir l'avion en ce temps
+
+ startPlot = geometry.findClosestSegment(self.avion.route['points'], (self.x, self.y), points)[1]
+ liste = self.avion.route['points'][self.avion.route['points'].index(startPlot):]
+ self.drawListe = [(self.x, self.y)]
+
+ point1 = (self.x, self.y)
+ self.drawListe.append(point1)
+ for index in range(len(liste)):
+ point2 = points[liste[index]['name']]
+
+ legDistance = geometry.calculateDistance(point1[0], point1[1], point2[0], point2[1])
+ if distance - legDistance <= 0:
+ ratio = distance / legDistance
+ x = ratio * (point2[0] - point1[0]) + point1[0]
+ y = ratio * (point2[1] - point1[1]) + point1[1]
+ self.drawListe.append(point1)
+ self.drawListe.append((x, y))
+ break
+
+ else:
+ distance -= legDistance
+ self.drawListe.append(point2)
+ point1 = point2
+
+ def kill(self):
+ self.valider.kill()
+ self.slider.kill()
+ self.avion.findNextPoint(self.carte)
+ if self.spawnDelay:
+ return self.spawnDelay, self.avion
+ return self.avion
diff --git a/paquets_avion.py b/Python/paquets_avion.py
similarity index 88%
rename from paquets_avion.py
rename to Python/paquets_avion.py
index 43db78d..65c4259 100644
--- a/paquets_avion.py
+++ b/Python/paquets_avion.py
@@ -1,7 +1,11 @@
-from geometry import *
-from valeurs_config import *
+
+# Native imports
import random
+# Imports fichiers
+from Python.geometry import *
+from Python.valeurs_config import *
+
class Game:
def __init__(self, heure):
@@ -69,8 +73,15 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL=
self.callsignFreq = 'Austrian' # TODO ajouter les callsigns
- self.provenance = random.choice(gameMap['aeroports'][route['provenance']])
- self.destination = random.choice(gameMap['aeroports'][route['destination']])
+ if route['type'] == 'DEPART':
+ self.provenance = route['provenance']
+ else:
+ self.provenance = random.choice(gameMap['aeroports'][route['provenance']])
+
+ if self.arrival:
+ self.destination = route['arrival']['aeroport']
+ else:
+ self.destination = random.choice(gameMap['aeroports'][route['destination']])
# perfo
self.turnRate = turnRateDefault
@@ -81,12 +92,9 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL=
self.route = route
self.headingMode = False
- # TODO changer ça pour pouvoir mettre un avion nimporte ou sur la route
- if calculateDistance(self.x, self.y, gameMap['points'][self.route['points'][0]['name']][0],
- gameMap['points'][self.route['points'][0]['name']][1]) <= 4 * self.speedPx:
- self.nextPoint = self.route['points'][1]
- else:
- self.nextPoint = self.route['points'][0]
+
+ self.nextPoint = None
+ self.findNextPoint(gameMap)
self.evolution = 0 # taux de variation/radar refresh
self.altitudeEvoTxt = '-'
@@ -118,14 +126,14 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL=
self.XFL = 300
for sortie in self.route['sortie']:
- print(sortie['min'] < self.PFL < sortie['max'])
if sortie['min'] < self.PFL < sortie['max']:
self.nextSector = sortie['name']
if not self.nextSector: # si on n'a pas réussi à mettre un secteur suivant, on met un défaut pour pas crash
self.nextSector = secteurDefault
- self.nextFrequency = '127.675' # TODO faire les fréquences automatiques
+ self.nextFrequency = gameMap['secteurs'][self.nextSector]['frequence']
+ self.etranger = gameMap['secteurs'][self.nextSector]['etranger']
self.CFL = None
@@ -153,19 +161,23 @@ def __init__(self, gameMap, Id, indicatif, aircraft, perfos, route, arrival, FL=
self.clearedHeading = None
self.clearedRate = None
- def changeXFL(self) -> None:
+ def findNextPoint(self, carte):
+
+ self.nextPoint = findClosestSegment(self.route['points'], (self.x, self.y), carte['points'])[1]
+
+ def changeXFL(self, carte) -> None:
"""
Change le XFL en fonction du PFL. À utliser quand le PFL change
:return:
"""
- print(self.PFL)
if self.PFL > 365 and not self.arrival:
self.nextSector = "RU"
self.XFL = 360
elif not self.arrival:
self.XFL = self.PFL
+ self.changeSortieSecteur(carte)
- def changeSortieSecteur(self) -> None:
+ def changeSortieSecteur(self, carte) -> None:
"""
Change le secteur de sortie. À utiliser quand le XFL change
:return:
@@ -175,6 +187,8 @@ def changeSortieSecteur(self) -> None:
if sortie['min'] < self.XFL < sortie['max']:
self.nextSector = sortie['name']
break
+ self.etranger = carte['secteurs'][self.nextSector]['etranger']
+ self.nextFrequency = carte['secteurs'][self.nextSector]['frequence']
def updateEtatFreq(self, nouvelEtat=None) -> None:
"""
@@ -188,6 +202,8 @@ def updateEtatFreq(self, nouvelEtat=None) -> None:
else:
i = liste_etat_freq.index(self.etatFrequence)
if i != len(liste_etat_freq) - 1: # on vérifie que ce n'est pas le dernier état fréquence
+ if self.etranger and self.etatFrequence == 'nextCoord':
+ i += 1
self.etatFrequence = liste_etat_freq[i + 1]
def updateAlti(self):
@@ -219,6 +235,12 @@ def updateAlti(self):
self.evolution = 0
def move(self, gameMap):
+
+ # frequence update
+ if self.etatFrequence == 'inFreq':
+
+ if self.calculeEstimate(gameMap['points'], self.XPT) <= 60 * valeurCoord: # si on sort dans moins de 6min
+ self.updateEtatFreq()
# heading update
if not self.headingMode: # si l'avion est en direct et pas en cap
@@ -233,9 +255,7 @@ def move(self, gameMap):
ancien = self.nextPoint # pour la comparaison après
self.nextPoint = self.route['points'][self.route['points'].index(self.nextPoint) + 1]
if ancien['name'] == self.DCT: # si le point clairé est celui qu'on passe
- self.DCT = self.nextPoint['name'] # alors on passe au point suivant aussi
-
-
+ self.DCT = self.nextPoint['name'] # alors, on passe au point suivant aussi
self.selectedHeading = calculateHeading(self.x, self.y, gameMap['points'][self.nextPoint['name']][0],
gameMap['points'][self.nextPoint['name']][1])
@@ -270,7 +290,7 @@ def move(self, gameMap):
self.x += self.speedPx * math.cos(self.headingRad)
self.y += self.speedPx * math.sin(self.headingRad)
- def calculeEstimate(self, points: dict, pointVoulu: str) -> None:
+ def calculeEstimate(self, points: dict, pointVoulu: str) -> float:
"""
Calcule le temps à un point sur notre route. Pour avoir l'estimée, il faudra rajouter l'heure courante
:param points: dict des points de la carte
diff --git a/player.py b/Python/player.py
similarity index 82%
rename from player.py
rename to Python/player.py
index 4820b68..5cf8416 100644
--- a/player.py
+++ b/Python/player.py
@@ -1,9 +1,15 @@
-import pygame
+
+# Native imports
import math
+
+# Module imports
+import pygame
import pygame_gui
-from valeurs_config import *
-import geometry
-import interface
+
+# Imports fichiers
+from Python.valeurs_config import *
+import Python.geometry as geometry
+import Python.interface as interface
def positionAffichage(x: int, y: int, zoom: float, scrollX: float, scrollY: float):
@@ -55,9 +61,12 @@ def __init__(self, Id: int, papa, zoom: float, scroll: list[float, float]):
self.predictionPoint = None # point pour la prédiction de route
self.drawRouteBool = False
self.locWarning = False
+ self.sep = False
+ self.sepSetting = {} # [temps à dessiner, distance minie en nm]
self.etiquetteExtended = False
self.lastHoveredTime = 0
self.pointDessinDirect = None
+ self.conflitSelected = False
self.drag = False # if true l'etiquette se fait drag
self.dragOffset = (0, 0) # le décalage de l'etiquette par rapport au curseur
self.startPressTime = 0
@@ -102,6 +111,49 @@ def drawVector(self, color, window, vecteurSetting, zoom):
self.affY + plotSize + self.papa.speedPx * 60 / radarRefresh * vecteurSetting * zoom * math.sin(
self.papa.headingRad)), 2)
+ def drawSep(self, window, zoom):
+ """
+ Dessine la sep avec un autre avion
+ :param window:
+ :param zoom:
+ :return:
+ """
+
+ sep = (0, 9999999)
+
+ for lettre, sepSetting in self.sepSetting.items():
+ if sepSetting[1] <= sep[1] and 0 <= sepSetting[0]:
+ sep = sepSetting
+
+ sepSetting = sep
+
+ coords = [self.affX + plotSize + self.papa.speedPx / radarRefresh * sepSetting[0] * zoom * math.cos(
+ self.papa.headingRad),
+ self.affY + plotSize + self.papa.speedPx / radarRefresh * sepSetting[0] * zoom * math.sin(
+ self.papa.headingRad)
+ ]
+
+ if sepSetting[1] <= 6:
+ color = (255, 0, 0)
+ elif sepSetting[1] <= 9:
+ color = (241, 237, 57)
+ else:
+ color = (157, 193, 126)
+
+ pygame.draw.line(window, color, (self.affX + plotSize, self.affY + plotSize), coords, 2)
+
+ font = pygame.font.SysFont('arial', 15)
+
+ coords[0] += 2
+ for lettre, sepSetting in self.sepSetting.items():
+ if 0 <= sepSetting[0]:
+ img = font.render(lettre + str(round(sepSetting[1], 1)), True, color)
+ window.blit(img, coords)
+ coords[1] += 15
+ else:
+ img = font.render(lettre, True, (170, 170, 255))
+ window.blit(img, coords)
+
def draw(self, win, zoom, scroll, vecteurs, vecteurSetting, points):
# updates
@@ -133,34 +185,39 @@ def draw(self, win, zoom, scroll, vecteurs, vecteurSetting, points):
width = 1
# Dessin
- if self.visible:
- if self.papa.warning:
- color = (255, 120, 60)
- elif self.locWarning:
- color = (100, 200, 100)
- elif self.etiquetteExtended:
- color = (30, 144, 255)
- width = 3
- else:
- color = (255, 255, 255)
-
- pygame.draw.circle(win, color, (self.affX + plotSize, self.affY + plotSize), plotSize, 1)
- pygame.draw.circle(win, color, (self.affX + plotSize, self.affY + plotSize), plotSize / 1.5, 1)
-
- if vecteurs or self.papa.warning or self.locWarning: # si on doit dessiner les vecteurs
- self.drawVector(color, win, vecteurSetting, zoom) # on appelle la fonction
-
- radius = 1
- for plot in self.papa.comete:
- affPlot = [plot[0] * zoom + scroll[0],
- plot[1] * zoom + scroll[1]]
- pygame.draw.circle(win, (255, 255, 255), affPlot, int(round(radius)), 1)
- radius += 0.3
-
- point = self.findEtiquetteAnchorPoint()
- if point is not None:
- pygame.draw.line(win, color, (self.affX + plotSize, self.affY + plotSize),
- (point[0], point[1]), width)
+ if not self.visible:
+ return None
+
+ if self.papa.warning:
+ color = (255, 120, 60)
+ elif self.locWarning:
+ color = (100, 200, 100)
+ elif self.etiquetteExtended:
+ color = (30, 144, 255)
+ width = 3
+ else:
+ color = (255, 255, 255)
+
+ pygame.draw.circle(win, color, (self.affX + plotSize, self.affY + plotSize), plotSize, 1)
+ pygame.draw.circle(win, color, (self.affX + plotSize, self.affY + plotSize), plotSize / 1.5, 1)
+
+ if self.sep:
+ self.drawSep(win, zoom)
+
+ elif vecteurs or self.papa.warning or self.locWarning: # si on doit dessiner les vecteurs
+ self.drawVector(color, win, vecteurSetting, zoom) # on appelle la fonction
+
+ radius = 1
+ for plot in self.papa.comete:
+ affPlot = [plot[0] * zoom + scroll[0],
+ plot[1] * zoom + scroll[1]]
+ pygame.draw.circle(win, (255, 255, 255), affPlot, int(round(radius)), 1)
+ radius += 0.3
+
+ point = self.findEtiquetteAnchorPoint()
+ if point is not None:
+ pygame.draw.line(win, color, (self.affX + plotSize, self.affY + plotSize),
+ (point[0], point[1]), width)
def findEtiquetteAnchorPoint(self) -> tuple[float, float]:
"""
@@ -176,18 +233,18 @@ def findEtiquetteAnchorPoint(self) -> tuple[float, float]:
pointGauche = geometry.calculateIntersection((rect[0], rect[1]),
(rect[0], rect[1] + rect[3]),
- (self.etiquette.centre[0], self.etiquette.centre[1]),
- (self.affX + plotSize, self.affY + plotSize))
+ (self.etiquette.centre[0], self.etiquette.centre[1]),
+ (self.affX + plotSize, self.affY + plotSize))
pointDroite = geometry.calculateIntersection((rect[0] + rect[2], rect[1]),
(rect[0] + rect[2], rect[1] + rect[3]),
- (self.etiquette.centre[0], self.etiquette.centre[1]),
- (self.affX + plotSize, self.affY + plotSize))
+ (self.etiquette.centre[0], self.etiquette.centre[1]),
+ (self.affX + plotSize, self.affY + plotSize))
pointBas = geometry.calculateIntersection((rect[0], rect[1] + rect[3]),
(rect[0] + rect[2], rect[1] + rect[3]),
- (self.etiquette.centre[0], self.etiquette.centre[1]),
- (self.affX + plotSize, self.affY + plotSize))
+ (self.etiquette.centre[0], self.etiquette.centre[1]),
+ (self.affX + plotSize, self.affY + plotSize))
if rect[0] <= pointHaut[0] <= rect[0] + rect[2] and self.affY <= self.etiquetteY:
return pointHaut
@@ -212,25 +269,29 @@ def drawDirect(self, points, point, win, zoom: float, scroll: list[float, float]
coord = [points[point][0] * zoom + scroll[0], points[point][1] * zoom + scroll[1]]
pygame.draw.line(win, (0, 206, 209), (self.affX + plotSize, self.affY + plotSize), coord)
- def drawEstimatedRoute(self, points, temps, win, zoom, scroll):
+ def drawEstimatedRoute(self, points, temps, color, win, zoom, scroll):
"""
Dessine la route future de l'avion jusuq'à un certain point défini par une valeur de temps
C'est une bonne approximation de la future position de l'avion, à vitesse constante
:param points: la liste des points récupérer les coords
- :param temps: combien de temps doit faire la route dessinée
+ :param temps: combien de temps doit faire la route dessinée en sec
+ :param color: La couleur du tracé
:param win: l'écran pygame
:param zoom: le niveau de zoom
:param scroll: le scroll format [x, y]
:return:
"""
+ if not self.conflitSelected:
+ return None
+
route = self.papa.route['points'] # on n'a besoin que des noms des points
nextPoint = self.papa.nextPoint
ratio = 0
route = route[route.index(nextPoint):] # on ne considère que la route devant l'avion
pointUn = [self.papa.x, self.papa.y] # on commence à dessiner à partir de l'avion
- distance = temps * self.papa.speedPx # on établit la distance de la route avec notre vitesse
+ distance = temps * self.papa.speedPx / radarRefresh # on établit la distance de la route avec notre vitesse
for point in route:
pointDeux = [points[point['name']][0], points[point['name']][1]]
@@ -246,18 +307,18 @@ def drawEstimatedRoute(self, points, temps, win, zoom, scroll):
pointUn[1] + (pointDeux[1] - pointUn[1]) * ratio]
# on dessine alors la dernière branche
- pygame.draw.line(win, (0, 255, 0),
+ pygame.draw.line(win, color,
(pointUn[0] * zoom + scroll[0], pointUn[1] * zoom + scroll[1]),
- (pointDeux[0] * zoom + scroll[0], pointDeux[1] * zoom + scroll[1]))
+ (pointDeux[0] * zoom + scroll[0], pointDeux[1] * zoom + scroll[1]), 2)
self.predictionPoint = pointDeux
break # on casse la boucle for, pas la peine de faire des calculs pour plus loin, la prédi est finie
else: # si le trajet s'arrête après la branche, on dessine la branche en entier
- pygame.draw.line(win, (0, 255, 0),
+ pygame.draw.line(win, color,
(pointUn[0] * zoom + scroll[0], pointUn[1] * zoom + scroll[1]),
- (pointDeux[0] * zoom + scroll[0], pointDeux[1] * zoom + scroll[1]))
+ (pointDeux[0] * zoom + scroll[0], pointDeux[1] * zoom + scroll[1]), 2)
distance -= legDistance # on enlève la distance de la branche parcourue à la distance à parcourir
pointUn = pointDeux # on passe au prochain point
@@ -271,10 +332,9 @@ def drawRoute(self, points, win, zoom, scroll):
:param scroll: le scroll format [x, y]
:return:
"""
- # TODO route qui se dessine correctement même quand on est en direct
route = self.papa.route['points'] # on n'a besoin que des noms des points
- nextPoint = self.papa.nextPoint
+ nextPoint = geometry.findClosestSegment(route, (self.papa.x, self.papa.y), points)[1]
point1 = points[route[route.index(nextPoint) - 1]['name']][:2]
point2 = points[nextPoint['name']][:2]
@@ -285,16 +345,30 @@ def drawRoute(self, points, win, zoom, scroll):
for point in route:
pointDeux = [points[point['name']][0], points[point['name']][1]]
- pygame.draw.line(win, (25, 25, 170),
+ pygame.draw.line(win, (46, 80, 174),
(pointUn[0] * zoom + scroll[0], pointUn[1] * zoom + scroll[1]),
- (pointDeux[0] * zoom + scroll[0], pointDeux[1] * zoom + scroll[1]), 2)
+ (pointDeux[0] * zoom + scroll[0], pointDeux[1] * zoom + scroll[1]), 3)
pointUn = pointDeux # on passe au prochain point
- def checkEvent(self, event, pilote):
+ def checkClicked(self, event) -> bool:
+ """
+ Renvoies True si un des boutons (avion ou etiquette) correspond à cet event
+ :param event:
+ :return:
+ """
+ if event.ui_element == self.bouton:
+ return True
+ if event.ui_element.ui_container == self.etiquette.container:
+ return True
+ else:
+ return False
+
+ def checkEvent(self, event, pilote, conflitBool):
"""
Vérifie si un bouton associé à l'avion correspond à l'event
+ :param conflitBool: Si ou non, on est en mode création de conflits
:param event: événement à vérifier
:param pilote: si l'interface est en mode pilote ou non
:return:
@@ -302,10 +376,16 @@ def checkEvent(self, event, pilote):
# on vérifie que le bouton est bien associé à l'etiquette
if event.ui_element.ui_container == self.etiquette.container:
+
self.startPressTime = 0
if self.drag:
self.drag = False
return None # comme on était en train de drag, on ne fait aucune action liée au bouton
+
+ if conflitBool:
+ self.conflitSelected = not self.conflitSelected
+ return None
+
if event.mouse_button == 2 and not pilote and event.ui_element is not self.etiquette.DCT:
# si c'est un clic milieu, alors on surligne ou non le bouton
@@ -322,6 +402,11 @@ def checkEvent(self, event, pilote):
return self.Id, 'HighlightBouton', (indexLigne, index)
if event.ui_element == self.bouton:
+
+ if conflitBool:
+ self.conflitSelected = not self.conflitSelected
+ return None
+
if event.mouse_button == 2 and not pilote: # clic milieu
return self.Id, 'Warning'
diff --git a/fonts/LTSuperior-Black.otf b/Python/ressources/fonts/LTSuperior-Black.otf
similarity index 100%
rename from fonts/LTSuperior-Black.otf
rename to Python/ressources/fonts/LTSuperior-Black.otf
diff --git a/fonts/LTSuperior-Medium.otf b/Python/ressources/fonts/LTSuperior-Medium.otf
similarity index 100%
rename from fonts/LTSuperior-Medium.otf
rename to Python/ressources/fonts/LTSuperior-Medium.otf
diff --git a/fonts/LTSuperior-SemiBold.otf b/Python/ressources/fonts/LTSuperior-SemiBold.otf
similarity index 100%
rename from fonts/LTSuperior-SemiBold.otf
rename to Python/ressources/fonts/LTSuperior-SemiBold.otf
diff --git a/theme.json b/Python/ressources/theme.json
similarity index 68%
rename from theme.json
rename to Python/ressources/theme.json
index 0040240..8a05e3e 100644
--- a/theme.json
+++ b/Python/ressources/theme.json
@@ -24,8 +24,8 @@
{
"name": "regular",
"size": "12",
- "regular_path": "fonts/LTSuperior-SemiBold.otf",
- "bold_path": "fonts/LTSuperior-Black.otf"
+ "regular_path": "ressources/fonts/LTSuperior-SemiBold.otf",
+ "bold_path": "ressources/fonts/LTSuperior-Black.otf"
}
},
"@menu":
@@ -38,8 +38,8 @@
"font": {
"name": "bold",
"size": "12",
- "regular_path": "fonts/LTSuperior-SemiBold.otf",
- "bold_path": "fonts/LTSuperior-Black.otf"
+ "regular_path": "ressources/fonts/LTSuperior-SemiBold.otf",
+ "bold_path": "ressources/fonts/LTSuperior-Black.otf"
},
"colours":
{
@@ -51,6 +51,35 @@
"active_border": "#3E3E3E"
}
},
+ "menuBleu": {
+ "colours":
+ {
+ "normal_border": "#808080",
+ "hovered_border": "#808080",
+ "disabled_border": "#808080",
+ "selected_border": "#808080",
+ "active_border": "#808080"
+ }
+ },
+ "menuBlanc": {
+ "colours":
+ {
+ "normal_bg": "#C6DADA",
+ "hovered_bg": "#A3B6C4",
+ "active_bg": "#C6DADA"
+ }
+ },
+ "@menuLabel":
+ {
+ "misc": {
+ "border_width": "1",
+ "text_horiz_alignment": "left",
+ "text_horiz_alignment_padding": "0",
+ "text_vert_alignment": "left",
+ "text_vert_alignment_padding": "2",
+ "shadow_width": "0"
+ }
+ },
"@flightDataWindow":
{
"misc": {
@@ -64,8 +93,8 @@
"font": {
"name": "bold",
"size": "15",
- "regular_path": "fonts/LTSuperior-SemiBold.otf",
- "bold_path": "fonts/LTSuperior-Black.otf"
+ "regular_path": "ressources/fonts/LTSuperior-SemiBold.otf",
+ "bold_path": "ressources/fonts/LTSuperior-Black.otf"
},
"colours":
{
@@ -81,6 +110,27 @@
"active_bg": "#3E3E3E"
}
},
+ "@menuRadar":
+ {
+ "misc": {
+ "border_width": "2",
+ "shadow_width": "0",
+ "enable_title_bar": "0"
+ },
+ "colours":
+ {
+ "normal_border": "#6B7C91",
+ "hovered_border": "#6B7C91",
+ "disabled_border": "#6B7C91",
+ "selected_border": "#6B7C91",
+ "active_border": "#6B7C91",
+ "dark_bg": "#7F94AD",
+ "normal_text": "#000000",
+ "hovered_text": "#505050",
+ "selected_text": "#A0A0A0",
+ "active_bg":"#5A697A"
+ }
+ },
"@etiquette":
{
"colours":
@@ -103,8 +153,8 @@
{
"name": "bold",
"size": "12",
- "regular_path": "fonts/LTSuperior-SemiBold.otf",
- "bold_path": "fonts/LTSuperior-Black.otf"
+ "regular_path": "ressources/fonts/LTSuperior-SemiBold.otf",
+ "bold_path": "ressources/fonts/LTSuperior-Black.otf"
}
},
"@etiquetteBold":
@@ -127,8 +177,8 @@
"name": "bold",
"size": "12",
"bold": "1",
- "regular_path": "fonts/LTSuperior-SemiBold.otf",
- "bold_path": "fonts/LTSuperior-Black.otf"
+ "regular_path": "ressources/fonts/LTSuperior-SemiBold.otf",
+ "bold_path": "ressources/fonts/LTSuperior-Black.otf"
}
},
"@etiquetteBoldBlue":
@@ -151,8 +201,8 @@
"name": "bold",
"size": "12",
"bold": "1",
- "regular_path": "fonts/LTSuperior-SemiBold.otf",
- "bold_path": "fonts/LTSuperior-Black.otf"
+ "regular_path": "ressources/fonts/LTSuperior-SemiBold.otf",
+ "bold_path": "ressources/fonts/LTSuperior-Black.otf"
}
},
"@etiquetteBlue":
@@ -174,8 +224,8 @@
"font": {
"name": "bold",
"size": "12",
- "regular_path": "fonts/LTSuperior-SemiBold.otf",
- "bold_path": "fonts/LTSuperior-Black.otf"
+ "regular_path": "ressources/fonts/LTSuperior-SemiBold.otf",
+ "bold_path": "ressources/fonts/LTSuperior-Black.otf"
}
},
"rose":
diff --git a/server.py b/Python/server.py
similarity index 84%
rename from server.py
rename to Python/server.py
index 2804886..91cb6f8 100644
--- a/server.py
+++ b/Python/server.py
@@ -1,17 +1,28 @@
+
+# Native imports
import socket
+import math
import sys
+import pickle
from _thread import *
from queue import Queue
-from network import MCAST_GRP, MCAST_PORT, port
-from paquets_avion import *
-import pickle
import xml.etree.ElementTree as ET
from xml.dom import minidom
import time
import struct
import platform
+from pathlib import Path
+
+# Import fichiers
+from Python.network import MCAST_GRP, MCAST_PORT, port
+from Python.paquets_avion import *
+
-SIMU = 'simu.xml'
+dossierXML = Path("").absolute() / 'XML'
+
+carte = 'carteSecteur.xml'
+aircraftFile = 'aircraft.xml'
+simu = 'simu.xml'
mode_ecriture = True
# On se connecte à internet pour avoir notre adresse IP locale... Oui oui
@@ -50,11 +61,11 @@
dictAvion = {} # dict contenant tous les avions
requests = [] # liste des requêtes que le serveur doit gérer
segments = {}
-gameMap = {'points': {}, 'secteurs': [], 'segments': [], 'routes': {}, 'mapScale': 0}
+gameMap = {'points': {}, 'zones': [], 'segments': [], 'routes': {}, 'mapScale': 0}
# XML map loading
-tree = ET.parse('XML/mapAPS.xml')
+tree = ET.parse(dossierXML / carte)
root = tree.getroot()
mapScale = float(root.find('scale').text) # conversion nm-px
@@ -73,19 +84,32 @@
gameMap['points'].update({name: (x, y, balise)})
-for secteur in root.find('secteurs'):
+for zone in root.find('zones'):
contour = [] # liste des points délimitant le contour du secteur, dans l'ordre de lecture xml
- for limite in secteur.findall('limite'):
+ for limite in zone.findall('limite'):
x = int(limite.find('x').text)
y = int(limite.find('y').text)
contour.append((x, y))
- gameMap['secteurs'].append({'couleur': [int(x) for x in secteur.attrib['color'].split(',')], 'contour': contour})
+ gameMap['zones'].append({'couleur': [int(x) for x in zone.attrib['color'].split(',')], 'contour': contour})
-listeAeroports = {}
+dictSecteurs = {}
+for secteur in root.find('secteurs'):
+ nom = secteur.attrib['name']
+ frequence = secteur.find('frequence').text
+ etranger = False
+ if secteur.find('etranger') is not None:
+ if secteur.find('etranger').text == 'True':
+ etranger = True
+
+ dictSecteurs.update({nom: {'frequence': frequence, 'etranger': etranger}})
+
+gameMap.update({'secteurs': dictSecteurs})
+
+listeAeroports = {}
for direction in root.find('Aeroports'):
liste_de_cette_direction = []
@@ -156,8 +180,9 @@
y1 = y2
provenance = 'N'
destination = 'S'
-
- if route.find('provenance') is not None:
+ if routeType == 'DEPART':
+ provenance = route.find('AD').text
+ elif route.find('provenance') is not None:
provenance = route.find('provenance').text
else:
print('[Problème] pas de direction de provenance pour la route', nomRoute)
@@ -170,7 +195,7 @@
destination = 'S'
if route.find('arrival') is not None:
- arrival = {'XFL': int(route.find('arrival').text), 'secteur': route.find('arrival').attrib['secteur']}
+ arrival = {'XFL': int(route.find('arrival').text), 'secteur': route.find('arrival').attrib['secteur'], 'aeroport': route.find('arrival').attrib['AD']}
for sortie in route.findall('sortie'):
listeSortie.append({'name': sortie.text, 'min': int(sortie.attrib['min']), 'max': int(sortie.attrib['max'])})
gameMap['routes'].update({nomRoute: {'name': nomRoute,
@@ -186,7 +211,7 @@
gameMap.update({'mapScale': mapScale})
aircraftType = {}
-tree = ET.parse('XML/aircrafts.xml')
+tree = ET.parse(dossierXML / aircraftFile)
root = tree.getroot()
for aircraft in root:
@@ -196,9 +221,10 @@
'ROC': int(aircraft.find('ROC').text),
'ROD': int(aircraft.find('ROD').text)}})
-
+planeId = 0
try: # on essaye de charger une simu, si elle existe
- tree = ET.parse('XML/' + SIMU)
+
+ tree = ET.parse(dossierXML / simu)
heure = tree.find('heure').text
heure = int(heure[0:2]) * 3600 + int(heure[2:]) * 60
@@ -218,7 +244,28 @@
heureSpawn = avion.attrib['heure']
heureSpawn = int(heureSpawn[0:2]) * 3600 + int(heureSpawn[2:]) * 60
- avionSpawnListe.append((heureSpawn, avionDict))
+ for route in gameMap['routes']:
+ if route == avionDict['route']:
+ spawnRoute = gameMap['routes'][route]
+ break
+ if 'altitude' in avionDict:
+ spawnFL = round(avionDict['altitude'] / 100)
+ else:
+ spawnFL = None
+ avionPack = AvionPacket(
+ gameMap,
+ planeId,
+ avionDict['indicatif'],
+ avionDict['aircraft'],
+ aircraftType[avionDict['aircraft']],
+ spawnRoute,
+ avionDict['arrival'] == 'True',
+ FL=spawnFL,
+ x=avionDict['x'],
+ y=avionDict['y'])
+ planeId += 1
+
+ avionSpawnListe.append((heureSpawn, avionPack))
except: # sinon, on demande juste l'heure de début
heure = input('Heure de début de simu, format: hhmm')
@@ -294,11 +341,9 @@ def threaded_client(conn, caca):
conn.sendall(pickle.dumps(reply))
nombre = 0
- except:
- if nombre >= 200:
- break
- else:
- nombre += 1
+
+ except error:
+ print(error)
print("Lost connection")
conn.close()
@@ -327,7 +372,6 @@ def threaded_ping_responder():
start_new_thread(threaded_ping_responder, ())
temps = time.time()
STCAtriggered = False
-planeId = 0
accelerationTemporelle = 1
Running = True
while Running:
@@ -357,6 +401,28 @@ def threaded_ping_responder():
xEcriture=req[2].x,
yEcriture=req[2].y,
PFLEcriture=req[2].PFL)
+ elif req[1] == 'DelayedAdd':
+
+ avionSpawnListe.append((game.heure + req[2][0], req[2][1]))
+
+ if mode_ecriture:
+
+ heures = str(round(game.heure + req[2][0] // 3600))
+ if len(heures) == 1:
+ heures = '0' + heures
+ minutes = str(round(game.heure + req[2][0] % 3600 // 60))
+ if len(minutes) == 1:
+ minutes = '0' + minutes
+
+ generateAvionXML(avionsXML,
+ heures + minutes,
+ req[2][1].indicatif,
+ req[2][1].aircraft,
+ req[2][1].route['name'],
+ req[2][1].altitude,
+ xEcriture=req[2][1].x,
+ yEcriture=req[2][1].y,
+ PFLEcriture=req[2][1].PFL)
elif req[1] == 'Remove':
dictAvion.pop(req[0])
@@ -364,8 +430,7 @@ def threaded_ping_responder():
dictAvion[req[0]].selectedAlti = req[2]
elif req[1] == 'PFL':
dictAvion[req[0]].PFL = req[2]
- dictAvion[req[0]].changeXFL()
- dictAvion[req[0]].changeSortieSecteur()
+ dictAvion[req[0]].changeXFL(gameMap)
elif req[1] == 'CFL':
dictAvion[req[0]].CFL = req[2]
elif req[1] == 'C_IAS':
@@ -380,7 +445,7 @@ def threaded_ping_responder():
dictAvion[req[0]].clearedRate = None
elif req[1] == 'XFL':
dictAvion[req[0]].XFL = req[2]
- dictAvion[req[0]].changeSortieSecteur()
+ dictAvion[req[0]].changeSortieSecteur(gameMap)
elif req[1] == 'XPT':
dictAvion[req[0]].XPT = req[2]
elif req[1] == 'HDG':
@@ -432,25 +497,8 @@ def threaded_ping_responder():
toBeRemovedFromSpawn = []
for spawn in avionSpawnListe:
if spawn[0] <= game.heure:
- for route in gameMap['routes']:
- if route == spawn[1]['route']:
- spawnRoute = gameMap['routes'][route]
- break
- if 'altitude' in spawn[1]:
- spawnFL = round(spawn[1]['altitude']/100)
- else:
- spawnFL = None
- dictAvion.update({planeId: AvionPacket(
- gameMap,
- planeId,
- spawn[1]['indicatif'],
- spawn[1]['aircraft'],
- aircraftType[spawn[1]['aircraft']],
- spawnRoute,
- spawn[1]['arrival'],
- FL=spawnFL)})
toBeRemovedFromSpawn.append(spawn)
- planeId += 1
+ dictAvion.update({spawn[1].Id: spawn[1]})
for avion in toBeRemovedFromSpawn:
avionSpawnListe.remove(avion)
@@ -467,6 +515,7 @@ def threaded_ping_responder():
dictAvion.pop(avion)
for avion in list(dictAvion.values()):
# tous les calculs de distances sont ici effectués en pixel, la conversion se fait avec le mapScale
+ # TODO remplacer ce STCA avec le nouveau dans server_def
STCAtriggered = False
predictedPos = []
VspeedOne = avion.evolution
diff --git a/server_browser.py b/Python/server_browser.py
similarity index 91%
rename from server_browser.py
rename to Python/server_browser.py
index 0ebc7fb..6dd4201 100644
--- a/server_browser.py
+++ b/Python/server_browser.py
@@ -1,6 +1,9 @@
+
+# Native Imports
import socket
-from network import MCAST_GRP, MCAST_PORT
+# Imports fichiers
+from Python.network import MCAST_GRP, MCAST_PORT
def serverBrowser():
"""Lan scan et server browser en une fonction
diff --git a/Python/server_def.py b/Python/server_def.py
new file mode 100644
index 0000000..88c01fb
--- /dev/null
+++ b/Python/server_def.py
@@ -0,0 +1,41 @@
+
+# Native imports
+import math
+
+# Imports fichiers
+import Python.geometry as geometry
+from Python.valeurs_config import *
+
+
+def STCA(avion1, avion2, carte) -> bool:
+ """
+ Calcule si on doit mettre le SCTA sur deux avions
+ :param avion1: l'avion paquet
+ :param avion2:
+ :param carte: la carte de la map
+ :return:
+ """
+
+ temps = geometry.distanceMinie((avion1.x, avion1.y), avion1.speedPx / radarRefresh, avion1.headingRad,
+ (avion2.x, avion2.y), avion2.speedPx / radarRefresh, avion2.headingRad)
+
+ if (math.sqrt(
+ ((avion1.x + avion1.speedPx / radarRefresh * temps * math.cos(avion1.headingRad)) -
+ (avion2.x + avion2.speedPx / radarRefresh * temps * math.cos(avion2.headingRad))) ** 2 +
+ ((avion1.y + avion1.speedPx / radarRefresh * temps * math.sin(avion1.headingRad)) -
+ (avion2.y + avion2.speedPx / radarRefresh * temps * math.sin(avion2.headingRad))) ** 2
+ ) <= 5 / carte['mapScale']): # si la distance est infèrieur à 5 nm
+
+ if avion1.evolution == avion2.evolution == 0 and avion2.altitude == avion1.altitude:
+ return True
+
+ elif avion1.evolution == avion2.evolution == 0:
+ return False
+
+ elif abs(avion2.altitude + avion2.evolution / radarRefresh * temps
+ - (avion1.altitude + avion1.evolution / radarRefresh * temps)) <= 2000:
+ return True
+
+ return False
+
+
diff --git a/valeurs_config.py b/Python/valeurs_config.py
similarity index 81%
rename from valeurs_config.py
rename to Python/valeurs_config.py
index fd9ed3e..b6a6cf1 100644
--- a/valeurs_config.py
+++ b/Python/valeurs_config.py
@@ -6,9 +6,8 @@
acceldefault = 3 # accélération/decelération de kt par refresh
turnRateDefault = 10 # turnrate/refresh par défault
liste_etat_freq = ['previousFreq', 'previousShoot', 'inFreq', 'nextCoord', 'nextShoot', 'nextFreq']
-secteurBoundaries = [245, 365] # niveau planché et plafond du secteur
+valeurCoord = 8 # combien de minute avant la sortie la coord passe
plotSize = 5 # taille des plots avions
-listeEtrangers = ['G2', 'M2'] # liste des secteurs ajdacents non interopérables
dragDelay = 150 # on utilise cette valeur seuil pour déterminer si on doit cliquer sur un bouton ou drag l'etiquette
offsettEtiquetteDefault = 30 # de combien les etiquettes sont décalées en px à quand on les dessine la 1ere fois
-temps_disparition_menus = 600 # en combien de milli sec les menus disparaissent après ne plus être survolé
+temps_disparition_menus = 300 # en combien de milli sec les menus disparaissent après ne plus être survolé
diff --git a/requirements.txt b/requirements.txt
index ae1e042..fb2aa95 100644
--- a/requirements.txt
+++ b/requirements.txt
@@ -1,4 +1,5 @@
-numpy
+numpy~=2.1.1
pygame-ce
-pygame_gui
-python-i18n
\ No newline at end of file
+pygame_gui~=0.6.12
+python-i18n
+scipy~=1.14.1
\ No newline at end of file