Compare commits

...

53 commits

Author SHA1 Message Date
Yannis ee17be37e2 Merge pull request 'delta_time' (#34) from delta_time into main
Reviewed-on: #34
Reviewed-by: Adastram <adastram@noreply.tipragot.fr>
2024-01-13 20:05:55 +00:00
Yannis 6cd2591659 Fix du mouvement des particules 2024-01-13 10:44:17 +01:00
Yannis 277ef41773 Fix du mouvement du loup 2024-01-13 10:40:46 +01:00
Yannis 9e132d48d3 Fix du calcul du mouvement des entités 2024-01-13 10:30:22 +01:00
Yannis 15fad80c2e Implementation du delta time pour la caméra 2024-01-13 10:29:53 +01:00
Yannis 46ca725a7e Implementation du delta time pour les entités 2024-01-13 10:24:05 +01:00
Yannis e3ea7cbed1 Ajout d'un vrai delta time 2024-01-13 10:09:21 +01:00
Adastram 561934e4ef Merge pull request 'Render_Fade' (#33) from Render_Fade into main
Reviewed-on: #33
Reviewed-by: Yannis <yannis@noreply.tipragot.fr>
2024-01-13 08:40:44 +00:00
Adastram cc79e5f22e Implemented .fade_in() and .fadeout(). Added fade at game start 2024-01-12 23:42:04 +01:00
Adastram bba73eeddd Upgraded main version in Render_Fade 2024-01-12 23:34:52 +01:00
Yannis 7c352d0379 Merge pull request 'sliders' (#32) from sliders into main
Reviewed-on: #32
Reviewed-by: Adastram <adastram@noreply.tipragot.fr>
2024-01-12 22:29:32 +00:00
Adastram fcc5d1633b Added Fade in main menu 2024-01-12 23:24:50 +01:00
Yannis 93d568f996 Merge remote-tracking branch 'origin/main' into sliders
# Conflicts:
#	src/engine/event_handler.py
2024-01-12 23:17:46 +01:00
Yannis 1644df5481 Petit nettoyage 2024-01-12 23:14:47 +01:00
Yannis 0464804b81 Suppression du code de test 2024-01-12 23:10:02 +01:00
Yannis c9ef08e2bc Fix du méga rectangle en debug en mode responsible du slider 2024-01-12 23:09:20 +01:00
Yannis f157d33f16 Ajout du callback lors du changement de la valeur 2024-01-12 22:36:27 +01:00
Yannis aff8f60b67 Ajout de la suppression des area sliders 2024-01-12 18:42:29 +01:00
Yannis 582060814e Ajout du rendu du rail du slider 2024-01-12 18:42:06 +01:00
Yannis 4128f32d26 Ajout du rendu et début d'implementation des sliders 2024-01-12 18:16:47 +01:00
Yannis 37cdffc8e3 Ajout du callback du hover 2024-01-12 17:17:02 +01:00
Adastram ebaa8accd4 Started debug of fade_in 2024-01-12 17:13:58 +01:00
Yannis 93b7e95dca Fix du callback du slider qui est appelé même si le slider n'était pas tenu 2024-01-12 14:31:57 +01:00
Adastram 71c9475e7a Started fadein implementation 2024-01-11 20:55:55 +01:00
Yannis d5674c7e63 Début d'implementation des callbacks de sliders 2024-01-11 20:42:36 +01:00
Yannis cfb82a5ee7 Ajout de la responsibility des sliders 2024-01-11 17:45:23 +01:00
Adastram d81a3e0452 implemented fadeout 2024-01-11 15:03:07 +01:00
Adastram 777b6adde4 Corrected docstring 2024-01-11 15:02:39 +01:00
Adastram 06a2d7085b Reorganized GUI update to allow fadeout in menus 2024-01-11 14:52:55 +01:00
Adastram 4c79436624 Merge pull request 'Prevented Player from moving when dialog is occuring' (#30) from BlockMoveWhenTalk into main
Reviewed-on: #30
Reviewed-by: Yannis <yannis300307@gmail.com>
2024-01-11 10:03:40 +00:00
Adastram daa7ca46d4 Added doc string 2024-01-11 10:58:56 +01:00
Adastram 67fef0b82e Edited dialogs_manager import 2024-01-11 10:51:14 +01:00
Adastram ce14e45e8f Fixed engine import 2024-01-11 10:50:54 +01:00
Adastram f5c1f8bc63 Moved pause to dialogs_manager 2024-01-11 10:42:42 +01:00
Adastram 9e400598a3 Imported whole engine and moved pause() handling from event_handler 2024-01-11 10:42:26 +01:00
Adastram 8cbe1a3727 Edited default zoom value 2024-01-11 10:41:57 +01:00
Adastram e8343160d4 Edited lock to block all interraction including animation instead of just the movements 2024-01-11 10:30:34 +01:00
Adastram 1f8444bc0b Edited .pause() function to lock all unlocked entity instead of blocking update function, and .resume() to unlock only non-locked entity before pause 2024-01-11 10:29:43 +01:00
Adastram 15565c6712 Added Game satuts display in debug 2024-01-11 09:55:24 +01:00
Adastram ddb27bfd5a Added sync between dialogs and game pause 2024-01-11 09:54:12 +01:00
Adastram 83d51f3e5e Added .pause() and .resume() functions (Pause the game logic (Entites moving and damage) and lock player (Player movements doesn't seems to be affected by the pause of the update loop)) 2024-01-11 09:53:44 +01:00
Adastram afb2e2386f Removed print when next dialog is called 2024-01-11 09:52:04 +01:00
Adastram e2a97c65fd Added .lock() and .unlock() entity functions (Prevent them from moving) 2024-01-11 09:51:29 +01:00
Adastram 197a4ae7dc Prevented Player from moving when dialog is occuring 2024-01-11 08:55:04 +01:00
Adastram f1ccc398b4 Started fade implementation 2024-01-11 08:49:28 +01:00
Yannis c31766cc0c Code exemple responsibility 2024-01-10 21:12:53 +01:00
Yannis 691a4f569f Début de responsibility 2024-01-10 21:12:33 +01:00
Yannis 38a9da9463 Centrage des sliders 2024-01-10 20:54:39 +01:00
Yannis 163f1f0901 Ajout d'une boite de limite de déplacement pour les sliders area 2024-01-10 20:39:11 +01:00
Yannis 8ab8eaba24 Début de rendu débug des sliders 2024-01-10 16:04:00 +01:00
Yannis 1b829aaf26 Ajout d'une classe temporaire slider 2024-01-10 15:58:14 +01:00
Yannis 636e006e50 Début de support de zones slider dans l'event handler 2024-01-10 15:57:46 +01:00
Yannis d74a7038a0 Merge pull request 'SoundManager' (#24) from SoundManager into main
Reviewed-on: #24
2024-01-08 15:22:22 +00:00
13 changed files with 547 additions and 133 deletions

Binary file not shown.

After

Width:  |  Height:  |  Size: 1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

View file

@ -32,23 +32,23 @@ class WolfAI(MobAI):
# On vérifie que le loup peut voir le joueur # On vérifie que le loup peut voir le joueur
if player_distance <= self.ATTACK_DISTANCE: if player_distance <= self.ATTACK_DISTANCE:
# On rétablit la vitesse du loup à 1 # On rétablit la vitesse du loup à 60
self.entity.max_speed = 1. self.entity.max_speed = 60.
# Si le loup touche le joueur, il lui inflige des dégats # Si le loup touche le joueur, il lui inflige des dégats
if player.get_collisions_with_entity(self.entity): if player.get_collisions_with_entity(self.entity):
player.take_damages(1) player.take_damages(1)
# Si le loup n'est pas déja sur le joueur, on le fait s'en raprocher # Si le loup n'est pas déja sur le joueur, on le fait s'en raprocher
if player_distance > self.entity.max_speed: if player_distance > 1.:
self.entity.move(x_distance / player_distance*self.entity.max_speed, self.entity.move(x_distance / player_distance*self.entity.max_speed,
y_distance / player_distance*self.entity.max_speed, self.map_manager) y_distance / player_distance*self.entity.max_speed, self.map_manager, delta)
else: else:
# Comportement d'attente # Comportement d'attente
# On diminue la vitesse # On diminue la vitesse
self.entity.max_speed = 0.5 self.entity.max_speed = 30.
self.timer -= delta self.timer -= delta
# Si le timer est fini et que le loup était en train d'attendre, il commence à marcher # Si le timer est fini et que le loup était en train d'attendre, il commence à marcher
@ -66,4 +66,4 @@ class WolfAI(MobAI):
# On fait avancer le loup quand il le doit # On fait avancer le loup quand il le doit
if self.comportment == 1: if self.comportment == 1:
self.entity.move(self.walk_x, self.walk_y, self.map_manager) self.entity.move(self.walk_x, self.walk_y, self.map_manager, delta)

View file

@ -5,7 +5,7 @@ class Camera:
def __init__(self): def __init__(self):
self.x = 0 self.x = 0
self.y = 0 self.y = 0
self.zoom = 1. self.zoom = 1.75
# Décalage lors du mouvement du joueur # Décalage lors du mouvement du joueur
self.player_moving_offset = 100 self.player_moving_offset = 100
@ -15,11 +15,11 @@ class Camera:
self.target_y = self.y self.target_y = self.y
self.target_zoom = self.zoom self.target_zoom = self.zoom
self.smoothness = 20. self.smoothness = 0.5
self.followed_entity: Entity | None = None self.followed_entity: Entity | None = None
def update(self): def update(self, delta: float):
"""Met à jour la caméra. Permet, par exemple, de faire le scrolling.""" """Met à jour la caméra. Permet, par exemple, de faire le scrolling."""
# Si on suit une entité, on met à jour les coordonnées de suivi # Si on suit une entité, on met à jour les coordonnées de suivi
@ -29,9 +29,9 @@ class Camera:
self.target_y = (self.followed_entity.y + self.followed_entity.mouvements[1] * self.target_y = (self.followed_entity.y + self.followed_entity.mouvements[1] *
self.player_moving_offset / self.zoom) self.player_moving_offset / self.zoom)
self.x += (self.target_x - self.x) / self.smoothness self.x += (self.target_x - self.x)*delta / self.smoothness
self.y += (self.target_y - self.y) / self.smoothness self.y += (self.target_y - self.y)*delta / self.smoothness
self.zoom += (self.target_zoom - self.zoom) / self.smoothness self.zoom += (self.target_zoom - self.zoom)*delta / self.smoothness
def follow_entity(self, entity: Entity | None): def follow_entity(self, entity: Entity | None):
"""Active le suivit de l'entité donnée. Mettre `None` pour retirer le suivit.""" """Active le suivit de l'entité donnée. Mettre `None` pour retirer le suivit."""

View file

@ -1,14 +1,11 @@
import json import json
from types import FunctionType from types import FunctionType
import src.engine.engine as engine
from src.engine.event_handler import EventHandler
class DialogsManager: class DialogsManager:
"""Classe qui gère la lecture des dialogues.""" """Classe qui gère la lecture des dialogues."""
def __init__(self, event_handler: EventHandler): def __init__(self, engine: 'engine.Engine'):
self.event_handler = event_handler self.engine = engine
self.current_dialogs = [] self.current_dialogs = []
self.current_dialog_id = -1 self.current_dialog_id = -1
self.dialogs = {} self.dialogs = {}
@ -31,8 +28,6 @@ class DialogsManager:
else: else:
self.next_dialog() self.next_dialog()
print("next")
def next_dialog(self): def next_dialog(self):
"""Passe au dialogue suivant. Appelle le callback si le dialogue est fini.""" """Passe au dialogue suivant. Appelle le callback si le dialogue est fini."""
self.current_dialog_id += 1 self.current_dialog_id += 1
@ -43,7 +38,8 @@ class DialogsManager:
self.current_dialog_id = -1 self.current_dialog_id = -1
self.writing_dialog = False self.writing_dialog = False
self.reading_dialog = False self.reading_dialog = False
self.event_handler.remove_button_area("next_dialog") self.engine.entity_manager.resume()
self.engine.event_handler.remove_button_area("next_dialog")
if self.dialogue_finished_callback is not None: if self.dialogue_finished_callback is not None:
self.dialogue_finished_callback() self.dialogue_finished_callback()
@ -52,7 +48,9 @@ class DialogsManager:
# Si un dialogue n'est pas déja lancé, on lance le dialogue au nom donné # Si un dialogue n'est pas déja lancé, on lance le dialogue au nom donné
if not self.reading_dialog: if not self.reading_dialog:
self.event_handler.register_button_area((0, 0, 1, 1), self.next_signal, "next_dialog", 2) self.engine.entity_manager.pause()
self.engine.event_handler.register_button_area((0, 0, 1, 1), self.next_signal, "next_dialog", 2)
self.current_dialogs = self.dialogs[name] self.current_dialogs = self.dialogs[name]
self.current_dialog_id = 0 self.current_dialog_id = 0

View file

@ -1,3 +1,5 @@
import time
from src.engine.boss_fight_manager import BossFightManager from src.engine.boss_fight_manager import BossFightManager
from src.engine.camera import Camera from src.engine.camera import Camera
from src.engine.dialogs_manager import DialogsManager from src.engine.dialogs_manager import DialogsManager
@ -24,8 +26,6 @@ class Engine:
# Etat courant du jeu # Etat courant du jeu
self.game_state = GameState.NORMAL self.game_state = GameState.NORMAL
self.clock = pygame.time.Clock()
self.running = False self.running = False
# Composants du moteur de jeu # Composants du moteur de jeu
@ -36,7 +36,7 @@ class Engine:
self.entity_manager = EntityManager(self.map_manager) self.entity_manager = EntityManager(self.map_manager)
self.boss_fight_manager = BossFightManager(self) self.boss_fight_manager = BossFightManager(self)
self.event_sheduler = EventSheduler(self) self.event_sheduler = EventSheduler(self)
self.dialogs_manager = DialogsManager(self.event_handler) self.dialogs_manager = DialogsManager(self)
self.menu_manager = MenuManager(self) self.menu_manager = MenuManager(self)
self.sound_manager = SoundManager(60) self.sound_manager = SoundManager(60)
@ -44,20 +44,26 @@ class Engine:
"""Fonction à lancer au début du programme et qui va lancer les updates dans une boucle. """Fonction à lancer au début du programme et qui va lancer les updates dans une boucle.
Attend jusqu'à la fin du jeu.""" Attend jusqu'à la fin du jeu."""
self.running = True self.running = True
while self.running:
self.update()
self.clock.tick(60.)
def update(self): delta = 1. # Le delta est le temps depuis la dernière image
last_time = time.time_ns()/10E8
while self.running:
self.update(delta)
new_time = time.time_ns()/10E8
delta = new_time-last_time
last_time = new_time
def update(self, delta: float):
"""Fonction qui regroupe toutes les updates des composants. Elle permet de mettre à jour le jeu quand on """Fonction qui regroupe toutes les updates des composants. Elle permet de mettre à jour le jeu quand on
l'appelle.""" l'appelle."""
self.camera.update() self.camera.update(delta)
self.entity_manager.update(0.016666666) self.entity_manager.update(delta)
self.renderer.update(0.016666666) self.renderer.update(delta)
self.event_handler.update() self.event_handler.update(delta)
self.event_sheduler.update() self.event_sheduler.update()
self.dialogs_manager.update(0.016666666) self.dialogs_manager.update(delta)
self.sound_manager.update(1/60) self.sound_manager.update(delta)
def stop(self): def stop(self):
"""Arrête le programme.""" """Arrête le programme."""

View file

@ -11,6 +11,8 @@ class Entity:
self.x = 8 self.x = 8
self.y = 8 self.y = 8
self.locked = False # Variable définissant si l'entité est bloqué ou non (.lock() et .unlock())
self.direction = 0 # 0 : tourné vers la droite (ou sens par défaut), 1 : tourné vers la gauche (ou retourné) self.direction = 0 # 0 : tourné vers la droite (ou sens par défaut), 1 : tourné vers la gauche (ou retourné)
# Variables utilisées pour détecter les mouvements # Variables utilisées pour détecter les mouvements
@ -63,8 +65,23 @@ class Entity:
# Si les coordonnées ont changé, l'entité a bougé # Si les coordonnées ont changé, l'entité a bougé
self.mouvements[0] = (self.x - self.last_x) / self.max_speed x_motion = (self.x - self.last_x)
self.mouvements[1] = (self.y - self.last_y) / self.max_speed
if x_motion > 0:
self.mouvements[0] = 1
elif x_motion < 0:
self.mouvements[0] = -1
else:
self.mouvements[0] = 0
y_motion = (self.y - self.last_y)
if y_motion > 0:
self.mouvements[1] = 1
elif y_motion < 0:
self.mouvements[1] = -1
else:
self.mouvements[1] = 0
self.last_x = self.x self.last_x = self.x
self.last_y = self.y self.last_y = self.y
@ -114,55 +131,67 @@ class Entity:
return collision return collision
def move(self, x: float, y: float, map_manager: MapManager): def move(self, x: float, y: float, map_manager: MapManager, delta: float):
"""Fait bouger l'entité en tenant compte des collisions.""" """Fait bouger l'entité en tenant compte des collisions."""
# On vérifie le sens du mouvement pour changer self.direction if not self.locked: # Si l'entité n'est pas verrouillée on calcul le mouvement
if x > 0:
self.direction = 0 # On vérifie le sens du mouvement pour changer self.direction
elif x < 0:
self.direction = 1
# On ne met pas de else car si x = 0, on ne change pas de direction
# On normalise la vitesse
initial_speed = math.sqrt(x**2+y**2)
x = x/initial_speed*self.max_speed
y = y/initial_speed*self.max_speed
# On simule le mouvement. Si on ne rencontre pas de collision, on applique le mouvement
if not self.get_collisions(self.x + x, self.y, map_manager):
self.x += x
else:
# Si on a une collision, on avance pixel par pixel jusqu'à atteindre la collision
i = 0
if x > 0: if x > 0:
while not self.get_collisions(self.x + i, self.y, map_manager): self.direction = 0
i += 1 elif x < 0:
i -= 1 self.direction = 1
# On ne met pas de else car si x = 0, on ne change pas de direction
# On normalise la vitesse
initial_speed = math.sqrt(x**2+y**2)
x = x*delta/initial_speed*self.max_speed
y = y*delta/initial_speed*self.max_speed
# On simule le mouvement. Si on ne rencontre pas de collision, on applique le mouvement
if not self.get_collisions(self.x + x, self.y, map_manager):
self.x += x
else: else:
while not self.get_collisions(self.x + i, self.y, map_manager): # Si on a une collision, on avance pixel par pixel jusqu'à atteindre la collision
i = 0
if x > 0:
while not self.get_collisions(self.x + i, self.y, map_manager):
i += 1
i -= 1 i -= 1
i += 1 else:
while not self.get_collisions(self.x + i, self.y, map_manager):
self.x += i i -= 1
# On répète le procédé avec l'ordonnée
if not self.get_collisions(self.x, self.y + y, map_manager):
self.y += y
else:
i = 0
if y > 0:
while not self.get_collisions(self.x, self.y + i, map_manager):
i += 1 i += 1
i -= 1
else:
while not self.get_collisions(self.x, self.y + i, map_manager):
i -= 1
i += 1
self.y += i self.x += i
# On répète le procédé avec l'ordonnée
if not self.get_collisions(self.x, self.y + y, map_manager):
self.y += y
else:
i = 0
if y > 0:
while not self.get_collisions(self.x, self.y + i, map_manager):
i += 1
i -= 1
else:
while not self.get_collisions(self.x, self.y + i, map_manager):
i -= 1
i += 1
self.y += i
def link_animation(self, name: str): def link_animation(self, name: str):
"""Met à jour l'animation en cours de l'entité.""" """Met à jour l'animation en cours de l'entitée."""
self.animation_name = name self.animation_name = name
def lock(self):
"""Bloque tout les mouvements de l'entitée"""
self.locked = True
def unlock(self):
"""Débloque tout les mouvements de l'entitée"""
self.locked = False

View file

@ -8,6 +8,8 @@ class EntityManager:
self.entities: dict[str:Entity] = {} self.entities: dict[str:Entity] = {}
self.player_entity_name = "" self.player_entity_name = ""
self.map_manager = map_manager self.map_manager = map_manager
self.locked_before_pause: list[Entity] = []
self.paused = False
def register_entity(self, name: str) -> Entity: def register_entity(self, name: str) -> Entity:
"""Crée une entité et l'enregistre dans un dictionnaire.""" """Crée une entité et l'enregistre dans un dictionnaire."""
@ -19,10 +21,10 @@ class EntityManager:
"""Définit l'entité donnée comme le joueur. Elle peut donc être controlée.""" """Définit l'entité donnée comme le joueur. Elle peut donc être controlée."""
self.player_entity_name = name self.player_entity_name = name
def move_player_controls(self, x: float, y: float): def move_player_controls(self, x: float, y: float, delta: float):
"""Bouge le joueur. X et y doivent être compris entre 0 et 1""" """Bouge le joueur. X et y doivent être compris entre 0 et 1"""
player: Entity = self.get_by_name(self.player_entity_name) player: Entity = self.get_by_name(self.player_entity_name)
player.move(x, y, self.map_manager) player.move(x, y, self.map_manager, delta)
def update(self, delta: float): def update(self, delta: float):
"""Met à jour toutes les entités enregistrées.""" """Met à jour toutes les entités enregistrées."""
@ -32,7 +34,7 @@ class EntityManager:
if entity.life_points == 0: if entity.life_points == 0:
self.entities.pop(entity_name) self.entities.pop(entity_name)
if entity.brain is not None: if entity.brain is not None and not self.paused:
entity.brain.update(delta) entity.brain.update(delta)
if self.player_entity_name: if self.player_entity_name:
@ -49,3 +51,21 @@ class EntityManager:
def get_by_name(self, name: str) -> Entity: def get_by_name(self, name: str) -> Entity:
"""Donne l'entité avec le nom donné.""" """Donne l'entité avec le nom donné."""
return self.entities[name] return self.entities[name]
def pause(self):
"""Met en pause tout les mouvements de toutes les entitées"""
for e in self.get_all_entities():
if e.locked:
self.locked_before_pause.append(e)
else:
e.lock()
self.paused = True
def resume(self):
"""Reprend les mouvement de toutes les entitées qui n'étaient pas lock avant l'appel de .pause()"""
for e in self.get_all_entities():
if not e in self.locked_before_pause:
e.unlock()
self.paused = False
self.locked_before_pause = []

View file

@ -14,7 +14,9 @@ class EventHandler:
self.engine = core self.engine = core
self.key_pressed = [] self.key_pressed = []
self.buttons_area = [] self.buttons_area = []
self.hovered_area = [] self.hovered_buttons_area = []
self.hovered_sliders_area = []
self.sliders_area = []
@staticmethod @staticmethod
def get_click_collision(rect: tuple[float | int, float | int, float | int, float | int], point: tuple[int, int], def get_click_collision(rect: tuple[float | int, float | int, float | int, float | int], point: tuple[int, int],
@ -40,7 +42,7 @@ class EventHandler:
callback: FunctionType | classmethod | staticmethod, name: str, callback: FunctionType | classmethod | staticmethod, name: str,
is_window_relative: int = -1, is_window_relative: int = -1,
hover_callback: FunctionType | classmethod | staticmethod = None): hover_callback: FunctionType | classmethod | staticmethod = None):
"""Enregistre une zone comme bouton. La fonction donnée sera donc executé lorsque la zone sur la fenêtre """Enregistre une zone comme bouton. La fonction donnée sera donc executée lorsque la zone sur la fenêtre
sera cliqué. is_window_relative doit être 0 pour que le rect soit multipliée par la largeur de la fenêtre et 1 sera cliqué. is_window_relative doit être 0 pour que le rect soit multipliée par la largeur de la fenêtre et 1
pour qu'elle soit multipliée par la hauteur""" pour qu'elle soit multipliée par la hauteur"""
self.buttons_area.append((rect, callback, is_window_relative, name, hover_callback)) self.buttons_area.append((rect, callback, is_window_relative, name, hover_callback))
@ -56,9 +58,51 @@ class EventHandler:
self.buttons_area = cleared_list self.buttons_area = cleared_list
def update(self): def register_slider_area(self, size: tuple[float | int, float | int],
motion_rect: tuple[float | int, float | int, float | int, float | int],
motion_axes: tuple[bool, bool],
name: str,
is_window_relative: int = -1,
clicked_callback: FunctionType | classmethod | staticmethod = None,
released_callback: FunctionType | classmethod | staticmethod = None,
motion_callback: FunctionType | classmethod | staticmethod = None,
hover_callback: FunctionType | classmethod | staticmethod = None):
"""Enregistre une zone comme une zone déplaçable à l'écran."""
self.sliders_area.append([[motion_rect[0], motion_rect[1], *size], is_window_relative, False, (0, 0),
motion_axes, motion_rect,
clicked_callback, released_callback, hover_callback, motion_callback, name])
# Le premier booléen correspond à l'état de suivi de la souris
def remove_slider_area(self, name: str):
"""Supprime les sliders aux noms donnés."""
# On itère dans toute la liste et on ne garde que les éléments ne portant pas le nom cherché
cleared_list = []
for area in self.sliders_area:
if area[10] != name:
cleared_list.append(area)
self.sliders_area = cleared_list
@staticmethod
def get_slider_area_values(slider: list):
"""Donne la valeur de la zone de slider donnée."""
if slider[5][2]:
x_value = round((slider[0][0]-slider[5][0])/slider[5][2], 5)
else:
x_value = -1
if slider[5][3]:
y_value = round((slider[0][1]-slider[5][1])/slider[5][3], 5)
else:
y_value = -1
return x_value, y_value
def update(self, delta: float):
"""Vérifie s'il y a de nouvelles interactions et les traites.""" """Vérifie s'il y a de nouvelles interactions et les traites."""
window_size = display.get_window_size()
# Récupère les événements # Récupère les événements
for e in event.get(): for e in event.get():
if e.type == QUIT: if e.type == QUIT:
@ -74,27 +118,98 @@ class EventHandler:
for area in self.buttons_area: for area in self.buttons_area:
if self.get_click_collision(area[0], e.pos, area[2]): if self.get_click_collision(area[0], e.pos, area[2]):
area[1]() area[1]()
for area in self.sliders_area:
if self.get_click_collision(
(area[0][0]-area[0][2]/2, area[0][1]-area[0][3]/2, area[0][2], area[0][3]),
e.pos, area[1]):
area[2] = True
if area[1] == 0:
area[3] = (e.pos[0]/window_size[0] - area[0][0], e.pos[1]/window_size[0] - area[0][1])
elif area[1] == 1:
area[3] = (e.pos[0]/window_size[1] - area[0][0], e.pos[1]/window_size[1] - area[0][1])
elif area[1] == 2:
area[3] = (e.pos[0]/window_size[0] - area[0][0], e.pos[1]/window_size[1] - area[0][1])
else:
area[3] = (e.pos[0] - area[0][0], e.pos[1] - area[0][1])
if area[6] is not None:
area[6](self.get_slider_area_values(area))
elif e.type == MOUSEBUTTONUP:
for area in self.sliders_area:
if area[2]:
area[2] = False
if area[7] is not None:
area[7](self.get_slider_area_values(area))
elif e.type == MOUSEMOTION: elif e.type == MOUSEMOTION:
for area in self.buttons_area: for area in self.buttons_area:
if area[4] is not None: if area[4] is not None:
if self.get_click_collision(area[0], e.pos, area[2]): if self.get_click_collision(area[0], e.pos, area[2]):
if area not in self.hovered_area: if area not in self.hovered_buttons_area:
area[4](True) area[4](True)
self.hovered_area.append(area) self.hovered_buttons_area.append(area)
else: else:
if area in self.hovered_area: if area in self.hovered_buttons_area:
area[4](False) area[4](False)
self.hovered_area.remove(area) self.hovered_buttons_area.remove(area)
for area in self.sliders_area:
if area[2]:
if area[4][0]:
if area[1] == 0:
area[0][0] = e.pos[0]/window_size[0]-area[3][0]
elif area[1] == 1:
area[0][0] = e.pos[0]/window_size[1]-area[3][0]
elif area[1] == 2:
area[0][0] = e.pos[0]/window_size[0]-area[3][0]
else:
area[0][0] = e.pos[0] - area[3][0]
if area[4][1]:
if area[1] == 0:
area[0][1] = e.pos[1]/window_size[0]-area[3][1]
elif area[1] == 1:
area[0][1] = e.pos[1]/window_size[1]-area[3][1]
elif area[1] == 2:
area[0][1] = e.pos[1]/window_size[1]-area[3][1]
else:
area[0][1] = e.pos[1]-area[3][1]
if area[0][0] < area[5][0]:
area[0][0] = area[5][0]
if area[0][0] > area[5][0]+area[5][2]:
area[0][0] = area[5][0]+area[5][2]
if area[0][1] < area[5][1]:
area[0][1] = area[5][1]
if area[0][1] > area[5][1]+area[5][3]:
area[0][1] = area[5][1]+area[5][3]
if area[9] is not None:
area[9](self.get_slider_area_values(area))
if area[8] is not None:
if self.get_click_collision(
(area[0][0] - area[0][2] / 2, area[0][1] - area[0][3] / 2, area[0][2], area[0][3]),
e.pos, area[1]):
if area not in self.hovered_sliders_area:
area[8](True)
self.hovered_sliders_area.append(area)
else:
if area in self.hovered_sliders_area:
area[8](False)
self.hovered_sliders_area.remove(area)
if self.engine.entity_manager.player_entity_name: if self.engine.entity_manager.player_entity_name:
if K_RIGHT in self.key_pressed: if K_RIGHT in self.key_pressed:
self.engine.entity_manager.move_player_controls(1, 0) self.engine.entity_manager.move_player_controls(1, 0, delta)
if K_LEFT in self.key_pressed: if K_LEFT in self.key_pressed:
self.engine.entity_manager.move_player_controls(-1, 0) self.engine.entity_manager.move_player_controls(-1, 0, delta)
if K_UP in self.key_pressed: if K_UP in self.key_pressed:
self.engine.entity_manager.move_player_controls(0, -1) self.engine.entity_manager.move_player_controls(0, -1, delta)
if K_DOWN in self.key_pressed: if K_DOWN in self.key_pressed:
self.engine.entity_manager.move_player_controls(0, 1) self.engine.entity_manager.move_player_controls(0, 1, delta)
if K_SPACE in self.key_pressed: if K_SPACE in self.key_pressed:
self.engine.dialogs_manager.next_signal() self.engine.dialogs_manager.next_signal()
@ -106,12 +221,13 @@ class EventHandler:
if K_p in self.key_pressed: if K_p in self.key_pressed:
self.engine.renderer.emit_particles(math.floor(self.engine.entity_manager.get_by_name("player").x), self.engine.renderer.emit_particles(math.floor(self.engine.entity_manager.get_by_name("player").x),
math.floor(self.engine.entity_manager.get_by_name("player").y), math.floor(self.engine.entity_manager.get_by_name("player").y),
16, 16, 16, 1, 8, 0, 1, 0.2, 1., (0, 200, 200)) 16, 16, 16, 1, 8, 0, 60., 0.2, 1., (0, 200, 200))
if K_o in self.key_pressed: if K_o in self.key_pressed:
print(f"Player pos: X = {self.engine.entity_manager.get_by_name('player').x} " print(f"Player pos: X = {self.engine.entity_manager.get_by_name('player').x} "
f"Y = {self.engine.entity_manager.get_by_name('player').y}") f"Y = {self.engine.entity_manager.get_by_name('player').y}")
if K_x in self.key_pressed: if K_x in self.key_pressed:
self.engine.camera.target_zoom *= 1.01 self.engine.camera.target_zoom *= 1.01
if K_c in self.key_pressed: if K_c in self.key_pressed:
self.engine.camera.target_zoom *= 0.99 self.engine.camera.target_zoom *= 0.99

View file

@ -7,30 +7,74 @@ import src.engine.engine
class Widget: class Widget:
"""Classe parente des widgets de menu.""" """Classe parente des widgets de menu."""
def __init__(self, x, y, is_window_relative): def __init__(self, x, y, is_window_relative, widget_name):
self.x = x self.x = x
self.y = y self.y = y
self.is_window_relative = is_window_relative self.is_window_relative = is_window_relative
self.widget_name = widget_name
class Label(Widget): class Label(Widget):
"""Un widget de texte.""" """Un widget de texte."""
def __init__(self, x: int | float, y: int | float, text: str, size: int | float, color: tuple[int, int, int], def __init__(self, x: int | float, y: int | float, text: str, size: int | float, color: tuple[int, int, int],
centered: bool = False, is_window_relative: int = -1): widget_name: str, centered: bool = False, is_window_relative: int = -1):
super().__init__(x, y, is_window_relative) super().__init__(x, y, is_window_relative, widget_name)
self.text = text self.text = text
self.size = size self.size = size
self.centered = centered self.centered = centered
self.color = color self.color = color
class Slider(Widget):
"""Un widget pouvant être glissé pour récupérer une valeur."""
def __init__(self, cursor_size: tuple[int | float, int | float],
area_rect: tuple[int | float, int | float],
width: int | float,
base_image: pygame.Surface,
hover_image: pygame.Surface,
rail_image: pygame.Surface,
widget_name: str,
value_changed_callback: FunctionType | classmethod | staticmethod | None = None,
is_window_relative: int = -1,
area_name: str = "menu_slider"):
super().__init__(area_rect[0], area_rect[1], is_window_relative, widget_name)
self.base_image = base_image
self.hover_image = hover_image
self.rail_image = rail_image
self.area_name = area_name
self.value_changed_callback = value_changed_callback
self.hovered = False
self.follow_mouse = False
self.cursor_size = cursor_size
self.value = 0.
self.width = width
def set_hover_state(self, state: bool):
"""Modifie la valeur du hover."""
self.hovered = state
def set_value(self, values: tuple[float, float]):
"""Appelée lorsque la valeur du slider est modifiée."""
new_value = values[0]
if new_value != self.value:
self.value = new_value
if self.value_changed_callback is not None:
self.value_changed_callback(self.value)
def get_value(self):
"""Retourne la valeur entre 0.0 et 1.0 du slider."""
return self.value
class Button(Widget): class Button(Widget):
"""Un widget de bouton.""" """Un widget de bouton."""
def __init__(self, x: int | float, y: int | float, text: str, size: int | float, color: tuple[int, int, int], def __init__(self, x: int | float, y: int | float, text: str, size: int | float, color: tuple[int, int, int],
callback: FunctionType | classmethod | staticmethod, base_image: pygame.Surface, callback: FunctionType | classmethod | staticmethod, base_image: pygame.Surface,
hover_image: pygame.Surface, centered: bool = False, is_window_relative: int = -1, hover_image: pygame.Surface, widget_name: str, centered: bool = False, is_window_relative: int = -1,
area_name: str = "menu_button"): area_name: str = "menu_button"):
super().__init__(x, y, is_window_relative) super().__init__(x, y, is_window_relative, widget_name)
self.text = text self.text = text
self.size = size self.size = size
self.color = color self.color = color
@ -68,28 +112,48 @@ class MenuManager:
"""Ajoute le menu donné au manager de menu avec le nom donné.""" """Ajoute le menu donné au manager de menu avec le nom donné."""
self.menus[name] = menu self.menus[name] = menu
def get_widgets_at_name(self, menu_name: str, widget_name: str):
"""Donne le widget au nom donné dans le menu au nom donné."""
menu = self.menus[menu_name]
found_sliders = []
for widget in menu.widgets:
if widget.widget_name == widget_name:
found_sliders.append(widget)
return found_sliders
def show(self, name: str): def show(self, name: str):
"""Affiche le menu au nom donné.""" """Affiche le menu au nom donné."""
self.active_menu = self.menus[name] self.active_menu = self.menus[name]
# On itère dans tous les bouttons pour leur ajouter une interaction # On itère dans tous les bouttons pour leur ajouter une interaction
for btn in self.active_menu.widgets: for widget in self.active_menu.widgets:
if isinstance(btn, Button): if isinstance(widget, Button):
width = btn.base_image.get_width() / self.engine.renderer.window_size[0] width = widget.base_image.get_width() / self.engine.renderer.window_size[0]
height = btn.base_image.get_height() / self.engine.renderer.window_size[1] height = widget.base_image.get_height() / self.engine.renderer.window_size[1]
area_x = btn.x area_x = widget.x
area_y = btn.y area_y = widget.y
if btn.centered: if widget.centered:
area_x -= width / 2 area_x -= width / 2
area_y -= height / 2 area_y -= height / 2
self.engine.event_handler.register_button_area((area_x, area_y, width, height), btn.callback, self.engine.event_handler.register_button_area((area_x, area_y, width, height), widget.callback,
btn.area_name, widget.area_name,
btn.is_window_relative, btn.set_hover_state) widget.is_window_relative, widget.set_hover_state)
elif isinstance(widget, Slider):
self.engine.event_handler.register_slider_area(widget.cursor_size,
(widget.x, widget.y, widget.width, 0), (True, False),
widget.area_name,
widget.is_window_relative,
hover_callback=widget.set_hover_state,
motion_callback=widget.set_value)
def hide(self): def hide(self):
"""Affiche le menu actuelement à l'écran.""" """Cache le menu actuelement à l'écran."""
# On itère dans tous les bouttons pour retirer l'interaction # On itère dans tous les bouttons pour retirer l'interaction
for btn in self.active_menu.widgets: for widget in self.active_menu.widgets:
if isinstance(btn, Button): if isinstance(widget, Button):
self.engine.event_handler.remove_button_area(btn.area_name) self.engine.event_handler.remove_button_area(widget.area_name)
if isinstance(widget, Slider):
self.engine.event_handler.remove_slider_area(widget.area_name)
self.active_menu = None self.active_menu = None

View file

@ -1,13 +1,15 @@
import math import math
import random import random
from time import time_ns
from types import FunctionType
from pygame import display, image, surface, transform, draw, font from pygame import display, image, surface, transform, draw, font
from pygame.locals import RESIZABLE, SRCALPHA, FULLSCREEN from pygame.locals import RESIZABLE, SRCALPHA, FULLSCREEN
import src.engine.engine as engine import src.engine.engine as engine
from src.engine.animation import Anim from src.engine.animation import Anim
from src.engine.enums import GameState from src.engine.enums import GameState
from src.engine.menu_manager import Label, Button from src.engine.menu_manager import Label, Button, Slider
class Renderer: class Renderer:
@ -15,6 +17,7 @@ class Renderer:
def __init__(self, core: 'engine.Engine'): def __init__(self, core: 'engine.Engine'):
self.engine = core self.engine = core
self.timer = 0 # Timer local
self.window_type = RESIZABLE self.window_type = RESIZABLE
self.window_size = (display.Info().current_w, display.Info().current_h) if self.window_type == FULLSCREEN else ( self.window_size = (display.Info().current_w, display.Info().current_h) if self.window_type == FULLSCREEN else (
600, 600) 600, 600)
@ -37,6 +40,24 @@ class Renderer:
# Particules affichées # Particules affichées
self.particles = [] self.particles = []
# Varialbes du fadeout
self.fadeout_timer = 0 # Timer de fadeout
self.fadeout_is_fading = False
self.fadeout_fade_in_s = 0
self.fadeout_fade_color = (255, 255, 255)
self.fadeout_fade_opacity = 100
self.fadeout_pause = False
self.fadeout_fade_callback = None
# Variables du fadein
self.fadein_timer = 0 # Timer de fadein
self.fadein_is_fading = False
self.fadein_fade_in_s = 0
self.fadein_fade_color = (255, 255, 255)
self.fadein_fade_opacity = 100
self.fadein_pause = False
self.fadein_fade_callback = None
def emit_particles(self, x: int, y: int, w: int, h: int, count: int, min_size: int, max_size: int, def emit_particles(self, x: int, y: int, w: int, h: int, count: int, min_size: int, max_size: int,
min_speed: float, max_speed: float, min_life_time: float, max_life_time: float, min_speed: float, max_speed: float, min_life_time: float, max_life_time: float,
color: tuple[int, int, int]): color: tuple[int, int, int]):
@ -80,18 +101,40 @@ class Renderer:
def update(self, delta: float): def update(self, delta: float):
"""Fait le rendu du jeu.""" """Fait le rendu du jeu."""
self.timer -= delta
self.fadeout_timer -= delta
self.fadein_timer -= delta
if self.fadeout_timer < 0:
if self.fadeout_is_fading:
self.fadeout_is_fading = False
if self.fadeout_pause:
self.engine.entity_manager.resume()
self.fadeout_pause = False
if self.fadeout_fade_callback is not None:
self.fadeout_fade_callback()
if self.fadein_timer < 0:
if self.fadein_is_fading:
self.fadein_is_fading = False
if self.fadein_pause:
self.engine.entity_manager.resume()
self.fadein_pause = False
if self.fadein_fade_callback is not None:
self.fadein_fade_callback()
self.window.fill((255, 255, 255)) self.window.fill((255, 255, 255))
# On crée une surface qui sera ajoutée à la fenêtre apres rendered_surface pour pouvoir mettre des GUI
gui_surface = surface.Surface(display.get_window_size(), SRCALPHA)
gui_surface.fill((0, 0, 0, 0))
if self.engine.game_state == GameState.NORMAL: if self.engine.game_state == GameState.NORMAL:
# On crée une surface temporaire qui nous permettra de faire le rendu à l'échelle 1:1 # On crée une surface temporaire qui nous permettra de faire le rendu à l'échelle 1:1
rendered_surface_size = (display.get_window_size()[0] / self.engine.camera.zoom, rendered_surface_size = (display.get_window_size()[0] / self.engine.camera.zoom,
display.get_window_size()[1] / self.engine.camera.zoom) display.get_window_size()[1] / self.engine.camera.zoom)
rendered_surface = surface.Surface(rendered_surface_size) rendered_surface = surface.Surface(rendered_surface_size)
# On crée une surface qui sera ajoutée à la fenêtre apres rendered_surface pour pouvoir mettre des GUI
gui_surface = surface.Surface(display.get_window_size(), SRCALPHA)
gui_surface.fill((0, 0, 0, 0))
self.render_layer(0, rendered_surface) self.render_layer(0, rendered_surface)
self.render_layer(1, rendered_surface) self.render_layer(1, rendered_surface)
self.render_entities(rendered_surface, gui_surface, delta) self.render_entities(rendered_surface, gui_surface, delta)
@ -105,7 +148,6 @@ class Renderer:
math.ceil(rendered_surface_size[1] * self.engine.camera.zoom))), math.ceil(rendered_surface_size[1] * self.engine.camera.zoom))),
(0, 0)) (0, 0))
self.window.blit(gui_surface, (0, 0))
elif self.engine.game_state == GameState.BOSS_FIGHT: elif self.engine.game_state == GameState.BOSS_FIGHT:
self.window.fill((255, 230, 230)) self.window.fill((255, 230, 230))
@ -115,9 +157,22 @@ class Renderer:
# Rend les menus # Rend les menus
self.render_menus() self.render_menus()
if self.fadeout_is_fading != self.fadein_is_fading:
if self.fadeout_is_fading:
r, g, b = self.fadeout_fade_color
a = (1 - self.fadeout_timer / self.fadeout_fade_in_s) * self.fadeout_fade_opacity
gui_surface.fill((r, g, b, a))
if self.fadein_is_fading:
r, g, b = self.fadein_fade_color
a = self.fadein_timer / self.fadein_fade_in_s * self.fadein_fade_opacity
gui_surface.fill((r, g, b, a))
self.window.blit(gui_surface, (0, 0))
# Conteur de FPS en mode DEBUG # Conteur de FPS en mode DEBUG
if self.engine.DEBUG_MODE: if self.engine.DEBUG_MODE:
self.window.blit(font.SysFont("Arial", 20).render(f"FPS: {round(self.engine.clock.get_fps())}", True, (255, 0, 0)), self.window.blit(font.SysFont("Arial", 20).render(f"FPS: {round(1/delta if delta else 1)}, Game Status: {'Paused' if self.engine.entity_manager.paused else 'Playing'}", True, (255, 0, 0)),
(0, 0)) (0, 0))
player = self.engine.entity_manager.get_by_name('player') player = self.engine.entity_manager.get_by_name('player')
self.window.blit(font.SysFont("Arial", 20).render(f"X: {round(player.x, 2)} Y:{round(player.y, 2)}", self.window.blit(font.SysFont("Arial", 20).render(f"X: {round(player.x, 2)} Y:{round(player.y, 2)}",
@ -129,9 +184,10 @@ class Renderer:
self.window.blit(font.SysFont("Arial", 20).render(f"Track: {self.engine.sound_manager.music_current_song}", self.window.blit(font.SysFont("Arial", 20).render(f"Track: {self.engine.sound_manager.music_current_song}",
True, (255, 0, 0)), (0, 120)) True, (255, 0, 0)), (0, 120))
window_size = display.get_window_size()
# On rend maintenant toutes les zones de détection de la fenêtre # On rend maintenant toutes les zones de détection de la fenêtre
for area in self.engine.event_handler.buttons_area: for area in self.engine.event_handler.buttons_area:
window_size = display.get_window_size()
if area[2] == 0: if area[2] == 0:
draw.rect(self.window, (255, 255, 0), draw.rect(self.window, (255, 255, 0),
(area[0][0] * window_size[0], area[0][1] * window_size[0], (area[0][0] * window_size[0], area[0][1] * window_size[0],
@ -148,6 +204,58 @@ class Renderer:
draw.rect(self.window, (255, 255, 0), draw.rect(self.window, (255, 255, 0),
area[0], width=1) area[0], width=1)
for area in self.engine.event_handler.sliders_area:
if area[1] == 0:
draw.rect(self.window, (0, 255, 20),
((area[0][0]-area[0][2]/2) * window_size[0], (area[0][1]-area[0][3]/2) * window_size[0],
area[0][2] * window_size[0], area[0][3] * window_size[0]), width=1)
draw.rect(self.window, (0, 255, 200),
(area[5][0] * window_size[0], area[5][1] * window_size[0],
area[5][2] * window_size[0], area[5][3] * window_size[0]), width=1)
draw.line(self.window, (255, 0, 0),
(area[0][0] * window_size[0] - 2, area[0][1] * window_size[0]),
(area[0][0] * window_size[0] + 2, area[0][1] * window_size[0]))
draw.line(self.window, (255, 0, 0),
(area[0][0] * window_size[0], area[0][1] * window_size[0] - 2),
(area[0][0] * window_size[0], area[0][1] * window_size[0] + 2))
elif area[1] == 1:
draw.rect(self.window, (0, 255, 20),
((area[0][0]-area[0][2]/2) * window_size[1], (area[0][1]-area[0][3]/2) * window_size[1],
area[0][2] * window_size[1], area[0][3] * window_size[1]), width=1)
draw.rect(self.window, (0, 255, 200),
(area[5][0] * window_size[1], area[5][1] * window_size[1],
area[5][2] * window_size[1], area[5][3] * window_size[1]), width=1)
draw.line(self.window, (255, 0, 0),
(area[0][0] * window_size[1] - 2, area[0][1] * window_size[1]),
(area[0][0] * window_size[1] + 2, area[0][1] * window_size[1]))
draw.line(self.window, (255, 0, 0),
(area[0][0] * window_size[1], area[0][1] * window_size[1] - 2),
(area[0][0] * window_size[1], area[0][1] * window_size[1] + 2))
elif area[1] == 2:
draw.rect(self.window, (0, 255, 20),
((area[0][0]-area[0][2]/2) * window_size[0], (area[0][1]-area[0][3]/2) * window_size[1],
area[0][2] * window_size[0], area[0][3] * window_size[1]), width=1)
draw.rect(self.window, (0, 255, 200),
(area[5][0] * window_size[0], area[5][1] * window_size[1],
area[5][2] * window_size[0], area[5][3] * window_size[1]), width=1)
draw.line(self.window, (255, 0, 0),
(area[0][0]*window_size[0] - 2, area[0][1]*window_size[1]),
(area[0][0]*window_size[0] + 2, area[0][1]*window_size[1]))
draw.line(self.window, (255, 0, 0),
(area[0][0]*window_size[0], area[0][1]*window_size[1] - 2),
(area[0][0]*window_size[0], area[0][1]*window_size[1] + 2))
else:
draw.rect(self.window, (0, 255, 20),
(area[0][0]-area[0][2]//2, area[0][1]-area[0][3]//2, area[0][2], area[0][3]), width=1)
draw.rect(self.window, (0, 255, 200),
area[5], width=1)
draw.line(self.window, (255, 0, 0),
(area[0][0]-2, area[0][1]),
(area[0][0]+2, area[0][1]))
draw.line(self.window, (255, 0, 0),
(area[0][0], area[0][1]-2),
(area[0][0], area[0][1]+2))
# Rendu présent dans tous les types de jeu # Rendu présent dans tous les types de jeu
self.render_dialogs_box() self.render_dialogs_box()
@ -236,6 +344,46 @@ class Renderer:
self.window.blit(btn_image, (x, y)) self.window.blit(btn_image, (x, y))
self.window.blit(rendered_text, (x, y)) self.window.blit(rendered_text, (x, y))
elif isinstance(widget, Slider):
if widget.hovered:
slider_image = widget.hover_image
else:
slider_image = widget.base_image
rail_image = widget.rail_image
if widget.is_window_relative == 0:
slider_image = transform.scale(slider_image,
(slider_image.get_width()*window_size[0]/self.window_size[0],
slider_image.get_height()*window_size[0]/self.window_size[0]))
rail_image = transform.scale(rail_image,
(rail_image.get_width() * window_size[0] / self.window_size[0],
rail_image.get_height() * window_size[0] / self.window_size[
0]))
width = widget.width*window_size[0]
elif widget.is_window_relative == 1:
slider_image = transform.scale(slider_image,
(slider_image.get_width()*window_size[1]/self.window_size[1],
slider_image.get_height()*window_size[1]/self.window_size[1]))
rail_image = transform.scale(rail_image,
(rail_image.get_width() * window_size[1] / self.window_size[1],
rail_image.get_height() * window_size[1] / self.window_size[
1]))
width = widget.width * window_size[1]
elif widget.is_window_relative == 2:
slider_image = transform.scale(slider_image,
(slider_image.get_width()*window_size[0]/self.window_size[0],
slider_image.get_height()*window_size[1]/self.window_size[1]))
rail_image = transform.scale(rail_image,
(rail_image.get_width() * window_size[0] / self.window_size[0],
rail_image.get_height() * window_size[1] / self.window_size[
1]))
width = widget.width * min(window_size[0], window_size[1])
self.window.blit(rail_image, (x+(width-rail_image.get_width()) // 2,
y - rail_image.get_height() // 2))
self.window.blit(slider_image, (x+widget.value*width-slider_image.get_width()//2,
y-slider_image.get_height()//2))
def render_dialogs_box(self): def render_dialogs_box(self):
"""Rend la boite de dialogue lorsqu'un dialogue est lancé.""" """Rend la boite de dialogue lorsqu'un dialogue est lancé."""
@ -332,8 +480,8 @@ class Renderer:
draw.rect(rendered_surface, part[7], part_dest + (part[2], part[2])) draw.rect(rendered_surface, part[7], part_dest + (part[2], part[2]))
part[5] += delta part[5] += delta
part[0] += part[3] part[0] += part[3]*delta
part[1] += part[4] part[1] += part[4]*delta
if part[5] > part[6]: if part[5] > part[6]:
self.particles.remove(part) self.particles.remove(part)
@ -488,3 +636,27 @@ class Renderer:
(math.floor(x * self.tile_size - self.engine.camera.x + x_middle_offset), (math.floor(x * self.tile_size - self.engine.camera.x + x_middle_offset),
math.floor(y * self.tile_size - self.engine.camera.y + y_middle_offset), math.floor(y * self.tile_size - self.engine.camera.y + y_middle_offset),
self.tile_size, self.tile_size), width=1) self.tile_size, self.tile_size), width=1)
def fadeout(self, fade_s: float, fade_color: tuple[int, int, int] = (0, 0, 0), fade_opacity: int = 100, pause_world: bool = True, callback: FunctionType = None):
"""Fait un fondu vers la couleur au format : (255, 255, 255) et a l'opacité max spécifié, et dans le temps spécifié, appelle la fonction callback une fois le fadout terminé"""
self.fadein_is_fading = False
self.fadeout_timer = fade_s
self.fadeout_fade_in_s = fade_s
self.fadeout_is_fading = True
self.fadeout_fade_color = fade_color
self.fadeout_fade_opacity = round(fade_opacity * 255 / 100)
self.fadeout_pause = pause_world
self.fadeout_fade_callback = callback
self.engine.entity_manager.pause()
def fadein(self, fade_s: float, fade_color: tuple[int, int, int] = (0, 0, 0), fade_opacity: int = 100, pause_world: bool = True, callback: FunctionType = None):
"""Fait un fondu depuis la couleur au format : (255, 255, 255) et depuis l'opacité spécifié, et dans le temps spécifié, appelle la fonction callback une fois le fadout terminé"""
self.fadeout_is_fading = False
self.fadein_timer = fade_s
self.fadein_fade_in_s = fade_s
self.fadein_is_fading = True
self.fadein_fade_color = fade_color
self.fadein_fade_opacity = round(fade_opacity * 255 / 100)
self.fadein_pause = pause_world
self.fadein_fade_callback = callback
self.engine.entity_manager.pause()

View file

@ -33,17 +33,26 @@ class Game(Engine):
def start_game(self): def start_game(self):
self.game_state = GameState.NORMAL self.game_state = GameState.NORMAL
self.renderer.fadein(1, (0, 0, 0), 100, True)
def play_button_callback(self):
self.renderer.fadeout(1, (0, 0, 0), 100, True, self.start_game)
self.menu_manager.hide() self.menu_manager.hide()
def setup_main_menu(self): def setup_main_menu(self):
"""Crée les éléments du menu principal.""" """Crée les éléments du menu principal."""
menu = Menu() menu = Menu()
menu.add_widget(Label(0.5, 0.1, "The Forest's Secret", 0.1, (0, 0, 0), True, 0)) menu.add_widget(Label(0.5, 0.1, "The Forest's Secret", 0.1, (0, 0, 0), "game_title", True, 0))
base_image = pygame.image.load("assets/textures/GUI/button_1.png").convert_alpha() btn_base_image = pygame.image.load("assets/textures/GUI/button_1.png").convert_alpha()
hover_image = pygame.image.load("assets/textures/GUI/button_2.png").convert_alpha() btn_hover_image = pygame.image.load("assets/textures/GUI/button_2.png").convert_alpha()
slider_base_image = pygame.image.load("assets/textures/GUI/slider_cursor_1.png").convert_alpha()
slider_hover_image = pygame.image.load("assets/textures/GUI/slider_cursor_2.png").convert_alpha()
slider_rail_image = pygame.image.load("assets/textures/GUI/slider_rail_1.png").convert_alpha()
menu.add_widget(Button(0.5, 0.3, "play", 0.08, (0, 0, 0), self.play_button_callback, btn_base_image, btn_hover_image, "play_button", True, 0))
menu.add_widget(Button(0.5, 0.3, "play", 0.08, (0, 0, 0), self.start_game, base_image, hover_image, True, 0))
self.menu_manager.register_menu(menu, "main") self.menu_manager.register_menu(menu, "main")
self.menu_manager.show("main") self.menu_manager.show("main")
@ -63,7 +72,7 @@ class Game(Engine):
player.collision_rect = [-6, -7, 6, 16] player.collision_rect = [-6, -7, 6, 16]
player.set_default_life(15) player.set_default_life(15)
player.max_speed = 1.1 player.max_speed = 64.0
self.entity_manager.set_player_entity("player") self.entity_manager.set_player_entity("player")
@ -86,7 +95,7 @@ class Game(Engine):
mob.collision_rect = [-15, -7, 12, 7] mob.collision_rect = [-15, -7, 12, 7]
mob.set_default_life(5) mob.set_default_life(5)
mob.max_speed = 1. mob.max_speed = 60.
mob.x, mob.y = 1600, 16 mob.x, mob.y = 1600, 16