Skip to content

Commit

Permalink
Merge GUI 3.4.X - Reorganisation
Browse files Browse the repository at this point in the history
  • Loading branch information
Florian-DELRIEU authored Sep 22, 2024
2 parents 5dbeb13 + 6ed06f3 commit 4684965
Show file tree
Hide file tree
Showing 8 changed files with 380 additions and 393 deletions.
451 changes: 88 additions & 363 deletions BattleWindow.py

Large diffs are not rendered by default.

11 changes: 7 additions & 4 deletions Classes/Camp.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,6 @@ def __init__(self):

def __repr__(self):
return "Camp Attacking" if self.is_attacking else "Camp Defending"
############# ROUNDS ####################

def move_in_frontline(self):
# Move divisions in frontline
Expand Down Expand Up @@ -54,17 +53,22 @@ def combat_width_penalty(self):
total_camp_width = sum(division.width for division in self.frontline)
combat_width_malus = 1
stacking_penalty = 1
#
# Penalty for extra width
if total_camp_width >= self.battle_info["Width"]:
combat_width_malus = min(total_camp_width / self.battle_info["Width"],1.33)
#self.combat_width_malus = round(1 - (self.combat_width_malus-1),1)
#self.combat_width_malus = round(1 - (self.combat_width_malus-1),1) #todo uncomment ?
#
# Penlalty for stacking
stacking_limit = 5 + 3 * self.battle_info["extra side"]
if len(self.frontline) > stacking_limit:
stacking_penalty = 2 * (len(self.frontline) - stacking_limit)
#
# Sum and attribute the two penalty
self.combat_penalty = round(1 - (combat_width_malus+stacking_penalty - 2), 1)
for division in self.frontline:
division.combat_width_malus = self.combat_penalty

############# GESTION ####################
def get_data(self):
return {
"divisions": [division.__dict__ for division in self.divisions],
Expand Down Expand Up @@ -98,7 +102,6 @@ def contains_division(self, division):
"""
return any(division_in_camp.id == division.id for division_in_camp in self.divisions)


def get_battle_info(self, Battle):
self.battle_info["Width"] = Battle.combat_width
self.battle_info["terrain"] = Battle.terrain
Expand Down
2 changes: 0 additions & 2 deletions Classes/Division.py
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ def targeting(self, enemy_camp):
def choose_priority_target(self):
"""
Définie la cible prioritaire en fonction des paramètres
:param attacking_division:
:param target_list:
:return:
"""

Expand Down
8 changes: 4 additions & 4 deletions Classes/Terrain.py
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
class Terrain:
def __init__(self, name, attack, width, extra_width, air_support):
#todo add on GUI:
# - river
# - other side
# - Paradrop ?
# - Naval invasion
# + river
# + other side
# o Paradrop ?
# o Naval invasion

self.name = name
self.attack = attack
Expand Down
40 changes: 40 additions & 0 deletions Functions/SavingFunctions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import tkinter as tk
import json
import os
from Classes.Division import Division



def load_divisions(self):
"""
Charge les divisions sauvegardées à partir d'un fichier JSON.
Cette méthode vérifie l'existence du fichier "divisions.json" et tente de charger les données JSON à partir
de ce fichier.
Les données sont ensuite converties en instances de la classe Division.
Retour:
List[Division]: Une liste d'instances de la classe Division représentant les divisions sauvegardées.
"""
if os.path.exists("Saves/divisions.json"):
with open("Saves/divisions.json", "r") as file:
try:
data = json.load(file)
return [Division.load(division) for division in data]
except json.JSONDecodeError:
return []
return []

"""
def save_battle_data(self):
battle_data = {
"weather": self.weather.get(),
"terrain": self.terrain.get(),
"leader_a": self.leader_attacker.get(),
"leader_b": self.leader_defender.get(),
"camp_a": self.camp_attacker.get_data(),
"camp_b": self.camp_defender.get_data(),
"log_text": self.log_text.get("1.0", tk.END).strip()
}
with open("Saves/battle_data.json", "w") as file:
json.dump(battle_data, file, indent=4)
"""
206 changes: 206 additions & 0 deletions Functions/UI_functions.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,206 @@
import tkinter as tk
from tkinter import ttk
from Library.TerrainList import terrain_list
from Library.LeaderList import *


def update_terrain(Battle, event=None):
"""
Recupère l'instance :terrain: a partir du choix fait dans le menu déroulant.
:return:
"""
selected_terrain_name = Battle.selected_terrain.get()
for terrain in terrain_list:
if terrain.name == selected_terrain_name:
Battle.terrain = terrain
break
update_combat_width(Battle)


def update_combat_width(Battle):
"""
Mets à jour l'affichage du combat_width
"""
Battle.combat_width = Battle.terrain.width
try:
Battle.combat_width *= Battle.camp_attacker.tactic.width_bonus * Battle.camp_defender.tactic.width_bonus
except Exception:
pass
Battle.combat_width_display.set(str(Battle.combat_width))


def update_battle_info_display(Battle):
"""
Met à jour l'affichage des tactiques pour chaque camp et la phase de bataille en cours.
"""
Battle.attacker_tactic_label.config(text=f"Tactique Attaquant : {Battle.camp_attacker.tactic.name}")
Battle.defender_tactic_label.config(text=f"Tactique Défenseur : {Battle.camp_defender.tactic.name}")
Battle.battle_phase_label.config(text=f"Phase de Bataille : {Battle.battle_phase}")

def get_division_names(Battle):
"""
Récupère les noms de toutes les divisions sauvegardées.
Cette méthode parcourt la liste des divisions chargées et extrait le nom de chaque division.
Retour:
List[str]: Une liste contenant les noms de toutes les divisions sauvegardées.
"""
return [division.template for division in Battle.divisions]


def add_division(Battle, frame, selected_division_var):
"""
Ajoute une division au camp spécifié et affiche ses statistiques de manière compacte.
Cette méthode recherche la division correspondant au nom sélectionné, crée un cadre pour la division,
et affiche ses statistiques sous forme abrégée en colonnes, avec des barres de progression pour les PV et l'organisation.
Args:
frame (tk.Frame): Le cadre dans lequel ajouter la division (camp A ou camp B).
selected_name (str): Le Nom de Template sélectionnée à ajouter.
"""
# todo Ajouter les division directement dans la réserves via les bouttons
if not (selected_name := selected_division_var.get()):
return
for division in Battle.divisions:
if division.template == selected_name:
camp = None
if frame == Battle.camp_attacker_divisions_frame:
Battle.camp_attacker.add_division(division)
camp = Battle.camp_attacker
if frame == Battle.camp_defender_divisions_frame:
Battle.camp_defender.add_division(division)
camp = Battle.camp_defender
assert camp is not None, "camp is not assigned"
frame_division = tk.Frame(frame, bd=1, relief=tk.SOLID, padx=5, pady=5)
frame_division.pack(fill=tk.X, pady=2)

stats_frame = tk.Frame(frame_division)
stats_frame.pack(fill=tk.X)

display_division_stats(Battle,stats_frame, division, camp)


def open_division_selection(Battle, camp, frame):
selection_window = tk.Toplevel(Battle)
selection_window.title("Sélectionner une Division")
selection_window.geometry("400x300")

listbox = tk.Listbox(selection_window)
listbox.pack(fill=tk.BOTH, expand=True)

for division in Battle.divisions:
listbox.insert(tk.END, division.template)

def on_select():
selected_name = tk.StringVar()
selected_name.set(listbox.get(listbox.curselection()))
Battle.add_division(frame, selected_name)
# selection_window.destroy()

tk.Button(selection_window, text="Ajouter", command=on_select).pack(pady=10)


def select_leader(Battle, camp_type):
leader_window = tk.Toplevel(Battle)
leader_window.title("Sélectionner un Leader")
leader_window.geometry("300x200")

listbox = tk.Listbox(leader_window)
listbox.pack(fill=tk.BOTH, expand=True)

# Remplir la liste avec les noms des leaders disponibles
leaders = Battle.get_leaders()
for leader in leaders:
listbox.insert(tk.END, leader.name) # Assurez-vous que les instances Leader ont un attribut `name`

def on_select():
selected_name = listbox.get(listbox.curselection())
if camp_type == "attacker":
Battle.attacker_leader_label.config(text=f"Leader Attaquant : {selected_name}")
Battle.camp_attacker.add_leader(
next((leader for leader in Battle.get_leaders() if leader.name == selected_name), None))
else:
Battle.defender_leader_label.config(text=f"Leader Défenseur : {selected_name}")
Battle.camp_defender.add_leader(
next((leader for leader in Battle.get_leaders() if leader.name == selected_name), None))
leader_window.destroy()

tk.Button(leader_window, text="Sélectionner", command=on_select).pack(pady=10)

def get_divisions_from_frame(Battle, frame):
"""
Récupère les divisions à partir d'un cadre spécifié.
Cette méthode extrait les noms des divisions à partir des enfants du cadre donné,
puis recherche et retourne les instances de Division correspondantes.
Args:
frame (tk.Frame): Le cadre contenant les divisions.
Retour:
List[Division]: Une liste d'instances de la classe Division correspondant aux noms dans le cadre.
"""
division_names = [child.winfo_children()[0].cget("text").split(":")[1].strip() for child in
frame.winfo_children()]
return [
next(
(division for division in Battle.divisions if division.nom == name),
None,
)
for name in division_names
]

def display_division_stats(Battle ,stats_frame ,division ,camp):
# Déterminer la couleur en fonction de la position de la division
if division in camp.frontline:
bg_color = "lightblue" # Couleur pour les divisions en frontline
elif division in camp.reserves:
bg_color = "lightgray" # Couleur pour les divisions en réserves
else:
bg_color = "white" # Couleur par défaut si la division n'est ni en frontline ni en réserves

# Appliquer la couleur de fond
stats_frame.configure(bg=bg_color)

stats = [division.pv, division.organisation, division.soft_attack, division.hard_attack, division.defense,
division.attaque, division.piercing, division.armor, division.hardness, division.width]
abbr_stats = ["PV", "Org", "SA", "HA", "Def", "Atk", "Prc", "Arm", "Hard", "Wdth"]

for i, stat in enumerate(stats):
row = i // 6
col = i % 6
if stat in [division.pv, division.organisation]:
# FIXME - TRUE lorsque :stat: à la meme valeur que :pv: ou :org:
tk.Label(stats_frame, text=f"{abbr_stats[i]}: {stat}").grid(row=row * 2, column=col)
value = stat
if stat == division.pv: max_value = division._PV
if stat == division.organisation: max_value = division._ORGANISATION
progress = ttk.Progressbar(stats_frame, maximum=max_value, value=value, length=80)
progress.grid(row=row * 2 + 1, column=col)
else:
tk.Label(stats_frame, text=f"{abbr_stats[i]}: {stat}").grid(row=row * 2, column=col)

def refresh_display(Battle):
for frame, current_camp in [(Battle.camp_attacker_divisions_frame, Battle.camp_attacker),
(Battle.camp_defender_divisions_frame, Battle.camp_defender)]:
for widget in frame.winfo_children():
widget.destroy() # Effacer les anciennes stats

for division in current_camp.get_divisions():
division_frame = tk.Frame(frame, bd=1, relief=tk.SOLID, padx=5, pady=5)
division_frame.pack(fill=tk.X, pady=2)
stats_frame = tk.Frame(division_frame)
stats_frame.pack(fill=tk.X)
display_division_stats(Battle,stats_frame, division, current_camp)

def get_leaders(Battle):
"""
Retourne la liste des leaders disponibles pour le camp spécifié.
"""
return [leader_A, leader_B, leader_C, leader_D, no_leader]


def on_river_change(Battle):
Battle.terrain.has_small_river = Battle.small_river_box.get()
Battle.terrain.has_large_river = Battle.large_river_box.get()


def on_encirclement_change(Battle):
Battle.encirclement = Battle.encirclement_box.get()
7 changes: 3 additions & 4 deletions Library/TerrainList.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
from Classes import Terrain
from Classes.Terrain import Terrain
from enum import Enum

Terrain = Terrain.Terrain

class Terrain_name(enumerate):
class Terrain_name(Enum):
Desert = "Desert"
Forest = "Forest"
Hills = "Hills"
Expand Down
48 changes: 32 additions & 16 deletions testing_hall.py
Original file line number Diff line number Diff line change
@@ -1,21 +1,37 @@
import unittest
from Library import LeaderList
from Functions.UI_functions import *
from BattleWindow import BattleWindow
import tkinter as tk

app = BattleWindow()

class add_adivision(unittest.TestCase):
app = BattleWindow()
app.add_division(app.camp_attacker_divisions_frame, tk.StringVar(value="Infanterie 36"))
division_1 = app.camp_attacker.divisions[0]
app.mainloop()
test_case = "Case 1"
if __name__ == "__main__":
if test_case == "Case 1":
app.camp_attacker.add_leader(LeaderList.leader_A)
app.attacker_leader_label.config(text=f"Leader Attaquant : {app.camp_attacker.leader.name}")
app.camp_defender.add_leader(LeaderList.no_leader)
app.defender_leader_label.config(text=f"Leader Defenseur : {app.camp_defender.leader.name}")
add_division(app,app.camp_attacker_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.camp_attacker.get_divisions()[-1].nom = "Div. A1"
add_division(app,app.camp_attacker_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.camp_attacker.get_divisions()[-1].nom = "Div. A2"
add_division(app,app.camp_attacker_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.camp_attacker.get_divisions()[-1].nom = "Div. A3"
add_division(app,app.camp_defender_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.camp_defender.get_divisions()[-1].nom = "Div. B1"
add_division(app,app.camp_defender_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.camp_defender.get_divisions()[-1].nom = "Div. B2"
add_division(app,app.camp_defender_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.camp_defender.get_divisions()[-1].nom = "Div. B3"

class battle_2v2(unittest.TestCase):
app = BattleWindow()
app.add_division(app.camp_attacker_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.add_division(app.camp_attacker_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.add_division(app.camp_defender_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.add_division(app.camp_defender_divisions_frame,tk.StringVar(value="Blindes 36"))
app.mainloop()

if __name__ == '__main__':
unittest.main()
elif test_case == "Case 2":
app.camp_attacker.add_leader(LeaderList.no_leader)
app.attacker_leader_label.config(text=f"Leader Attaquant : {app.camp_attacker.leader.name}")
app.camp_defender.add_leader(LeaderList.no_leader)
app.defender_leader_label.config(text=f"Leader Defenseur : {app.camp_defender.leader.name}")
add_division(app,app.camp_attacker_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.camp_attacker.get_divisions()[-1].nom = "Div. A"
add_division(app,app.camp_defender_divisions_frame,tk.StringVar(value="Infanterie 36"))
app.camp_attacker.get_divisions()[-1].nom = "Div. B"
app.mainloop()

0 comments on commit 4684965

Please sign in to comment.