Compare commits

...

53 commits

Author SHA1 Message Date
Adastram 3a53f2a8ba Added title image 2024-01-16 10:05:28 +01:00
Adastram 71725cdaef Merge branch 'game_assembly' of https://git.tipragot.fr/rfg/nsi-rpg into Merge-in-game_assembly-(NO-MORRE-TIME) 2024-01-16 07:53:33 +01:00
Adastram 95e9b4af00 Format Debug info 2024-01-16 07:35:25 +01:00
Adastram 277c8c4132 Added fullscreen option 2024-01-16 07:32:24 +01:00
Adastram d0c98925e2 Fixed fps cap and latency system 2024-01-15 14:56:34 +01:00
Adastram 779ac9194b Fix du crash quand on ferme le jeu 2024-01-14 23:51:26 +01:00
Adastram 937fc88297 Ajout du controle des fps et d'un système de contrebalance de latence 2024-01-14 23:51:07 +01:00
Adastram 0f5f6cc21c Reduced move of element caused by pos reducing and expanding from time to time using format to force 3 decimals 2024-01-14 22:09:51 +01:00
Adastram 24b6090385 Updated OptionMenu based on main 2024-01-14 17:57:36 +01:00
Adastram 41e2bdb3d6 Fixed base music volume 2024-01-14 17:53:09 +01:00
Adastram 02df5fd97e Imported OST in Option Menu 2024-01-14 17:52:47 +01:00
Yannis 7ff96fe5c9 Retrait de la grande vitesse du joueur pour les tests 2024-01-13 23:33:27 +01:00
Yannis b65fb9d127 Fix du crash lors de la mort du joueur 2024-01-13 23:30:27 +01:00
Yannis 28df17050f Update du pdn tileset 2024-01-13 23:17:02 +01:00
Yannis af05762706 Retrait des modifications sur le joueur 2024-01-13 23:16:38 +01:00
Yannis e043093748 Ajout des entrées de temples 2024-01-13 23:15:08 +01:00
Yannis 28a4cf1db0 Nettoyage du code 2024-01-13 22:57:07 +01:00
Yannis 29adfda8dd Fix dans la map 2024-01-13 22:50:21 +01:00
Yannis 989587379a Fix des problèmes lors du merge 2024-01-13 22:19:42 +01:00
Yannis 323e190474 Merge remote-tracking branch 'origin/main' into game_assembly
# Conflicts:
#	src/main.py
2024-01-13 21:42:10 +01:00
Adastram 3f0e4d0aaa Added debug ost in gitignore to prevent from import again 2024-01-12 23:46:41 +01:00
Adastram b59290118e Removed wrong OST files 2024-01-12 23:45:28 +01:00
Adastram 9effb87332 Started debugging sound control 2024-01-10 11:15:34 +01:00
Adastram ec12e2dc17 Moved default settings to engine 2024-01-10 10:49:36 +01:00
Adastram 12aff5f73f Removed zoom control from key while not in debug mode 2024-01-10 10:46:07 +01:00
Adastram 0502f1d278 Added zoom control from settings_manager (debug mode still works) 2024-01-10 10:42:20 +01:00
Adastram 988500af43 Added sound volume control in engine 2024-01-10 10:31:58 +01:00
Adastram f95ff4834a Merge pull request 'MergeToOptionMenu' (#26) from MergeToOptionMenu into OptionMenu
Reviewed-on: #26
2024-01-10 09:09:44 +00:00
Adastram ef64ce324e Added SoundManager latest to start building SettingsMenu 2024-01-10 10:07:25 +01:00
Adastram e34c7b8b8a Merge branch 'OptionMenu' into MergeToOptionMenu 2024-01-10 10:06:36 +01:00
Adastram a86d89c563 Ajout du support du réglage de son dans le sound_manager 2024-01-09 23:12:28 +01:00
Adastram c625e15d34 Added global sound volume managing 2024-01-09 21:24:37 +01:00
Adastram 77b41aee66 Temp 2024-01-09 17:04:38 +01:00
Adastram 75391e5876 Synced main with sound_manager manager 2024-01-09 16:15:26 +01:00
Adastram fd7434993d Added local sound stop 2024-01-09 15:56:54 +01:00
Adastram 8b0dddea1a Added better dict tipage 2024-01-09 14:10:34 +01:00
Yannis 4a613ccfb6 Merge remote-tracking branch 'origin/game_assembly' into game_assembly 2024-01-09 12:00:57 +01:00
Yannis 8d12c791d1 Ajout des temples 2024-01-09 12:00:24 +01:00
Adastram 2146cacc48 Implemented single global sound stop 2024-01-09 11:59:23 +01:00
Adastram 5b2420dfae Added support of local sound and volume based on distance from emiter 2024-01-09 11:40:39 +01:00
Adastram 490b5c46ba Added Shuffle and Next Function fro music 2024-01-09 11:27:11 +01:00
Adastram b2a1311839 Merge branch 'game_assembly' of https://gitea.tipragot.fr/rfg/nsi-rpg into game_assembly 2024-01-09 11:20:56 +01:00
Adastram 37db2829a7 Added shuffle function for background music 2024-01-09 11:20:47 +01:00
Yannis 347febaf11 Fix du spam de la touche o 2024-01-09 10:21:27 +01:00
Adastram 0a659dcbe0 Removed Debug prints 2024-01-09 10:03:19 +01:00
Adastram 7c6031a713 FIXED SOUND MANAGER AND MAIN 2024-01-09 08:02:33 +01:00
Adastram df86633f8e Added main OST and auto_audio_start, WORKING VERSION 2024-01-09 08:01:52 +01:00
Adastram b9a81ba12e Started Gloval sound handling 2024-01-08 23:27:45 +01:00
Yannis 6219dcdb71 Réglage du joueur 2024-01-08 21:17:43 +01:00
Yannis b3663ed81b Fix collisions map 5 2024-01-08 21:03:48 +01:00
Yannis 04e51bd376 Update de la map 5 2024-01-08 20:55:52 +01:00
Yannis 75ce3b6c37 Update de la map 5 2024-01-08 19:12:26 +01:00
Adastram f454bfff6d Merge branch 'SoundManager' (Musique de fond) 2024-01-08 10:57:02 +01:00
19 changed files with 5471 additions and 199 deletions

6
.gitignore vendored
View file

@ -160,3 +160,9 @@ cython_debug/
# option (not recommended) you can uncomment the following to ignore the entire idea folder.
.idea/
# DEBUG OST
assets/OST/Hyrule Field - The Legend of Zelda Ocarina of Time.mp3
assets/OST/Lost Woods - The Legend of Zelda Ocarina of Time.mp3
assets/OST/Title Theme - The Legend of Zelda Ocarina of Time.mp3
assets/OST/Vampire Killer.mp3
assets/sounds/Vampire Killer.mp3

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 303 KiB

BIN
assets/textures/temples.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 245 KiB

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

After

Width:  |  Height:  |  Size: 210 KiB

File diff suppressed because it is too large Load diff

View file

@ -2,10 +2,12 @@ from src.engine.entity import Entity
class Camera:
def __init__(self):
def __init__(self, DEBUG_MODE: bool, zoom: float):
self.DEBUG_MODE = DEBUG_MODE
self.x = 0
self.y = 0
self.zoom = 1.75
self.zoom = zoom
# Décalage lors du mouvement du joueur
self.player_moving_offset = 100
@ -19,9 +21,12 @@ class Camera:
self.followed_entity: Entity | None = None
def update(self, delta: float):
def update(self, delta: float, zoom: float):
"""Met à jour la caméra. Permet, par exemple, de faire le scrolling."""
if not self.DEBUG_MODE:
self.zoom = zoom
# Si on suit une entité, on met à jour les coordonnées de suivi
if self.followed_entity is not None:
self.target_x = (self.followed_entity.x + self.followed_entity.mouvements[0] *

View file

@ -11,6 +11,7 @@ from src.engine.menu_manager import MenuManager
from src.engine.renderer import Renderer
from src.engine.enums import GameState
from src.engine.sound_manager import SoundManager
from src.engine.settings_manager import SettingsManager
import pygame
@ -29,41 +30,88 @@ class Engine:
self.running = False
# Composants du moteur de jeu
self.settings_manager = SettingsManager(60, 1.75) # DOIT ABSOLUMENT ETRE EN PREMIER (Sinon les autres composants qui nécessite les settings crash)
self.renderer = Renderer(self)
self.event_handler = EventHandler(self)
self.map_manager = MapManager()
self.camera = Camera()
self.camera = Camera(self.DEBUG_MODE, self.settings_manager.get_zoom())
self.entity_manager = EntityManager(self.map_manager)
self.boss_fight_manager = BossFightManager(self)
self.event_sheduler = EventSheduler(self)
self.dialogs_manager = DialogsManager(self)
self.menu_manager = MenuManager(self)
self.sound_manager = SoundManager(60)
self.sound_manager = SoundManager(self.settings_manager.get_music_master_volume(),
self.settings_manager.get_sound_global_master_volume(),
self.settings_manager.get_sound_master_volume())
self.global_latency = 0
self.last_latency = []
self.latency_precision = self.settings_manager.latency_precision
def loop(self):
"""Fonction à lancer au début du programme et qui va lancer les updates dans une boucle.
Attend jusqu'à la fin du jeu."""
self.running = True
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
# Initialisation ddes valeurs de delta et de last_time
delta = 1. # Le delta est le temps depuis la dernière image
last_time = time.time()
while self.running:
refresh_rate = self.settings_manager.get_refresh_rate()
if refresh_rate == -1: # Pas de limite, vers l'infini et l'au-delà !!!
self.update(delta)
new_time = time.time()
delta = new_time - last_time
last_time = new_time
else:
while time.time() < last_time + 1 / refresh_rate - self.global_latency:
pass
new_time = time.time()
delta = new_time-last_time
last_time = new_time
self.update(delta)
new_refresh_rate = self.settings_manager.get_refresh_rate()
if refresh_rate != new_refresh_rate:
refresh_rate = new_refresh_rate
self.global_latency = 0
self.last_latency = []
latency = 0
latency = delta - 1/refresh_rate
if not latency > self.global_latency * 100 or self.global_latency == 0: # Impossible que le jeu prenne autant de retard, on skip cette latence dans le calcul, l'utilisateur a surement cliquer hors de la fenêtre
if len(self.last_latency) < self.latency_precision:
self.last_latency.append(latency)
else:
self.last_latency.pop(0)
self.last_latency.append(latency)
n = 0
for i in self.last_latency:
n += i
self.global_latency = n/len(self.last_latency)
def update(self, delta: float):
"""Fonction qui regroupe toutes les updates des composants. Elle permet de mettre à jour le jeu quand on
l'appelle."""
self.camera.update(delta)
self.camera.update(delta, self.settings_manager.get_zoom())
self.entity_manager.update(delta)
self.renderer.update(delta)
self.event_handler.update(delta)
self.event_sheduler.update()
self.dialogs_manager.update(delta)
self.sound_manager.update(delta)
self.sound_manager.update(delta, self.settings_manager.get_music_master_volume(),
self.settings_manager.get_sound_global_master_volume(),
self.settings_manager.get_sound_master_volume())
def stop(self):
"""Arrête le programme."""

View file

@ -1,5 +1,6 @@
import math
from src.engine.enums import EntityDeathResult
from src.engine.map_manager import MapManager
from src.engine.mobs_AI import MobAI
@ -42,6 +43,9 @@ class Entity:
self.shadow = None
self.death_callback = None
self.death_result = EntityDeathResult.REMOVED
def set_default_life(self, life: int):
"""Définit le nombre de PV de l'entité. Mettre -1 pour rendre l'entité immortelle."""
self.life_points = life

View file

@ -1,4 +1,5 @@
from src.engine.entity import Entity
from src.engine.enums import EntityDeathResult
from src.engine.map_manager import MapManager
@ -29,10 +30,16 @@ class EntityManager:
def update(self, delta: float):
"""Met à jour toutes les entités enregistrées."""
for entity_name in list(self.entities.keys()):
entity = self.entities[entity_name]
entity: Entity = self.entities[entity_name]
entity.update(delta)
if entity.life_points == 0:
if entity.death_callback is not None:
entity.death_callback()
if entity.death_result == EntityDeathResult.REMOVED:
self.entities.pop(entity_name)
elif entity.death_result == EntityDeathResult.RESET_LIFE:
entity.life_points = entity.max_life_points
if entity.brain is not None and not self.paused:
entity.brain.update(delta)

View file

@ -8,3 +8,8 @@ class GameState(Enum):
BOSS_FIGHT = 2
MAIN_MENU = 3
# AJouter si besoin, mais à utiliser de préférence avec parsimony
class EntityDeathResult(Enum):
REMOVED = 0 # The entity is removed
RESET_LIFE = 1

View file

@ -225,9 +225,9 @@ class EventHandler:
if K_o in self.key_pressed:
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}")
self.key_pressed.remove(K_o)
if K_x in self.key_pressed:
self.engine.camera.target_zoom *= 1.01
self.engine.settings_manager.zoom *= 1.01
if K_c in self.key_pressed:
self.engine.camera.target_zoom *= 0.99
self.engine.settings_manager.zoom *= 0.99

View file

@ -89,6 +89,15 @@ class Button(Widget):
"""Modifie la valeur du hover."""
self.hovered = state
class Image(Widget):
"""Un widget d'image."""
def __init__(self, x: int | float, y: int | float, size: int | float, image_path: str, widget_name: str,
centered: bool = False, is_window_relative: int = -1):
super().__init__(x, y, is_window_relative, widget_name)
self.size = size
self.image = pygame.image.load(image_path)
self.centered = centered
class Menu:
"""Un menu contenant des widgets."""

View file

@ -9,7 +9,7 @@ from pygame.locals import RESIZABLE, SRCALPHA, FULLSCREEN
import src.engine.engine as engine
from src.engine.animation import Anim
from src.engine.enums import GameState
from src.engine.menu_manager import Label, Button, Slider
from src.engine.menu_manager import Label, Button, Slider, Image
class Renderer:
@ -17,11 +17,13 @@ class Renderer:
def __init__(self, core: 'engine.Engine'):
self.engine = core
self.fullscreen_size = display.Info().current_w, display.Info().current_h
self.timer = 0 # Timer local
self.window_type = RESIZABLE
self.window_size = (display.Info().current_w, display.Info().current_h) if self.window_type == FULLSCREEN else (
self.window_size = self.fullscreen_size if self.window_type == FULLSCREEN else (
600, 600)
self.window = display.set_mode(self.window_size, self.window_type)
self.tiles = []
self.tile_size = 0
self.animations: dict[str: Anim] = {}
@ -175,11 +177,11 @@ class Renderer:
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))
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):.2f} Y:{round(player.y, 2):.2f}",
True, (255, 0, 0)), (0, 30))
self.window.blit(font.SysFont("Arial", 20).render(f"Zoom: {round(self.engine.camera.zoom, 2)}",
self.window.blit(font.SysFont("Arial", 20).render(f"Zoom: {round(self.engine.settings_manager.get_zoom(), 2)}",
True, (255, 0, 0)), (0, 60))
self.window.blit(font.SysFont("Arial", 20).render(f"Volume: {self.engine.sound_manager.music_get_volume()}, Pos: {self.engine.sound_manager.music_get_current_song_pos()}s, Index: {self.engine.sound_manager.music_current_index}, Paused: {self.engine.sound_manager.music_is_paused}",
self.window.blit(font.SysFont("Arial", 20).render(f"Volume: {self.engine.sound_manager.music_get_volume()}, Pos: {self.engine.sound_manager.music_get_current_song_pos():.3f}s, Index: {self.engine.sound_manager.music_current_index}, Paused: {self.engine.sound_manager.music_is_paused}",
True, (255, 0, 0)), (0, 90))
self.window.blit(font.SysFont("Arial", 20).render(f"Track: {self.engine.sound_manager.music_current_song}",
True, (255, 0, 0)), (0, 120))
@ -385,6 +387,36 @@ class Renderer:
self.window.blit(slider_image, (x+widget.value*width-slider_image.get_width()//2,
y-slider_image.get_height()//2))
elif isinstance(widget, Image):
if widget.is_window_relative == 0:
size = widget.size*window_size[0]
elif widget.is_window_relative == 1:
size = widget.size*window_size[1]
elif widget.is_window_relative == 2:
size = widget.size*min(window_size[0], window_size[1])
else:
size = widget.size
image = widget.image
if widget.is_window_relative == 0:
image = transform.scale(image, (image.get_width()*window_size[0]/self.window_size[0],
image.get_height()*window_size[0]/self.window_size[0]))
elif widget.is_window_relative == 1:
image = transform.scale(image, (image.get_width()*window_size[1]/self.window_size[1],
image.get_height()*window_size[1]/self.window_size[1]))
elif widget.is_window_relative == 2:
image = transform.scale(image, (image.get_width()*window_size[0]/self.window_size[0],
image.get_height()*window_size[1]/self.window_size[1]))
# On affiche l'image
if widget.centered:
self.window.blit(image, (x-image.get_width()//2,
y-image.get_height()//2))
else:
self.window.blit(image, (x, y))
def render_dialogs_box(self):
"""Rend la boite de dialogue lorsqu'un dialogue est lancé."""
@ -660,3 +692,11 @@ class Renderer:
self.fadein_pause = pause_world
self.fadein_fade_callback = callback
self.engine.entity_manager.pause()
def set_display(self, window_type: FULLSCREEN | RESIZABLE, size: tuple[int, int] = None):
self.window_type = window_type
self.window_size = self.fullscreen_size if self.window_type == FULLSCREEN else (
size[0], size[1])
self.window = display.set_mode(self.window_size, self.window_type)
display.flip()

View file

@ -0,0 +1,27 @@
class SettingsManager:
def __init__(self, default_master_volume: float, default_zoom: float) -> None:
self.refresh_rate = 30
self.latency_precision = 100 # Nombre de valeurs de latence stocké (Pour faire la moyenne)
self.master_volume = default_master_volume
self.sound_master_volume = 100
self.music_master_volume = 100
self.sound_global_master_volume = 100
self.zoom = default_zoom
def get_refresh_rate(self):
return self.refresh_rate
def get_zoom(self):
return self.zoom
def get_music_master_volume(self):
return round(self.master_volume / 100 * self.music_master_volume, 3)
def get_sound_global_master_volume(self):
return round(self.master_volume / 100 * self.sound_global_master_volume, 3)
def get_sound_master_volume(self):
return round(self.master_volume / 100 * self.sound_master_volume, 3)

View file

@ -1,52 +1,121 @@
from pygame import mixer
from src.engine.entity import Entity
from random import randint
from pygame import mixer, error
from math import sqrt
from time import time
class SoundManager:
def __init__(self, music_base_volume: float):
self.__tick = 0 # Compteur de la valeur d'un tick sur 1 (Utilisé pour le comptage de tick)
def __init__(self, music_master_volume: float, sound_global_master_volume: float, sound_master_volume: float):
self.__tick = 0 # Compteur de la valeur d'un tick (Utilisé pour le comptage de tick)
self.tick = 0 # Compteur de tick
self.time = 0 # Temps local a la class (en s)
self.music_master_volume = music_master_volume
self.sound_global_master_volume = sound_global_master_volume
self.sound_master_volume = sound_master_volume
self.music_playlist = []
self.music_current_song = ""
self.music_play_playlist = False
self.music_current_index = 0
self.music_shuffle_playlist = True
self.music_next_request = False
self.music_set_volume(100)
self.music_before_pause_pos = 0
self.music_before_pause_song = ""
self.music_is_paused = False
self.music_pos_delay = 0
self.music_set_volume(music_base_volume)
self.sound_currently_playing: dict[float: list[mixer.Sound, float, list[float, float], float]] = {} # Format {unique_id : [Sound, max_volume, [pos_x, pos_y], stop_at]}
self.sound_loaded: dict[str: mixer.Sound] = {}# Format : {name: mixer.Sound}
self.sound_global_currently_playing: dict[float: list[mixer.Sound, float, float]] = {} # Format {unique_id: [Sound, volume, stop_at]}
def update(self, delta: float):
self.sound_hears_anchor = None
def update(self, delta: float, music_master_volume: float, sound_global_master_volume: float, sound_master_volume: float):
self.__tick += delta
self.tick = int(self.__tick / delta)
self.time = self.tick * delta
self.music_master_volume = music_master_volume
self.sound_global_master_volume = sound_global_master_volume
self.sound_master_volume = sound_master_volume
if self.sound_hears_anchor: # Update la position des "Oreilles" du joueur (Ou de l'entité séléctionné comme ancre pour les oreilles)
self.sound_hears_x = self.sound_hears_anchor.x
self.sound_hears_y = self.sound_hears_anchor.y
for key in self.sound_global_currently_playing.keys(): # Son globaux
sound_container: list[mixer.Sound, float, float] = self.sound_global_currently_playing[key]
if sound_container[2] > self.time:
self.sound_global_currently_playing.pop(key)
else:
sound_container[0].set_volume(round(sound_global_master_volume / 100 * sound_container[1] / 100, 3))
for key in self.sound_currently_playing.keys(): # Son locaux
sound_container: list[mixer.Sound, float, list[float, float], float] = self.sound_currently_playing[key]
if sound_container[3] > self.time: # Timeout des sons
self.sound_currently_playing.pop(key)
else: # Gère le volume en fonction de la position
sound = sound_container[0]
max_volume = sound_container[1]
pos_x, pos_y = sound_container[2]
sound.set_volume(max(0, int((round(sound_master_volume / 100 * max_volume / 100, 3)) - sqrt((pos_x - self.sound_hears_x) ** 2 + (pos_y - self.sound_hears_y) ** 2))) / (round(sound_master_volume / 100 * max_volume / 100, 3)))
if self.music_play_playlist and not self.music_is_paused: # Musique de fond
if not mixer.music.get_busy():
try:
if not mixer.music.get_busy() or self.music_next_request:
if self.music_next_request:
self.music_next_request = False
mixer.music.fadeout(1)
if len(self.music_playlist) == 0:
pass
elif self.music_current_song == "":
self.__music_play(self.music_playlist[0])
else:
if self.music_current_song in self.music_playlist:
just_played_index = self.music_playlist.index(self.music_current_song)
if len(self.music_playlist) - 1 <= just_played_index: # Dernier son de la playlist / la playlist a rétréci entre temps
if self.music_shuffle_playlist and len(self.music_playlist) != 1:
while True:
new_index = randint(0, len(self.music_playlist) - 1)
if new_index != just_played_index:
break
self.music_current_index = new_index
self.__music_play(self.music_playlist[new_index])
elif len(self.music_playlist) - 1 <= just_played_index: # Dernier son de la playlist / la playlist a rétréci entre temps
self.music_current_index = 0
self.__music_play(self.music_playlist[0]) # Recommence depuis le début de la playlist
else:
self.music_current_index = just_played_index + 1
self.__music_play(self.music_playlist[self.music_current_index]) # Joue la musique suivante dans la playlist
else: # Song removed from playlist, no idea what was the index, starting again from start or from random index if playlist_shuffle = True
new_index = randint(0, len(self.music_playlist) - 1)
self.music_current_index = new_index
self.__music_play(self.music_playlist[new_index])
except error:
pass
def music_get_volume(self):
return mixer.music.get_volume() * 100
def music_set_volume(self, new_volume: float):
"""Définit le nouveau volume de la musique"""
mixer.music.set_volume((round(new_volume / 100, 3)))
mixer.music.set_volume(round(self.music_master_volume / 100 * new_volume / 100, 3))
def music_pause(self, fade_s: float, restart_tolerance: float = 33):
"""Met en pause la musique, la musique reprendra à la fin de la musique moin la tolérance (en pourcentage)"""
@ -61,14 +130,12 @@ class SoundManager:
self.music_before_pause_pos = 0
self.music_before_pause_song = ""
def music_get_current_song_pos(self):
if mixer.music.get_busy():
return round(mixer.music.get_pos() /1000 + self.music_pos_delay, 3)
return round(mixer.music.get_pos() / 1000 + self.music_pos_delay, 3)
else:
return round(self.music_before_pause_pos, 3)
def __music_play(self, song: str, fade_s: float = 0, start_at: float = 0):
mixer.music.unload()
mixer.music.load(song)
@ -84,7 +151,7 @@ class SoundManager:
def music_remove_from_playlist(self, song_path: str = None, index: int = None):
if song_path:
index = self.music_playlist.index(song_path)
if index:
if index != None:
self.music_playlist.pop(index)
def music_start_playlist(self):
@ -92,3 +159,65 @@ class SoundManager:
def music_stop_playlist(self):
self.music_play_playlist = False
def music_playlist_set_shuffle(self, shuffle: bool):
self.music_shuffle_playlist = shuffle
def music_next(self):
self.music_next_request = True
def sound_link_hears(self, entity: Entity):
self.sound_hears_anchor = entity
def create_unique_id(self):
return time()*10e99999
def sound_load(self, file_path: str, name: str):
self.sound_loaded[name] = mixer.Sound(file_path)
def sound_play(self, name: str, max_volume: float, pos_x: float, pos_y: float):
sound = self.sound_loaded[name]
stop_at = stop_at = self.time + sound.get_length()
unique_id = self.create_unique_id()
self.sound_currently_playing[unique_id] = [sound, max_volume, [pos_x, pos_y], stop_at] # Format {unique_id : [Sound, max_volume, [pos_x, pos_y], stop_at]
return unique_id
def sound_stop(self, name: str, unique_id: float = None, all: bool = False):
if all:
for key in self.sound_currently_playing.keys():
self.sound_currently_playing.pop(key)[0].stop()
elif unique_id:
sound_container = self.sound_currently_playing.get(unique_id, None)
if sound_container:
self.sound_currently_playing.pop(unique_id)[0].stop()
else:
for key in self.sound_currently_playing.keys():
if self.sound_loaded[name] == self.sound_currently_playing[key][0]:
self.sound_currently_playing.pop(key)[0].stop()
def sound_global_play(self, name: str, volume: float):
"""Joue un son avec le même son dans tout le monde"""
sound = self.sound_loaded[name]
stop_at = self.time + sound.get_length()
unique_id = self.create_unique_id()
self.sound_global_currently_playing[unique_id] = [sound, volume, stop_at]
sound.play()
return unique_id
def sound_global_stop(self, name: str, unique_id: float = None, all: bool = False):
if all:
for key in self.sound_global_currently_playing.keys():
self.sound_global_currently_playing.pop(key)[0].stop()
elif unique_id:
sound_container = self.sound_global_currently_playing.get(unique_id, None)
if sound_container:
self.sound_global_currently_playing.pop(unique_id)[0].stop()
else:
for key in self.sound_global_currently_playing.keys():
if self.sound_loaded[name] == self.sound_global_currently_playing[key][0]:
self.sound_global_currently_playing.pop(key)[0].stop()

View file

@ -3,9 +3,9 @@ import pygame.image
from src.custom_AI import WolfAI
from src.engine.animation import Anim
from src.engine.engine import Engine
from src.engine.enums import GameState
from src.engine.menu_manager import Menu, Label, Button
from src.engine.enums import GameState, EntityDeathResult
from src.engine.menu_manager import Menu, Label, Button, Image
from pygame.locals import RESIZABLE, FULLSCREEN
class Game(Engine):
def __init__(self):
@ -29,6 +29,10 @@ class Game(Engine):
self.event_handler.register_button_area((0, 0, 0.1, 0.1), lambda : print("salut"), 0)
self.sound_manager.music_add_to_playlist(".\\assets\\OST\\Main Title (Y'as pas de boss la donc jpp le mettre pour un fight).mp3")
self.sound_manager.music_start_playlist()
self.setup_main_menu()
def start_game(self):
@ -39,10 +43,14 @@ class Game(Engine):
self.renderer.fadeout(1, (0, 0, 0), 100, True, self.start_game)
self.menu_manager.hide()
self.sound_manager.music_remove_from_playlist(".\\assets\\OST\\Main Title (Y'as pas de boss la donc jpp le mettre pour un fight).mp3")
self.sound_manager.music_add_to_playlist(".\\assets\\OST\\Bruit de foret pour yannis.mp3")
self.sound_manager.music_next()
def setup_main_menu(self):
"""Crée les éléments du menu principal."""
menu = Menu()
menu.add_widget(Label(0.5, 0.1, "The Forest's Secret", 0.1, (0, 0, 0), "game_title", True, 0))
menu.add_widget(Image(0, 0, 1, ".\\assets\\textures\\Title_Screen.png", "title_screen_image", False, 2))
btn_base_image = pygame.image.load("assets/textures/GUI/button_1.png").convert_alpha()
btn_hover_image = pygame.image.load("assets/textures/GUI/button_2.png").convert_alpha()
@ -51,14 +59,42 @@ class Game(Engine):
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.4, "Play", 0.08, (0, 0, 0), self.play_button_callback, btn_base_image, btn_hover_image, "play_button", True, 0))
self.menu_manager.register_menu(menu, "main")
self.menu_manager.show("main")
self.create_boss_temple_area()
def setup_settings_menu(self):
"""Crée les éléments du menu de paramètre"""
menu = Menu()
menu.add_widget(Label(0, 0.3, "Paramètres", 0.05, (192,192,192), True, 0))
#base_image = pygame.image.load("assets\\textures\\GUI\\setting_menu.png")
#hover_image = pygame.image.load("assets\\textures\\GUI\\setting_menu_hovered.png")
def create_boss_temple_area(self):
"""Enregistre les zones d'entrées de boss fight."""
self.event_sheduler.register_area((3104, 608, 48, 16), lambda _: print("temple 1"), ["player"], True)
self.event_sheduler.register_area((4544, 592, 48, 16), lambda _: print("temple 2"), ["player"], True)
self.event_sheduler.register_area((5664, 688, 32, 16), lambda _: print("temple 3"), ["player"], True)
self.event_sheduler.register_area((6720, 720, 16, 32), lambda _: print("temple 4"), ["player"], True)
self.create_boss_temple_area()
def create_boss_temple_area(self):
"""Enregistre les zones d'entrées de boss fight."""
self.event_sheduler.register_area((3104, 608, 48, 16), lambda _: print("temple 1"), ["player"], True)
self.event_sheduler.register_area((4544, 592, 48, 16), lambda _: print("temple 2"), ["player"], True)
self.event_sheduler.register_area((5664, 688, 32, 16), lambda _: print("temple 3"), ["player"], True)
self.event_sheduler.register_area((6720, 720, 16, 32), lambda _: print("temple 4"), ["player"], True)
def create_player_entity(self):
"""Crée une entité joueur."""
# On crée les animations
anim = Anim(0.5)
anim.load_animation_from_directory("assets/textures/entities/player/none")
self.renderer.register_animation(anim, "player_none")
@ -67,18 +103,33 @@ class Game(Engine):
anim.load_animation_from_directory("assets/textures/entities/player/walking")
self.renderer.register_animation(anim, "player_walking")
# On crée l'entité
player = self.entity_manager.register_entity("player")
player.link_animation("player_none")
player.collision_rect = [-6, -7, 6, 16]
player.set_default_life(15)
player.max_speed = 64.0
player.death_result = EntityDeathResult.RESET_LIFE
player.death_callback = self.create_player_entity
self.entity_manager.set_player_entity("player")
player.shadow = "player_shadow"
self.renderer.register_shadow("assets/textures/entities/player/shadow.png", "player_shadow")
# On définit ses attributs
player.set_default_life(15)
player.max_speed = 64.0
player.x = 220.
player.y = 767.
# On place la caméra au niveau du joueur
self.camera.x = player.x
self.camera.y = player.y
self.camera.target_x = player.x
self.camera.target_y = player.y
# On enregistre l'entité
self.entity_manager.set_player_entity("player")
self.camera.follow_entity(player)
def spawn_mobs(self):