diff --git a/assets/image/scroller/background.png b/assets/image/scroller/background.png index aaf01ca..b000156 100644 Binary files a/assets/image/scroller/background.png and b/assets/image/scroller/background.png differ diff --git a/assets/image/scroller/cursor.png b/assets/image/scroller/cursor.png index e88fe4e..772485f 100644 Binary files a/assets/image/scroller/cursor.png and b/assets/image/scroller/cursor.png differ diff --git a/assets/sound/README.md b/assets/sound/README.md new file mode 100644 index 0000000..03e6cd9 --- /dev/null +++ b/assets/sound/README.md @@ -0,0 +1,12 @@ +Les effets sonores suivants ont été téléchargé sur le site freesound.org. + +Source individuelles : + - placed.wav : https://freesound.org/people/Kayyy/sounds/61015/ + - touched.wav : https://freesound.org/people/derplayer/sounds/587196/ + - missed.wav : https://freesound.org/people/ComputerHotline/sounds/407060/ + - sunken.wav : https://freesound.org/people/Iwiploppenisse/sounds/156031/ + - victory.wav : https://freesound.org/people/Sheyvan/sounds/470083/ + - defeat.wav : https://freesound.org/people/Unlistenable/sounds/391536/ + + - menu.wav : https://freesound.org/people/levelclearer/sounds/424271/ + - sea.wav : https://freesound.org/people/Pfannkuchn/sounds/360630/ diff --git a/assets/sound/ambient/menu.wav b/assets/sound/ambient/menu.wav new file mode 100644 index 0000000..bc0485b Binary files /dev/null and b/assets/sound/ambient/menu.wav differ diff --git a/assets/sound/ambient/sea.wav b/assets/sound/ambient/sea.wav new file mode 100644 index 0000000..817fb2f Binary files /dev/null and b/assets/sound/ambient/sea.wav differ diff --git a/assets/sound/effect/defeat.wav b/assets/sound/effect/defeat.wav new file mode 100644 index 0000000..fd1400a Binary files /dev/null and b/assets/sound/effect/defeat.wav differ diff --git a/assets/sound/effect/missed.wav b/assets/sound/effect/missed.wav new file mode 100644 index 0000000..a45d343 Binary files /dev/null and b/assets/sound/effect/missed.wav differ diff --git a/assets/sound/effect/placed.wav b/assets/sound/effect/placed.wav new file mode 100644 index 0000000..788ad7a Binary files /dev/null and b/assets/sound/effect/placed.wav differ diff --git a/assets/sound/effect/sunken.wav b/assets/sound/effect/sunken.wav new file mode 100644 index 0000000..e5793c2 Binary files /dev/null and b/assets/sound/effect/sunken.wav differ diff --git a/assets/sound/effect/touched.wav b/assets/sound/effect/touched.wav new file mode 100644 index 0000000..8dc29e2 Binary files /dev/null and b/assets/sound/effect/touched.wav differ diff --git a/assets/sound/effect/victory.wav b/assets/sound/effect/victory.wav new file mode 100644 index 0000000..cd0a502 Binary files /dev/null and b/assets/sound/effect/victory.wav differ diff --git a/assets/sound/game/loose.wav b/assets/sound/game/loose.wav deleted file mode 100644 index 865b51e..0000000 Binary files a/assets/sound/game/loose.wav and /dev/null differ diff --git a/assets/sound/game/missed.wav b/assets/sound/game/missed.wav deleted file mode 100644 index 7c84573..0000000 Binary files a/assets/sound/game/missed.wav and /dev/null differ diff --git a/assets/sound/game/sunken_ally.wav b/assets/sound/game/sunken_ally.wav deleted file mode 100644 index 9662cca..0000000 Binary files a/assets/sound/game/sunken_ally.wav and /dev/null differ diff --git a/assets/sound/game/sunken_enemy.wav b/assets/sound/game/sunken_enemy.wav deleted file mode 100644 index cd17f44..0000000 Binary files a/assets/sound/game/sunken_enemy.wav and /dev/null differ diff --git a/assets/sound/game/touched.wav b/assets/sound/game/touched.wav deleted file mode 100644 index 2429a0b..0000000 Binary files a/assets/sound/game/touched.wav and /dev/null differ diff --git a/assets/sound/game/won.wav b/assets/sound/game/won.wav deleted file mode 100644 index 724c829..0000000 Binary files a/assets/sound/game/won.wav and /dev/null differ diff --git a/source/gui/media/Game.py b/source/gui/media/Game.py deleted file mode 100644 index 28932c5..0000000 --- a/source/gui/media/Game.py +++ /dev/null @@ -1,15 +0,0 @@ -from .type import Sound -from .abc import MediaGroup -from source.path import path_sound - -path = path_sound / "game" - - -class Game(MediaGroup): - 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/media/SoundAmbient.py b/source/gui/media/SoundAmbient.py new file mode 100644 index 0000000..9b2dd76 --- /dev/null +++ b/source/gui/media/SoundAmbient.py @@ -0,0 +1,10 @@ +from .abc import MediaGroup +from source.path import path_sound +from .type import Sound + +path = path_sound / "ambient" + + +class SoundAmbient(MediaGroup): + menu = Sound(path / "menu.wav") + sea = Sound(path / "sea.wav") diff --git a/source/gui/media/SoundEffect.py b/source/gui/media/SoundEffect.py new file mode 100644 index 0000000..744dd49 --- /dev/null +++ b/source/gui/media/SoundEffect.py @@ -0,0 +1,14 @@ +from .type import Sound +from .abc import MediaGroup +from source.path import path_sound + +path = path_sound / "effect" + + +class SoundEffect(MediaGroup): + placed = Sound(path / "placed.wav") + touched = Sound(path / "touched.wav") + missed = Sound(path / "missed.wav") + sunken = Sound(path / "sunken.wav") + victory = Sound(path / "victory.wav") + defeat = Sound(path / "defeat.wav") diff --git a/source/gui/media/__init__.py b/source/gui/media/__init__.py index 340ae36..93a1c80 100644 --- a/source/gui/media/__init__.py +++ b/source/gui/media/__init__.py @@ -1 +1,2 @@ -from .Game import Game +from .SoundEffect import SoundEffect +from .SoundAmbient import SoundAmbient diff --git a/source/gui/media/abc/MediaGroup.py b/source/gui/media/abc/MediaGroup.py index 4af891d..9e3a114 100644 --- a/source/gui/media/abc/MediaGroup.py +++ b/source/gui/media/abc/MediaGroup.py @@ -24,3 +24,7 @@ class MediaGroup(ABC): @classmethod def set_volume(cls, value: float): cls.player.volume = value + + @classmethod + def stop(cls): + cls.player.next_source() diff --git a/source/gui/media/type/abc/Media.py b/source/gui/media/type/abc/Media.py index 4de58d9..115c770 100644 --- a/source/gui/media/type/abc/Media.py +++ b/source/gui/media/type/abc/Media.py @@ -20,13 +20,20 @@ class Media(ABC): cls.loaded_media[path] = media # modifie la fonction pour jouer le son en utilisant le player - def _play(): - owner.player.delete() # arrête la musique en cours s'il y en a une et vide la queue + def _play(loop: bool = False): + owner.player.next_source() # passe à la prochaine musique owner.player.queue(media) # ajoute la musique à la queue owner.player.play() # joue la musique + owner.player.on_eos = (lambda: _play(loop=True)) if loop else (lambda: "pass") media.play = _play + def _play_safe(*args, **kwargs): + if owner.player.source is media: return + media.play(*args, **kwargs) # NOQA + + media.play_safe = _play_safe + return media @abstractmethod diff --git a/source/gui/scene/Game.py b/source/gui/scene/Game.py index c725ef1..a418fda 100644 --- a/source/gui/scene/Game.py +++ b/source/gui/scene/Game.py @@ -37,6 +37,7 @@ class Game(BaseGame): PacketBoatPlaced().send_connection(connection) self.grid_ally.add_listener("on_all_boats_placed", board_ally_ready) + self.grid_ally.add_listener("on_boat_placed", lambda *_: media.SoundEffect.placed.play()) def board_enemy_bomb(widget, cell: Point2D): if not (self.boat_ready_ally and self.boat_ready_enemy): return @@ -121,6 +122,8 @@ class Game(BaseGame): self.save_cooldown: Optional[datetime] = None + media.SoundAmbient.sea.play_safe() + self._refresh_turn_text() self._refresh_score_text() @@ -294,10 +297,10 @@ class Game(BaseGame): # joue la musique associée à ce mouvement match bomb_state: - case BombState.NOTHING: media.Game.touched.play() - case BombState.TOUCHED: media.Game.missed.play() - case BombState.SUNKEN: media.Game.sunken_ally.play() - case BombState.WON: media.Game.loose.play() + case BombState.NOTHING: media.SoundEffect.missed.play() + case BombState.TOUCHED: media.SoundEffect.touched.play() + case BombState.SUNKEN: media.SoundEffect.sunken.play() + case BombState.WON: media.SoundEffect.defeat.play() # envoie le résultat à l'autre joueur PacketBombState(position=packet.position, bomb_state=bomb_state).send_connection(self.connection) @@ -327,10 +330,10 @@ class Game(BaseGame): # joue la musique associée à ce mouvement match packet.bomb_state: - case BombState.NOTHING: media.Game.missed.play() - case BombState.TOUCHED: media.Game.touched.play() - case BombState.SUNKEN: media.Game.sunken_enemy.play() - case BombState.WON: media.Game.won.play() + case BombState.NOTHING: media.SoundEffect.missed.play() + case BombState.TOUCHED: media.SoundEffect.touched.play() + case BombState.SUNKEN: media.SoundEffect.sunken.play() + case BombState.WON: media.SoundEffect.victory.play() if packet.bomb_state is BombState.WON: # si cette bombe a touché le dernier bateau, alors l'on a gagné diff --git a/source/gui/scene/GameError.py b/source/gui/scene/GameError.py index c9d68ef..c4a28d0 100644 --- a/source/gui/scene/GameError.py +++ b/source/gui/scene/GameError.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from source.gui import widget, texture +from source.gui import widget, texture, media from source.gui.position import vw_full, vw_center, vh_center, right, px, vw, vh, vh_full from source.gui.scene.abc import Scene @@ -46,3 +46,5 @@ class GameError(Scene): from source.gui.scene import MainMenu self.back.add_listener("on_click_release", lambda *_: self.window.set_scene(MainMenu)) + + media.SoundAmbient.menu.play_safe(loop=True) diff --git a/source/gui/scene/GameLoad.py b/source/gui/scene/GameLoad.py index 28256e6..4c8ce49 100644 --- a/source/gui/scene/GameLoad.py +++ b/source/gui/scene/GameLoad.py @@ -1,7 +1,7 @@ from pathlib import Path from typing import TYPE_CHECKING -from source.gui import widget, texture +from source.gui import widget, texture, media from source.gui.position import vw_full, vw, vh, right, px, vw_center, vh_center, vh_full from source.gui.scene.abc import Scene from source.network import Host @@ -64,6 +64,8 @@ class GameLoad(Scene): self.accept.add_listener("on_click_release", lambda *_: self.response(value=True)) + media.SoundAmbient.menu.play_safe(loop=True) + def response(self, value: bool): self.thread_host.accept_load = value self.window.remove_scene(self) diff --git a/source/gui/scene/GameWaitLoad.py b/source/gui/scene/GameWaitLoad.py index 2a80e72..e9a51c4 100644 --- a/source/gui/scene/GameWaitLoad.py +++ b/source/gui/scene/GameWaitLoad.py @@ -1,7 +1,7 @@ from pathlib import Path from typing import TYPE_CHECKING -from source.gui import widget, texture +from source.gui import widget, texture, media from source.gui.position import vw_full, vh, vw, vh_full from source.gui.scene.abc import Scene from source.utils import path_ctime_str @@ -37,3 +37,5 @@ class GameWaitLoad(Scene): multiline=True, font_size=28, ) + + media.SoundAmbient.menu.play_safe(loop=True) diff --git a/source/gui/scene/HistoryGame.py b/source/gui/scene/HistoryGame.py index 8cbeb72..b0f66f1 100644 --- a/source/gui/scene/HistoryGame.py +++ b/source/gui/scene/HistoryGame.py @@ -2,7 +2,7 @@ import json from pathlib import Path from typing import TYPE_CHECKING -from source.gui import widget, texture +from source.gui import widget, texture, media from source.gui.position import vw, vh, vw_center from source.gui.scene.abc import BaseGame @@ -68,6 +68,8 @@ class HistoryGame(BaseGame): ) self._refresh_move_text() + media.SoundAmbient.menu.play_safe(loop=True) + def _refresh_move_text(self): self.text_move.text = f"{self.move_number} / {len(self.history)}" self._refresh_score_text() diff --git a/source/gui/scene/HistoryMenu.py b/source/gui/scene/HistoryMenu.py index 03cd6be..28b423b 100644 --- a/source/gui/scene/HistoryMenu.py +++ b/source/gui/scene/HistoryMenu.py @@ -5,7 +5,7 @@ from typing import TYPE_CHECKING from source.gui.position import vw, vh, top, vh_full, vw_full from source.path import path_history -from source.gui import widget, texture +from source.gui import widget, texture, media from source.gui.scene.abc import Scene from source.utils import path_ctime_str @@ -112,3 +112,5 @@ class HistoryMenu(Scene): "on_click_release", lambda *_: self.window.set_scene(self.__class__, page=page+1) ) + + media.SoundAmbient.menu.play_safe(loop=True) diff --git a/source/gui/scene/MainMenu.py b/source/gui/scene/MainMenu.py index 58c01c4..d79809f 100644 --- a/source/gui/scene/MainMenu.py +++ b/source/gui/scene/MainMenu.py @@ -2,7 +2,7 @@ from typing import TYPE_CHECKING from source.gui.position import vw_full, vh_full, vw, vh from source.gui.scene.abc import Scene -from source.gui import widget, scene, texture +from source.gui import widget, scene, texture, media if TYPE_CHECKING: from source.gui.window import Window @@ -79,3 +79,5 @@ class MainMenu(Scene): ) self.settings.add_listener("on_click_release", lambda *_: self.window.add_scene(scene.Settings)) + + media.SoundAmbient.menu.play_safe(loop=True) diff --git a/source/gui/scene/RoomCreate.py b/source/gui/scene/RoomCreate.py index ade9c11..e2a3eb4 100644 --- a/source/gui/scene/RoomCreate.py +++ b/source/gui/scene/RoomCreate.py @@ -1,6 +1,6 @@ from typing import TYPE_CHECKING -from source.gui import widget, texture, regex +from source.gui import widget, texture, regex, media from source.gui.position import vw, vh, right, px, vw_full, vh_full from source.gui.scene import RoomHost from source.gui.scene.abc import Scene @@ -256,6 +256,8 @@ class RoomCreate(Scene): self.start.add_listener("on_click_release", lambda *_: self.confirm()) + media.SoundAmbient.menu.play_safe(loop=True) + def confirm(self): if not self.valid: return # si tous les formulaires ne sont pas correctement remplis, ignore diff --git a/source/gui/scene/RoomHost.py b/source/gui/scene/RoomHost.py index b44e8f9..5a32bf6 100644 --- a/source/gui/scene/RoomHost.py +++ b/source/gui/scene/RoomHost.py @@ -5,7 +5,7 @@ import requests from source import network from source.gui.position import vw, vh, vh_full, vw_full from source.gui.scene.abc import Scene -from source.gui import widget, texture +from source.gui import widget, texture, media from source.utils.thread import in_pyglet_context, StoppableThread if TYPE_CHECKING: @@ -72,6 +72,8 @@ class RoomHost(Scene): self.thread_ip = StoppableThread(target=self._refresh_ip) # NOQA self.thread_ip.start() + media.SoundAmbient.menu.play_safe(loop=True) + def _refresh_ip(self): while True: try: diff --git a/source/gui/scene/RoomJoin.py b/source/gui/scene/RoomJoin.py index 25341a5..de35acc 100644 --- a/source/gui/scene/RoomJoin.py +++ b/source/gui/scene/RoomJoin.py @@ -3,7 +3,7 @@ from typing import TYPE_CHECKING, Optional from source import network from source.gui.position import vw, vh, vw_full, vh_full from source.gui.scene.abc import Scene -from source.gui import widget, texture, regex +from source.gui import widget, texture, regex, media if TYPE_CHECKING: from source.gui.window import Window @@ -92,6 +92,8 @@ class RoomJoin(Scene): self.thread: Optional[network.Client] = None + media.SoundAmbient.menu.play_safe(loop=True) + def button_connect(self, widget, *_): if not self.valid: return # si tous les formulaires ne sont pas correctement remplis, ignore diff --git a/source/gui/scene/Settings.py b/source/gui/scene/Settings.py index 5c1086f..fb9bcca 100644 --- a/source/gui/scene/Settings.py +++ b/source/gui/scene/Settings.py @@ -1,9 +1,6 @@ -import itertools from math import inf from typing import TYPE_CHECKING -import pyglet.app - from source.gui import widget, texture, media from source.gui.position import vw_full, vh_full, vw, vh from source.gui.scene.abc.Popup import Popup @@ -142,7 +139,7 @@ class Settings(Popup): font_size=20, ) - # Volume + # Volume Effet Sonore self.volume_sfx = self.add_widget( widget.Scroller, @@ -151,14 +148,14 @@ class Settings(Popup): style=texture.Scroller.Style1, from_=0, - value=media.Game.get_volume(), + value=media.SoundEffect.get_volume(), to=1, text_transform=lambda value: f"{round(value * 100)}%" ) def change_volume_sfx(widget): - media.Game.set_volume(widget.value) + media.SoundEffect.set_volume(widget.value) self.volume_sfx.add_listener( "on_value_change", @@ -173,3 +170,35 @@ class Settings(Popup): text="Effets Sonore", font_size=20, ) + + # Volume Ambient + + self.volume_ambient = self.add_widget( + widget.Scroller, + + x=5 * vw, y=76 * vh, width=20 * vw, height=10 * vh, + + style=texture.Scroller.Style1, + from_=0, + value=media.SoundAmbient.get_volume(), + to=1, + + text_transform=lambda value: f"{round(value * 100)}%" + ) + + def change_volume_ambient(widget): + media.SoundAmbient.set_volume(widget.value) + + self.volume_ambient.add_listener( + "on_value_change", + change_volume_ambient + ) + + self.add_widget( + widget.Text, + + x=27 * vw, y=79 * vh, + + text="Musique Ambiante", + font_size=20, + ) diff --git a/source/gui/widget/GameGrid.py b/source/gui/widget/GameGrid.py index 71ac309..9d831dc 100644 --- a/source/gui/widget/GameGrid.py +++ b/source/gui/widget/GameGrid.py @@ -238,6 +238,8 @@ class GameGrid(BoxWidget): else: # if the boat have been placed self.boats_length.pop(0) # remove the boat from the list of boat to place + + self.trigger_event("on_boat_placed") if len(self.boats_length) == 0: self.trigger_event("on_all_boats_placed") diff --git a/source/gui/widget/Scroller.py b/source/gui/widget/Scroller.py index 4f3d992..487c6fb 100644 --- a/source/gui/widget/Scroller.py +++ b/source/gui/widget/Scroller.py @@ -4,7 +4,7 @@ from source.gui.better_pyglet import Sprite, Label from source.gui.texture.abc import Style from source.gui.widget.abc import BoxWidget from source.type import Distance -from source.utils import dict_filter_prefix +from source.utils import dict_filter_prefix, in_bbox if TYPE_CHECKING: from source.gui.scene.abc import Scene @@ -126,3 +126,7 @@ class Scroller(BoxWidget): def on_resize(self, width: int, height: int): self._refresh() + + def on_mouse_drag(self, x, y, dx, dy, buttons, modifiers): + if in_bbox((x, y), self.bbox): + self._refresh_cursor(x - self.x)