Added prompt to save the game
This commit is contained in:
parent
5cbb28df72
commit
312210b323
15 changed files with 438 additions and 17 deletions
227
.save/127.0.0.1.bn-save
Normal file
227
.save/127.0.0.1.bn-save
Normal file
|
@ -0,0 +1,227 @@
|
|||
{
|
||||
"grid_ally": {
|
||||
"columns": 8,
|
||||
"rows": 8,
|
||||
"boats": [
|
||||
[
|
||||
{
|
||||
"length": 5,
|
||||
"orientation": "H"
|
||||
},
|
||||
[
|
||||
0,
|
||||
0
|
||||
]
|
||||
],
|
||||
[
|
||||
{
|
||||
"length": 4,
|
||||
"orientation": "V"
|
||||
},
|
||||
[
|
||||
1,
|
||||
2
|
||||
]
|
||||
],
|
||||
[
|
||||
{
|
||||
"length": 4,
|
||||
"orientation": "H"
|
||||
},
|
||||
[
|
||||
3,
|
||||
4
|
||||
]
|
||||
],
|
||||
[
|
||||
{
|
||||
"length": 3,
|
||||
"orientation": "V"
|
||||
},
|
||||
[
|
||||
0,
|
||||
5
|
||||
]
|
||||
],
|
||||
[
|
||||
{
|
||||
"length": 2,
|
||||
"orientation": "H"
|
||||
},
|
||||
[
|
||||
2,
|
||||
6
|
||||
]
|
||||
]
|
||||
],
|
||||
"bombs": [
|
||||
[
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
]
|
||||
},
|
||||
"grid_enemy": {
|
||||
"columns": 8,
|
||||
"rows": 8,
|
||||
"boats": [],
|
||||
"bombs": [
|
||||
[
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
false,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
],
|
||||
[
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true,
|
||||
true
|
||||
]
|
||||
]
|
||||
}
|
||||
}
|
2
NOTE.md
2
NOTE.md
|
@ -6,13 +6,11 @@ A faire :
|
|||
- Historique / Replay
|
||||
- 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" (?)
|
||||
|
|
|
@ -0,0 +1,4 @@
|
|||
from pathlib import Path
|
||||
|
||||
path_save: Path = Path(".save")
|
||||
path_save.mkdir(exist_ok=True)
|
|
@ -164,3 +164,5 @@ if __name__ == "__main__":
|
|||
print(board.bomb((4, 3)))
|
||||
print(board.bomb((4, 4)))
|
||||
print(board)
|
||||
|
||||
print(board.to_json())
|
||||
|
|
|
@ -1,13 +1,15 @@
|
|||
import json
|
||||
import socket
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from source import path_save
|
||||
from source.core.enums import BombState
|
||||
from source.core.error import InvalidBombPosition, PositionAlreadyShot
|
||||
from source.gui.scene import GameResult
|
||||
from source.gui.scene.abc import Scene
|
||||
from source.gui import widget, texture, scene
|
||||
from source import core
|
||||
from source.network.packet import PacketChat, PacketBombPlaced, PacketBoatPlaced, PacketBombState, PacketQuit
|
||||
from source.network.packet import PacketChat, PacketBombPlaced, PacketBoatPlaced, PacketBombState, PacketQuit, \
|
||||
PacketAskSave, PacketResponseSave
|
||||
from source.type import Point2D
|
||||
from source.utils import StoppableThread
|
||||
|
||||
|
@ -164,6 +166,12 @@ class Game(Scene):
|
|||
style=texture.Button.Style1
|
||||
)
|
||||
|
||||
def ask_save(widget, x, y, button, modifiers):
|
||||
PacketAskSave().send_connection(self.connection)
|
||||
self.chat_new_message("System", "demande de sauvegarde envoyé.")
|
||||
|
||||
self.button_save.add_listener("on_click_release", ask_save)
|
||||
|
||||
self.button_quit = self.add_widget(
|
||||
widget.Button,
|
||||
|
||||
|
@ -187,9 +195,6 @@ class Game(Scene):
|
|||
font_size=20
|
||||
)
|
||||
|
||||
self.board_ally = core.Board(rows=self.grid_height, columns=self.grid_width)
|
||||
self.board_enemy = core.Board(rows=self.grid_height, columns=self.grid_width)
|
||||
|
||||
self._my_turn = my_turn # is it the player turn ?
|
||||
self._boat_ready_ally: bool = False # does the player finished placing his boat ?
|
||||
self._boat_ready_enemy: bool = False # does the opponent finished placing his boat ?
|
||||
|
@ -266,6 +271,24 @@ class Game(Scene):
|
|||
|
||||
# function
|
||||
|
||||
def to_json(self) -> dict:
|
||||
return {
|
||||
"grid_ally": self.grid_ally.board.to_json(),
|
||||
"grid_enemy": self.grid_enemy.board.to_json(),
|
||||
}
|
||||
|
||||
def save(self, value: bool):
|
||||
self.chat_new_message(
|
||||
"System",
|
||||
"Sauvegarde de la partie..." if value else "Sauvegarde de la partie refusé."
|
||||
)
|
||||
if not value: return
|
||||
|
||||
ip_address, _ = self.connection.getpeername()
|
||||
|
||||
with open(path_save / (ip_address + ".bn-save"), "w", encoding="utf-8") as file:
|
||||
json.dump(self.to_json(), file, ensure_ascii=False, indent=4)
|
||||
|
||||
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
|
||||
|
@ -287,15 +310,13 @@ class Game(Scene):
|
|||
def network_on_bomb_placed(self, packet: PacketBombPlaced):
|
||||
try:
|
||||
# essaye de poser la bombe sur la grille alliée
|
||||
bomb_state = self.grid_ally.board.bomb(packet.position)
|
||||
bomb_state = self.grid_ally.place_bomb(packet.position)
|
||||
except (InvalidBombPosition, PositionAlreadyShot):
|
||||
# si une erreur se produit, signale l'erreur
|
||||
bomb_state = BombState.ERROR
|
||||
# l'opposant va rejouer, ce n'est donc pas notre tour
|
||||
self.my_turn = False
|
||||
else:
|
||||
# si la bombe a bien été placé, affiche-la sur la grille visuel allié
|
||||
self.grid_ally.place_bomb(packet.position, bomb_state.success)
|
||||
# c'est à notre tour si l'opposant à loupé sa bombe
|
||||
self.my_turn = not bomb_state.success
|
||||
|
||||
|
@ -324,7 +345,7 @@ class Game(Scene):
|
|||
self.boat_broken_ally += 1
|
||||
|
||||
# place la bombe sur la grille ennemie visuelle
|
||||
self.grid_enemy.place_bomb(packet.position, packet.bomb_state.success)
|
||||
self.grid_enemy.place_bomb(packet.position, force_touched=packet.bomb_state.success)
|
||||
|
||||
if packet.bomb_state is BombState.WON:
|
||||
# si cette bombe a touché le dernier bateau, alors l'on a gagné
|
||||
|
@ -336,6 +357,13 @@ class Game(Scene):
|
|||
from source.gui.scene import GameError
|
||||
self.window.set_scene(GameError, text="L'adversaire a quitté la partie.")
|
||||
|
||||
def network_on_ask_save(self, packet: PacketAskSave):
|
||||
from source.gui.scene import GameSave
|
||||
self.window.add_scene(GameSave, game_scene=self)
|
||||
|
||||
def network_on_response_save(self, packet: PacketResponseSave):
|
||||
self.save(value=packet.value)
|
||||
|
||||
# event
|
||||
|
||||
def on_resize_after(self, width: int, height: int):
|
||||
|
|
47
source/gui/scene/GameLoad.py
Normal file
47
source/gui/scene/GameLoad.py
Normal file
|
@ -0,0 +1,47 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from source.gui import widget, texture
|
||||
from source.gui.scene.abc import Scene
|
||||
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.window import Window
|
||||
|
||||
|
||||
class GameLoad(Scene):
|
||||
def __init__(self, window: "Window", **kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
|
||||
self.label = self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
x=0.5, y=0.5, width=1.0,
|
||||
|
||||
anchor_x="center",
|
||||
|
||||
text="Une ancienne partie contre cet adversaire a été sauvegardé.\nSouhaitez-vous la reprendre ?",
|
||||
align="center",
|
||||
multiline=True,
|
||||
font_size=28,
|
||||
)
|
||||
|
||||
self.refuse = self.add_widget(
|
||||
widget.Button,
|
||||
|
||||
x=20, y=20, width=0.2, height=0.1,
|
||||
|
||||
label_text="Refuser",
|
||||
|
||||
style=texture.Button.Style1
|
||||
)
|
||||
|
||||
self.accept = self.add_widget(
|
||||
widget.Button,
|
||||
|
||||
x=lambda widget: widget.scene.window.width - 20 - widget.width, y=20, width=0.2, height=0.1,
|
||||
|
||||
label_text="Accepter",
|
||||
|
||||
style=texture.Button.Style1
|
||||
)
|
||||
|
64
source/gui/scene/GameSave.py
Normal file
64
source/gui/scene/GameSave.py
Normal file
|
@ -0,0 +1,64 @@
|
|||
from typing import TYPE_CHECKING
|
||||
|
||||
from source.gui import widget, texture
|
||||
from source.gui.scene.abc.Popup import Popup
|
||||
from source.network.packet import PacketResponseSave
|
||||
|
||||
if TYPE_CHECKING:
|
||||
from source.gui.window import Window
|
||||
from source.gui.scene import Game
|
||||
|
||||
|
||||
class GameSave(Popup):
|
||||
def __init__(self, window: "Window", game_scene: "Game", **kwargs):
|
||||
super().__init__(window, **kwargs)
|
||||
|
||||
self.game_scene = game_scene
|
||||
|
||||
self.background = self.add_widget(
|
||||
widget.Image,
|
||||
|
||||
x=0, y=0, width=1.0, height=1.0,
|
||||
|
||||
image=texture.Popup.Style1.background
|
||||
)
|
||||
|
||||
self.text = self.add_widget(
|
||||
widget.Text,
|
||||
|
||||
x=0.5, y=0.5,
|
||||
|
||||
anchor_x="center",
|
||||
|
||||
text="L'adversaire souhaite sauvegarder",
|
||||
font_size=28,
|
||||
align="center",
|
||||
)
|
||||
|
||||
self.refuse = self.add_widget(
|
||||
widget.Button,
|
||||
x=0.20, y=0.20, width=0.2, height=0.1,
|
||||
|
||||
label_text="Refuser",
|
||||
|
||||
style=texture.Button.Style1
|
||||
)
|
||||
|
||||
self.refuse.add_listener("on_click_release", lambda *_: self.choose_save(value=False))
|
||||
|
||||
self.accept = self.add_widget(
|
||||
widget.Button,
|
||||
x=0.60, y=0.20, width=0.2, height=0.1,
|
||||
|
||||
label_text="Accepter",
|
||||
|
||||
style=texture.Button.Style1
|
||||
)
|
||||
|
||||
self.accept.add_listener("on_click_release", lambda *_: self.choose_save(value=True))
|
||||
|
||||
def choose_save(self, value: bool):
|
||||
PacketResponseSave(value=value).send_connection(self.game_scene.connection)
|
||||
self.window.remove_scene(self)
|
||||
self.game_scene.save(value=value)
|
||||
|
|
@ -2,6 +2,8 @@ from .GameResult import GameResult
|
|||
from .Game import Game
|
||||
from .GameQuit import GameQuit
|
||||
from .GameError import GameError
|
||||
from .GameSave import GameSave
|
||||
from .GameLoad import GameLoad
|
||||
from .Settings import Settings
|
||||
from .RoomHost import RoomHost
|
||||
from .RoomJoin import RoomJoin
|
||||
|
|
|
@ -5,7 +5,7 @@ import numpy as np
|
|||
import pyglet.shapes
|
||||
|
||||
from source.core import Board, Boat
|
||||
from source.core.enums import Orientation
|
||||
from source.core.enums import Orientation, BombState
|
||||
from source.core.error import InvalidBoatPosition
|
||||
from source.gui.sprite import Sprite
|
||||
from source.gui.texture.abc import Style
|
||||
|
@ -230,15 +230,21 @@ class GameGrid(BoxWidget):
|
|||
except InvalidBoatPosition: self.display_board(self.board) # if the boat can't be placed, ignore
|
||||
else: self.display_board(preview_board, preview=True)
|
||||
|
||||
def place_bomb(self, cell: Point2D, touched: bool):
|
||||
def place_bomb(self, cell: Point2D, force_touched: bool = None) -> BombState:
|
||||
bomb_state = self.board.bomb(cell)
|
||||
|
||||
self.cell_sprites[cell] = Sprite(
|
||||
img=self.bomb_style.get("touched" if touched else "missed"),
|
||||
img=self.bomb_style.get(
|
||||
"touched" if (bomb_state.success if force_touched is None else force_touched) else "missed"
|
||||
),
|
||||
batch=self.scene.batch,
|
||||
**self._bomb_kwargs
|
||||
)
|
||||
|
||||
self._refresh_size()
|
||||
|
||||
return bomb_state
|
||||
|
||||
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)
|
||||
|
||||
|
|
|
@ -33,6 +33,8 @@ class Client(StoppableThread):
|
|||
|
||||
print(f"[Client] Connecté avec {connection}")
|
||||
|
||||
...
|
||||
|
||||
settings: Any = PacketSettings.from_connection(connection)
|
||||
PacketUsername(username=self.username).send_data_connection(connection)
|
||||
packet_username = PacketUsername.from_connection(connection)
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
import socket
|
||||
from typing import TYPE_CHECKING
|
||||
|
||||
from source import path_save
|
||||
from source.gui import scene
|
||||
from source.network import game_network
|
||||
from source.utils import StoppableThread
|
||||
|
@ -36,13 +37,19 @@ class Host(StoppableThread):
|
|||
|
||||
while True:
|
||||
try:
|
||||
connection, address = server.accept() # accepte la première connexion entrante
|
||||
connection, (ip_address, port) = server.accept() # accepte la première connexion entrante
|
||||
break # sort de la boucle
|
||||
except socket.timeout: # en cas de timeout
|
||||
if self.stopped: return # vérifie si le thread n'est pas censé s'arrêter
|
||||
# sinon, réessaye
|
||||
|
||||
print(f"[Serveur] Connecté avec {address}")
|
||||
print(f"[Serveur] Connecté avec {ip_address}")
|
||||
|
||||
# check pour ancienne sauvegarde contre ce joueur
|
||||
|
||||
...
|
||||
|
||||
# paramètres & jeu
|
||||
|
||||
self.settings.send_data_connection(connection)
|
||||
packet_username = PacketUsername.from_connection(connection)
|
||||
|
|
|
@ -27,6 +27,8 @@ def game_network(
|
|||
packet.PacketBombPlaced: game_scene.network_on_bomb_placed,
|
||||
packet.PacketBombState: game_scene.network_on_bomb_state,
|
||||
packet.PacketQuit: game_scene.network_on_quit,
|
||||
packet.PacketAskSave: game_scene.network_on_ask_save,
|
||||
packet.PacketResponseSave: game_scene.network_on_response_save,
|
||||
}
|
||||
|
||||
try:
|
||||
|
|
7
source/network/packet/PacketAskSave.py
Normal file
7
source/network/packet/PacketAskSave.py
Normal file
|
@ -0,0 +1,7 @@
|
|||
from source.network.packet.abc import SignalPacket
|
||||
|
||||
|
||||
class PacketAskSave(SignalPacket):
|
||||
"""
|
||||
A packet that is sent when the player wish to save the game.
|
||||
"""
|
23
source/network/packet/PacketResponseSave.py
Normal file
23
source/network/packet/PacketResponseSave.py
Normal file
|
@ -0,0 +1,23 @@
|
|||
import struct
|
||||
from dataclasses import field, dataclass
|
||||
|
||||
from source.network.packet.abc import SimplePacket
|
||||
|
||||
|
||||
@dataclass
|
||||
class PacketResponseSave(SimplePacket):
|
||||
"""
|
||||
A packet that is sent when the player accept or refuse a requested save.
|
||||
"""
|
||||
|
||||
value: bool = field() # True si requête accepter, sinon False
|
||||
|
||||
packet_format = ">?"
|
||||
|
||||
def to_bytes(self) -> bytes:
|
||||
return struct.pack(self.packet_format, self.value)
|
||||
|
||||
@classmethod
|
||||
def from_bytes(cls, data: bytes) -> "PacketResponseSave":
|
||||
value, *_ = struct.unpack(cls.packet_format, data)
|
||||
return cls(value=value)
|
|
@ -2,6 +2,8 @@ from .PacketChat import PacketChat
|
|||
from .PacketBombPlaced import PacketBombPlaced
|
||||
from .PacketBombState import PacketBombState
|
||||
from .PacketBoatPlaced import PacketBoatPlaced
|
||||
from .PacketAskSave import PacketAskSave
|
||||
from .PacketResponseSave import PacketResponseSave
|
||||
from .PacketSettings import PacketSettings
|
||||
from .PacketUsername import PacketUsername
|
||||
from .PacketQuit import PacketQuit
|
||||
|
|
Loading…
Reference in a new issue