diff --git a/NOTE.md b/NOTE.md index 1ee7307..d27811c 100644 --- a/NOTE.md +++ b/NOTE.md @@ -1,15 +1,19 @@ A faire : -- Sauvegarde / Quitter + + +1. Principal : +- Sauvegarde - Historique / Replay -- Police d'écriture -- Gérer les erreurs (quitter en cours de connexion, ...) - Documenter + + +2. Visuel : - Rendre le texte de status plus visible - - +- Police d'écriture - Changer les images, rajouter les fonds, ... +3. Hypothétique : - Voir si les event listener intégré à pyglet sont plus pratiques que l'event propagation (?) - Faire une scène incluant par défaut les boutons "Retour" (?) diff --git a/assets/sound/game/loose.wav b/assets/sound/game/loose.wav new file mode 100644 index 0000000..865b51e Binary files /dev/null and b/assets/sound/game/loose.wav differ diff --git a/assets/sound/game/missed.wav b/assets/sound/game/missed.wav new file mode 100644 index 0000000..7c84573 Binary files /dev/null and b/assets/sound/game/missed.wav differ diff --git a/assets/sound/game/sunken_ally.wav b/assets/sound/game/sunken_ally.wav new file mode 100644 index 0000000..9662cca Binary files /dev/null and b/assets/sound/game/sunken_ally.wav differ diff --git a/assets/sound/game/sunken_enemy.wav b/assets/sound/game/sunken_enemy.wav new file mode 100644 index 0000000..cd17f44 Binary files /dev/null and b/assets/sound/game/sunken_enemy.wav differ diff --git a/assets/sound/game/touched.wav b/assets/sound/game/touched.wav new file mode 100644 index 0000000..2429a0b Binary files /dev/null and b/assets/sound/game/touched.wav differ diff --git a/assets/sound/game/won.wav b/assets/sound/game/won.wav new file mode 100644 index 0000000..724c829 Binary files /dev/null and b/assets/sound/game/won.wav differ diff --git a/source/gui/scene/Game.py b/source/gui/scene/Game.py index e33b3ec..8145638 100644 --- a/source/gui/scene/Game.py +++ b/source/gui/scene/Game.py @@ -198,7 +198,7 @@ class Game(Scene): self._refresh_turn_text() - # function + # refresh def _refresh_chat_box(self): # supprime des messages jusqu'à ce que la boite soit plus petite que un quart de la fenêtre @@ -217,22 +217,6 @@ class Game(Scene): "L'adversaire place ses bombes..." ) - def quit(self): - PacketQuit().send_connection(self.connection) - - self.thread.stop() - from source.gui.scene import MainMenu - self.window.set_scene(MainMenu) - - def game_end(self, won: bool): - self.window.add_scene(GameResult, game_scene=self, won=won) - - def chat_new_message(self, author: str, content: str): - message: str = f"[{author}] - {content}" - self.chat_log.text += "\n" + message - - self._refresh_chat_box() - # property @property @@ -280,6 +264,18 @@ class Game(Scene): self._boat_ready_enemy = boat_ready_enemy self._refresh_turn_text() + # function + + def game_end(self, won: bool): + self.window.add_scene(GameResult, game_scene=self, won=won) # affiche le résultat + self.thread.stop() # coupe la connexion + + def chat_new_message(self, author: str, content: str): + message: str = f"[{author}] - {content}" + self.chat_log.text += "\n" + message + + self._refresh_chat_box() + # network def network_on_chat(self, packet: PacketChat): @@ -313,7 +309,6 @@ class Game(Scene): if bomb_state is BombState.WON: # si l'ennemie à gagner, alors l'on a perdu self.game_end(won=False) - return True # coupe la connexion def network_on_bomb_state(self, packet: PacketBombState): if packet.bomb_state is BombState.ERROR: @@ -334,10 +329,10 @@ class Game(Scene): if packet.bomb_state is BombState.WON: # si cette bombe a touché le dernier bateau, alors l'on a gagné self.game_end(won=True) - return True # coupe la connexion def network_on_quit(self, packet: PacketQuit): - self.thread.stop() + self.thread.stop() # coupe la connexion + from source.gui.scene import GameError self.window.set_scene(GameError, text="L'adversaire a quitté la partie.") diff --git a/source/gui/scene/GameError.py b/source/gui/scene/GameError.py index 0d38bff..d4a21a2 100644 --- a/source/gui/scene/GameError.py +++ b/source/gui/scene/GameError.py @@ -20,6 +20,8 @@ class GameError(Scene): anchor_x="center", text=text, + align="center", + multiline=True, font_size=28, ) diff --git a/source/gui/scene/GameQuit.py b/source/gui/scene/GameQuit.py index 449fe88..b969854 100644 --- a/source/gui/scene/GameQuit.py +++ b/source/gui/scene/GameQuit.py @@ -2,6 +2,7 @@ from typing import TYPE_CHECKING from source.gui import widget, texture from source.gui.scene.abc.Popup import Popup +from source.network.packet import PacketQuit if TYPE_CHECKING: from source.gui.window import Window @@ -55,4 +56,11 @@ class GameQuit(Popup): style=texture.Button.Style1 ) - self.confirm.add_listener("on_click_release", lambda *_: self.game_scene.quit()) + self.confirm.add_listener("on_click_release", lambda *_: self.quit()) + + def quit(self): + PacketQuit().send_connection(self.game_scene.connection) # envoie le packet + self.game_scene.thread.stop() # coupe le thread & la connexion + + from source.gui.scene import MainMenu + self.window.set_scene(MainMenu) # affiche le menu principal diff --git a/source/gui/scene/GameResult.py b/source/gui/scene/GameResult.py index 35b39b6..889703a 100644 --- a/source/gui/scene/GameResult.py +++ b/source/gui/scene/GameResult.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING import pyglet.clock -from source.gui import texture, widget +from source.gui import texture, widget, sound from source.gui.scene.abc.Popup import Popup if TYPE_CHECKING: @@ -20,9 +20,12 @@ class GameResult(Popup): widget.Image, x=0, y=0, width=1.0, height=1.0, - image=texture.Result.Style1.victory if won else texture.Result.Style1.defeat + image=texture.Result.Style1.get("victory" if won else "defeat") ) + sound.Game.get("won" if won else "loose").play() + # TODO: rendre l'image transparente si possible - pyglet.clock.schedule_once(lambda dt: self.game_scene.quit(), 5.0) + from source.gui.scene import MainMenu + pyglet.clock.schedule_once(lambda dt: self.window.set_scene(MainMenu), 5.0) diff --git a/source/gui/scene/RoomCreate.py b/source/gui/scene/RoomCreate.py index 967a5e5..70997b6 100644 --- a/source/gui/scene/RoomCreate.py +++ b/source/gui/scene/RoomCreate.py @@ -233,8 +233,6 @@ class RoomCreate(Scene): update_boat_size_text() - # TODO: si on diminue la taille de la grille après avoir mis des bateaux de plus longue taille, faire un check - # Démarrer self.start = self.add_widget( diff --git a/source/gui/sound/Game.py b/source/gui/sound/Game.py new file mode 100644 index 0000000..4d0cb26 --- /dev/null +++ b/source/gui/sound/Game.py @@ -0,0 +1,15 @@ +from . import path +from .type import Sound +from .abc import SoundGroup + +path = path / "game" + + +class Game(SoundGroup): + touched = Sound(path / "touched.wav") + sunken_ally = Sound(path / "sunken_ally.wav") + won = Sound(path / "won.wav") + + missed = Sound(path / "missed.wav") + sunken_enemy = Sound(path / "sunken_enemy.wav") + loose = Sound(path / "loose.wav") diff --git a/source/gui/sound/__init__.py b/source/gui/sound/__init__.py new file mode 100644 index 0000000..7d38370 --- /dev/null +++ b/source/gui/sound/__init__.py @@ -0,0 +1,5 @@ +from pathlib import Path + +path: Path = Path("./assets/sound") + +from .Game import Game diff --git a/source/gui/sound/abc/SoundGroup.py b/source/gui/sound/abc/SoundGroup.py new file mode 100644 index 0000000..10809c5 --- /dev/null +++ b/source/gui/sound/abc/SoundGroup.py @@ -0,0 +1,11 @@ +from abc import ABC + + +class SoundGroup(ABC): + """ + This class represent a music group that can be played. + """ + + @classmethod + def get(cls, item, default=None): + return getattr(cls, item, default) diff --git a/source/gui/sound/abc/__init__.py b/source/gui/sound/abc/__init__.py new file mode 100644 index 0000000..4caa4df --- /dev/null +++ b/source/gui/sound/abc/__init__.py @@ -0,0 +1 @@ +from .SoundGroup import SoundGroup diff --git a/source/gui/sound/type/Sound.py b/source/gui/sound/type/Sound.py new file mode 100644 index 0000000..d1b0b80 --- /dev/null +++ b/source/gui/sound/type/Sound.py @@ -0,0 +1,13 @@ +from pathlib import Path + +import pyglet + +from source.gui.sound.type.abc import SoundType + + +class Sound(SoundType): + def __init__(self, path: Path): + self.path = path + + def __get__(self, instance, owner) -> pyglet.media.Source: + return self.get_sound(self.path) diff --git a/source/gui/sound/type/__init__.py b/source/gui/sound/type/__init__.py new file mode 100644 index 0000000..9b9ef55 --- /dev/null +++ b/source/gui/sound/type/__init__.py @@ -0,0 +1 @@ +from .Sound import Sound diff --git a/source/gui/sound/type/abc/SoundType.py b/source/gui/sound/type/abc/SoundType.py new file mode 100644 index 0000000..37bb199 --- /dev/null +++ b/source/gui/sound/type/abc/SoundType.py @@ -0,0 +1,20 @@ +from abc import ABC, abstractmethod +from pathlib import Path + +import pyglet + + +class SoundType(ABC): + loaded_sound: dict[Path, pyglet.media.Source] = {} + + @classmethod + def get_sound(cls, path: Path) -> pyglet.media.Source: + if (sound := cls.loaded_sound.get(path)) is None: + sound = pyglet.media.load(path) + cls.loaded_sound[path] = sound + + return sound + + @abstractmethod + def __get__(self, instance, owner) -> pyglet.image.AbstractImage: + pass diff --git a/source/gui/sound/type/abc/__init__.py b/source/gui/sound/type/abc/__init__.py new file mode 100644 index 0000000..2f75779 --- /dev/null +++ b/source/gui/sound/type/abc/__init__.py @@ -0,0 +1 @@ +from .SoundType import SoundType diff --git a/source/network/game_network.py b/source/network/game_network.py index e879d72..2b03899 100644 --- a/source/network/game_network.py +++ b/source/network/game_network.py @@ -29,15 +29,25 @@ def game_network( packet.PacketQuit: game_scene.network_on_quit, } - while True: - data_type: Type["Packet"] = Packet.type_from_connection(connection) + try: + while True: + data_type: Type["Packet"] = Packet.type_from_connection(connection) + + if data_type is None: + if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter + continue + + data = data_type.from_connection(connection) + + in_pyglet_context( + game_methods[data_type], data # récupère la methode relié ce type de donnée + ) # Appelle la méthode. - if data_type is None: if thread.stopped: return # vérifie si le thread n'est pas censé s'arrêter - continue - data = data_type.from_connection(connection) + except Exception as e: + # TODO: meilleur messages - if in_pyglet_context( - game_methods[data_type], data # récupère la methode relié ce type de donnée - ): return # Appelle la méthode. Si elle renvoie True, arrête le thread + from source.gui.scene import GameError + in_pyglet_context(game_scene.window.set_scene, GameError, text=f"Une erreur est survenu :\n{str(e)}") + print(type(e), e) diff --git a/source/network/packet/abc/Packet.py b/source/network/packet/abc/Packet.py index 4ad7eb0..74beb77 100644 --- a/source/network/packet/abc/Packet.py +++ b/source/network/packet/abc/Packet.py @@ -18,12 +18,12 @@ class Packet(ABC): packet_id: int = 0 def __init_subclass__(cls, **kwargs): - cls.packet_header = Packet.packet_id.to_bytes(1, "big") # give a header to the newly created subclass - Packet.packet_id = Packet.packet_id + 1 # increment by one the packet header for the next subclass + if isabstract(cls): return # si la classe est abstraite, ignore - if not isabstract(cls): - # si la classe n'est pas abstraite, ajoute-la aux types de packet enregistré. - cls.packet_types.add(cls) + cls.packet_header = Packet.packet_id.to_bytes(1, "big") # ajoute un header à la sous-classe + Packet.packet_id = Packet.packet_id + 1 # incrémente l'id pour que le prochain header soit différent + + cls.packet_types.add(cls) # ajoute la sous-classe aux types de packet enregistré. @abstractmethod def to_bytes(self) -> bytes: