diff --git a/NOTE.md b/NOTE.md index cdde04a..963291d 100644 --- a/NOTE.md +++ b/NOTE.md @@ -7,9 +7,9 @@ A faire : - Voir si les event listener intégré à pyglet sont plus pratique que l'event propagation - Documenter +- Ajouter plus d'event sur les widgets - Ecran de victoire -- Affichage des coups de l'opposant - Faire marcher le tchat - Sauvegarde / Quitter diff --git a/source/gui/scene/Game.py b/source/gui/scene/Game.py index bc75f36..c50498a 100644 --- a/source/gui/scene/Game.py +++ b/source/gui/scene/Game.py @@ -5,7 +5,6 @@ import pyglet from source.gui.scene.abc import Scene from source.gui import widget, texture -from source.gui.widget.grid import GameGridAlly, GameGridEnemy from source import core from source.network.packet import PacketChat, PacketBombPlaced, PacketBoatPlaced from source.type import Point2D @@ -38,7 +37,7 @@ class Game(Scene): ) self.grid_ally = self.add_widget( - GameGridAlly, + widget.GameGrid, x=75, y=0.25, width=0.35, height=0.5, @@ -63,17 +62,19 @@ class Game(Scene): self.grid_ally.add_listener("on_all_boats_placed", board_ally_ready) self.grid_enemy = self.add_widget( - GameGridEnemy, + widget.GameGrid, x=lambda widget: widget.scene.window.width - 75 - widget.width, y=0.25, width=0.35, height=0.5, grid_style=texture.Grid.Style1, + boat_style=texture.Grid.Boat.Style1, bomb_style=texture.Grid.Bomb.Style1, rows=8, columns=8, background_batch=self.batch_grid_background, line_batch=self.batch_grid_line, cursor_batch=self.batch_grid_cursor, + boat_batch=self.batch_grid_boat, bomb_batch=self.batch_grid_bomb ) diff --git a/source/gui/widget/grid/GameGridAlly.py b/source/gui/widget/GameGrid.py similarity index 63% rename from source/gui/widget/grid/GameGridAlly.py rename to source/gui/widget/GameGrid.py index 7b5fb3f..2d07c36 100644 --- a/source/gui/widget/grid/GameGridAlly.py +++ b/source/gui/widget/GameGrid.py @@ -1,25 +1,25 @@ from copy import copy from typing import TYPE_CHECKING, Type -import pyglet import numpy as np +import pyglet.shapes +from source.core import Board, Boat from source.core.enums import Orientation from source.core.error import InvalidBoatPosition from source.gui.sprite import Sprite from source.gui.texture.abc import Style -from source.gui.widget.grid.abc import GameGrid -from source.core import Board, Boat -from source.type import Point2D, ColorRGB +from source.gui.widget.abc import BoxWidget +from source.type import Distance, ColorRGB, Point2D from source.utils import dict_filter_prefix if TYPE_CHECKING: from source.gui.scene.abc import Scene -class GameGridAlly(GameGrid): +class GameGrid(BoxWidget): """ - A game grid that represent the ally grid. + A widget that represent a game grid. """ def __init__(self, scene: "Scene", @@ -31,15 +31,23 @@ class GameGridAlly(GameGrid): boat_style: Type[Style], bomb_style: Type[Style], - boats_length: list[int], + x: Distance = 0, + y: Distance = 0, + width: Distance = None, + height: Distance = None, + preview_color: ColorRGB = (150, 255, 150), + boats_length: list[int] = None, **kwargs): self.cell_sprites: dict[Point2D, "Sprite"] = {} - super().__init__(scene, rows, columns, grid_style, **kwargs) + super().__init__(scene, x, y, width, height) - self.boats_length = boats_length # the list of the size of the boats to place + self.rows = rows + self.columns = columns + + self.boats_length = [] if boats_length is None else boats_length # the list of the size of the boats to place self.preview_color = preview_color self.board = Board(rows=self.rows, columns=self.columns) @@ -47,19 +55,69 @@ class GameGridAlly(GameGrid): self._boat_kwargs = dict_filter_prefix("boat_", kwargs) self._bomb_kwargs = dict_filter_prefix("bomb_", kwargs) + self.grid_style = grid_style self.boat_style = boat_style self.bomb_style = bomb_style + self.background = Sprite( + img=grid_style.get("background"), + **dict_filter_prefix("background_", kwargs) + ) + + self.lines: list[pyglet.shapes.Line] = [ + pyglet.shapes.Line( + 0, 0, 0, 0, + **dict_filter_prefix("line_", kwargs) + ) + for _ in range((self.columns - 1) + (self.rows - 1)) + ] + + self.cursor = pyglet.shapes.Rectangle( + 0, 0, 0, 0, + color=(0, 0, 0, 100), + **dict_filter_prefix("cursor_", kwargs) + ) + self.add_listener("on_click_release", self.on_click_release) - self.add_listener("on_hover", lambda rel_x, rel_y: self.preview_boat(self.get_cell_from_rel(rel_x, rel_y))) + self.add_listener("on_hover_leave", lambda *_: self.hide_cursor()) + self.add_listener("on_hover", self._refresh_cursor) + + + self._refresh_size() + + def get_cell_from_rel(self, rel_x: int, rel_y: int) -> tuple[int, int]: + """ + Return the cell of the grid from a point relative position + """ + + return ( + int((rel_x-1) / self.cell_width), + int((rel_y-1) / self.cell_height) + ) # refresh def _refresh_size(self): - super()._refresh_size() + self.background.x, self.background.y = self.xy + self.background.width, self.background.height = self.size + + # lines + + for column, line in enumerate(self.lines[:self.columns - 1], start=1): + line.x = self.x + self.cell_width * column + line.x2 = line.x + line.y = self.y + line.y2 = self.y2 + + for row, line in enumerate(self.lines[-self.rows + 1:], start=1): + line.x = self.x + line.x2 = self.x2 + line.y = self.y + self.cell_height * row + line.y2 = line.y + + # sprites for (x, y), sprite in self.cell_sprites.items(): - # calcul des décalages à cause de la rotation qui est faite par rapport à l'origine de l'image offset_x = 0 if sprite.rotation <= 90 else self.cell_width @@ -75,6 +133,20 @@ class GameGridAlly(GameGrid): sprite.width = width sprite.height = height + def _refresh_cursor(self, rel_x: int, rel_y: int): + cell_x, cell_y = self.get_cell_from_rel(rel_x, rel_y) + + self.cursor.x = self.x + cell_x * self.width / self.columns + self.cursor.y = self.y + cell_y * self.height / self.rows + self.cursor.width, self.cursor.height = self.cell_size + + self.preview_boat((cell_x, cell_y)) # display the previsualisation of the boat on this cell + + # function + + def hide_cursor(self): + self.cursor.width, self.cursor.height = 0, 0 + def display_board(self, board: Board, preview: bool = False): self.cell_sprites: dict[Point2D, "Sprite"] = {} @@ -151,7 +223,6 @@ class GameGridAlly(GameGrid): else: self.display_board(preview_board, preview=True) def place_bomb(self, cell: Point2D, touched: bool): - self.cell_sprites[cell] = Sprite( img=self.bomb_style.get("touched" if touched else "missed"), **self._bomb_kwargs @@ -169,6 +240,26 @@ class GameGridAlly(GameGrid): case pyglet.window.mouse.LEFT: self.place_boat(cell) + self.trigger_event("on_request_place_bomb", cell) + + # property + + @property + def cell_width(self) -> float: + return self.width / self.columns + + @property + def cell_height(self) -> float: + return self.height / self.rows + + @property + def cell_size(self) -> tuple[float, float]: + return self.cell_width, self.cell_height + + # event + + def on_resize(self, width: int, height: int): + self._refresh_size() def draw(self): self.background.draw() diff --git a/source/gui/widget/__init__.py b/source/gui/widget/__init__.py index 096854d..4b98369 100644 --- a/source/gui/widget/__init__.py +++ b/source/gui/widget/__init__.py @@ -4,3 +4,4 @@ from .Input import Input from .Image import Image from .Checkbox import Checkbox from .Scroller import Scroller +from .GameGrid import GameGrid diff --git a/source/gui/widget/grid/GameGridEnemy.py b/source/gui/widget/grid/GameGridEnemy.py deleted file mode 100644 index 8882da1..0000000 --- a/source/gui/widget/grid/GameGridEnemy.py +++ /dev/null @@ -1,65 +0,0 @@ -from typing import Type, TYPE_CHECKING - -import pyglet - -from source.gui.texture.abc import Style -from source.gui.widget.grid.abc import GameGrid -from source.gui.sprite import Sprite -from source.type import Point2D -from source.utils import dict_filter_prefix - -if TYPE_CHECKING: - from source.gui.scene.abc import Scene - - -class GameGridEnemy(GameGrid): - """ - A game grid that represent the enemy grid. - """ - - def __init__(self, scene: "Scene", - - rows: int, - columns: int, - - grid_style: Type[Style], - bomb_style: Type[Style], - - **kwargs): - self.cell_sprites: dict[Point2D, "Sprite"] = {} - - super().__init__(scene, rows, columns, grid_style, **kwargs) - - self._bomb_kwargs = dict_filter_prefix("bomb_", kwargs) - self.bomb_style = bomb_style - - self.add_listener("on_click_release", self.on_click_release) - - def _refresh_size(self): - super()._refresh_size() - - for (x, y), sprite in self.cell_sprites.items(): - sprite.x = self.x + (self.cell_width * x) - sprite.y = self.y + (self.cell_height * y) - sprite.width = self.cell_width - sprite.height = self.cell_height - - def place_bomb(self, cell: Point2D, touched: bool): - self.cell_sprites[cell] = Sprite( - img=self.bomb_style.get("touched" if touched else "missed"), - **self._bomb_kwargs - ) - - self._refresh_size() - - def on_click_release(self, rel_x: int, rel_y: int, button: int, modifiers: int): - cell = self.get_cell_from_rel(rel_x, rel_y) - - if button == pyglet.window.mouse.LEFT: - self.trigger_event("on_request_place_bomb", cell) - - def draw(self): - self.background.draw() - for sprite in self.cell_sprites.values(): sprite.draw() - self.cursor.draw() - for line in self.lines: line.draw() \ No newline at end of file diff --git a/source/gui/widget/grid/__init__.py b/source/gui/widget/grid/__init__.py deleted file mode 100644 index ad1f32c..0000000 --- a/source/gui/widget/grid/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -from .GameGridAlly import GameGridAlly -from .GameGridEnemy import GameGridEnemy diff --git a/source/gui/widget/grid/abc/GameGrid.py b/source/gui/widget/grid/abc/GameGrid.py deleted file mode 100644 index 7c802ca..0000000 --- a/source/gui/widget/grid/abc/GameGrid.py +++ /dev/null @@ -1,124 +0,0 @@ -from typing import TYPE_CHECKING, Type - -import pyglet.shapes - -from source.gui.sprite import Sprite -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 - -if TYPE_CHECKING: - from source.gui.scene.abc import Scene - - -class GameGrid(BoxWidget): - """ - A widget that represent a game grid. - """ - - def __init__(self, scene: "Scene", - - rows: int, - columns: int, - - style: Type[Style], - - x: Distance = 0, - y: Distance = 0, - width: Distance = None, - height: Distance = None, - - **kwargs): - super().__init__(scene, x, y, width, height) - - self.rows = rows - self.columns = columns - - self.style = style - - self.background = Sprite( - img=style.get("background"), - **dict_filter_prefix("background_", kwargs) - ) - - self.lines: list[pyglet.shapes.Line] = [ - pyglet.shapes.Line( - 0, 0, 0, 0, - **dict_filter_prefix("line_", kwargs) - ) - for _ in range((self.columns - 1) + (self.rows - 1)) - ] - - self.cursor = pyglet.shapes.Rectangle( - 0, 0, 0, 0, - color=(0, 0, 0, 100), - **dict_filter_prefix("cursor_", kwargs) - ) - - self.add_listener("on_hover_leave", lambda *_: self.hide_cursor()) - self.add_listener("on_hover", self._refresh_cursor) - - self._refresh_size() - - def get_cell_from_rel(self, rel_x: int, rel_y: int) -> tuple[int, int]: - """ - Return the cell of the grid from a point relative position - """ - - return ( - int((rel_x-1) / self.cell_width), - int((rel_y-1) / self.cell_height) - ) - - # refresh - - def _refresh_size(self): - self.background.x, self.background.y = self.xy - self.background.width, self.background.height = self.size - - for column, line in enumerate(self.lines[:self.columns - 1], start=1): - line.x = self.x + self.cell_width * column - line.x2 = line.x - line.y = self.y - line.y2 = self.y2 - - for row, line in enumerate(self.lines[-self.rows + 1:], start=1): - line.x = self.x - line.x2 = self.x2 - line.y = self.y + self.cell_height * row - line.y2 = line.y - - def _refresh_cursor(self, rel_x: int, rel_y: int): - cell_x, cell_y = self.get_cell_from_rel(rel_x, rel_y) - - self.cursor.x = self.x + cell_x * self.width / self.columns - self.cursor.y = self.y + cell_y * self.height / self.rows - self.cursor.width, self.cursor.height = self.cell_size - - def hide_cursor(self): - self.cursor.width, self.cursor.height = 0, 0 - - # property - - @property - def cell_width(self) -> float: - return self.width / self.columns - - @property - def cell_height(self) -> float: - return self.height / self.rows - - @property - def cell_size(self) -> tuple[float, float]: - return self.cell_width, self.cell_height - - # event - - def on_resize(self, width: int, height: int): - self._refresh_size() - - def draw(self): - self.background.draw() - self.cursor.draw() - for line in self.lines: line.draw() diff --git a/source/gui/widget/grid/abc/__init__.py b/source/gui/widget/grid/abc/__init__.py deleted file mode 100644 index bc73df7..0000000 --- a/source/gui/widget/grid/abc/__init__.py +++ /dev/null @@ -1 +0,0 @@ -from .GameGrid import GameGrid