mirror of
https://github.com/BreizhHardware/project_sanic.git
synced 2026-01-18 16:47:25 +01:00
Feat(Instructions) - Add instructions screen to display game controls; implement event handling for navigation and drawing functionality.
This commit is contained in:
@@ -21,6 +21,8 @@ class Player(Entity):
|
||||
|
||||
self.jump_button = 0
|
||||
self.dash_button = 1
|
||||
self.attack_button = 2
|
||||
self.menu_button = 3
|
||||
|
||||
try:
|
||||
if pygame.joystick.get_count() > 0:
|
||||
@@ -540,152 +542,170 @@ class Player(Entity):
|
||||
self.is_attacking = False
|
||||
current_time = pygame.time.get_ticks()
|
||||
pressed_keys = pygame.key.get_pressed()
|
||||
if pressed_keys[K_q] and pressed_keys[K_c]:
|
||||
if current_time - self.last_attack_time >= self.attack_cooldown:
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(self.pos.x, self.pos.y)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x, self.pos.y),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
)
|
||||
# Add projectile to the sprite group (to be placed in main.py)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
|
||||
if pressed_keys[K_d] and pressed_keys[K_c]:
|
||||
if current_time - self.last_attack_time >= self.attack_cooldown:
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(self.pos.x, self.pos.y)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x, self.pos.y),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
)
|
||||
# Add projectile to the sprite group (to be placed in main.py)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
joystick_attack = False
|
||||
if self.has_joystick and self.joystick:
|
||||
try:
|
||||
if self.joystick.get_numbuttons() > self.attack_button:
|
||||
joystick_attack = self.joystick.get_button(self.attack_button)
|
||||
except pygame.error:
|
||||
pass
|
||||
|
||||
if pressed_keys[K_q] and pressed_keys[K_v]:
|
||||
if current_time - self.last_attack_time >= self.attack_cooldown:
|
||||
attack_sound = pygame.mixer.Sound("assets/sound/Boule de feu.mp3")
|
||||
attack_sound.set_volume(0.4)
|
||||
attack_sound.play()
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(-self.pos.x, 0)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x - 50, self.pos.y - 50),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
texturePath="assets/player/Boule de feu.png",
|
||||
size=(50, 50),
|
||||
)
|
||||
# Add projectile to the sprite group (to be placed in main.py)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
if self.projectiles > 0:
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(-self.pos.x, 0)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x - 50, self.pos.y - 50),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
texturePath="assets/player/Boule de feu.png",
|
||||
)
|
||||
# Add projectile to the sprite group (to be placed in main.py)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
self.projectiles -= 1
|
||||
if (
|
||||
joystick_attack
|
||||
and current_time - self.last_attack_time >= self.attack_cooldown
|
||||
and self.projectiles > 0
|
||||
):
|
||||
attack_sound = pygame.mixer.Sound("assets/sound/Boule de feu.mp3")
|
||||
attack_sound.set_volume(0.4)
|
||||
attack_sound.play()
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
|
||||
if pressed_keys[K_d] and pressed_keys[K_v]:
|
||||
if current_time - self.last_attack_time >= self.attack_cooldown:
|
||||
attack_sound = pygame.mixer.Sound("assets/sound/Boule de feu.mp3")
|
||||
attack_sound.set_volume(0.4)
|
||||
attack_sound.play()
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
# Direction en fonction de où le personnage est tourné
|
||||
if self.facing_right:
|
||||
direction = vec(self.pos.x, 0)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x + 50, self.pos.y - 50),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
texturePath="assets/player/Boule de feu.png",
|
||||
size=(50, 50),
|
||||
position = vec(self.pos.x + 50, self.pos.y - 50)
|
||||
else:
|
||||
direction = vec(-self.pos.x, 0)
|
||||
position = vec(self.pos.x - 50, self.pos.y - 50)
|
||||
|
||||
projectile = Projectile(
|
||||
pos=position,
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
texturePath="assets/player/Boule de feu.png",
|
||||
size=(50, 50),
|
||||
)
|
||||
|
||||
# Ajouter le projectile au groupe de sprites
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
# Add projectile to the sprite group (to be placed in main.py)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
|
||||
self.projectiles -= 1
|
||||
|
||||
if (
|
||||
pressed_keys[K_q]
|
||||
and pressed_keys[K_c]
|
||||
and current_time - self.last_attack_time >= self.attack_cooldown
|
||||
):
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(self.pos.x, self.pos.y)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x, self.pos.y),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
)
|
||||
# Add projectile to the sprite group (to be placed in main.py)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
if self.projectiles > 0:
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(self.pos.x, 0)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x + 50, self.pos.y - 50),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
texturePath="assets/player/Boule de feu.png",
|
||||
)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
self.projectiles -= 1
|
||||
)
|
||||
|
||||
if (
|
||||
pressed_keys[K_d]
|
||||
and pressed_keys[K_c]
|
||||
and current_time - self.last_attack_time >= self.attack_cooldown
|
||||
):
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(self.pos.x, self.pos.y)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x, self.pos.y),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
)
|
||||
# Add projectile to the sprite group (to be placed in main.py)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
|
||||
if (
|
||||
pressed_keys[K_q]
|
||||
and pressed_keys[K_v]
|
||||
and current_time - self.last_attack_time >= self.attack_cooldown
|
||||
and self.projectiles > 0
|
||||
):
|
||||
attack_sound = pygame.mixer.Sound("assets/sound/Boule de feu.mp3")
|
||||
attack_sound.set_volume(0.4)
|
||||
attack_sound.play()
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(-self.pos.x, 0)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x - 50, self.pos.y - 50),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
texturePath="assets/player/Boule de feu.png",
|
||||
)
|
||||
# Add projectile to the sprite group (to be placed in main.py)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
self.projectiles -= 1
|
||||
|
||||
if (
|
||||
pressed_keys[K_d]
|
||||
and pressed_keys[K_v]
|
||||
and current_time - self.last_attack_time >= self.attack_cooldown
|
||||
and self.projectiles > 0
|
||||
):
|
||||
attack_sound = pygame.mixer.Sound("assets/sound/Boule de feu.mp3")
|
||||
attack_sound.set_volume(0.4)
|
||||
attack_sound.play()
|
||||
self.is_attacking = True
|
||||
self.attack_start_time = current_time
|
||||
self.last_attack_time = current_time
|
||||
# Calculate direction to player
|
||||
direction = vec(self.pos.x, 0)
|
||||
projectile = Projectile(
|
||||
pos=vec(self.pos.x + 50, self.pos.y - 50),
|
||||
direction=direction,
|
||||
speed=2,
|
||||
damage=1,
|
||||
color=(165, 42, 42),
|
||||
enemy_proj=False,
|
||||
texturePath="assets/player/Boule de feu.png",
|
||||
)
|
||||
pygame.event.post(
|
||||
pygame.event.Event(
|
||||
pygame.USEREVENT,
|
||||
{"action": "create_projectile", "projectile": projectile},
|
||||
)
|
||||
)
|
||||
self.projectiles -= 1
|
||||
|
||||
def add_projectiles(self):
|
||||
"""Set player projectiles to 3 and show floating text"""
|
||||
|
||||
97
src/Menu/InstructionsScreen.py
Normal file
97
src/Menu/InstructionsScreen.py
Normal file
@@ -0,0 +1,97 @@
|
||||
import pygame
|
||||
import math
|
||||
from src.Menu.BackgroundManager import BackgroundManager
|
||||
|
||||
|
||||
class InstructionsScreen:
|
||||
def __init__(self, game_resources):
|
||||
self.game_resources = game_resources
|
||||
self.bg_manager = BackgroundManager(game_resources.WIDTH, game_resources.HEIGHT)
|
||||
|
||||
self.title_font = pygame.font.SysFont("Arial", 72)
|
||||
self.text_font = pygame.font.SysFont("Arial", 32)
|
||||
|
||||
self.blink_timer = 0
|
||||
self.blink_speed = 0.5
|
||||
|
||||
def draw(self, surface):
|
||||
self.bg_manager.draw(surface)
|
||||
|
||||
def render_text_with_outline(text, font, text_color, outline_color):
|
||||
text_surface = font.render(text, True, text_color)
|
||||
outline_surface = font.render(text, True, outline_color)
|
||||
|
||||
w, h = text_surface.get_size()
|
||||
outline_surf = pygame.Surface((w + 2, h + 2), pygame.SRCALPHA)
|
||||
|
||||
# Dessiner le contour en décalant le texte
|
||||
offsets = [
|
||||
(1, 1),
|
||||
(1, -1),
|
||||
(-1, 1),
|
||||
(-1, -1),
|
||||
(1, 0),
|
||||
(-1, 0),
|
||||
(0, 1),
|
||||
(0, -1),
|
||||
]
|
||||
for dx, dy in offsets:
|
||||
outline_surf.blit(outline_surface, (dx + 1, dy + 1))
|
||||
|
||||
# Dessiner le texte principal au centre
|
||||
outline_surf.blit(text_surface, (1, 1))
|
||||
return outline_surf
|
||||
|
||||
title_surf = render_text_with_outline(
|
||||
"Game control", self.title_font, (255, 255, 255), (0, 0, 0)
|
||||
)
|
||||
title_rect = title_surf.get_rect(center=(self.game_resources.WIDTH // 2, 100))
|
||||
surface.blit(title_surf, title_rect)
|
||||
|
||||
instructions = [
|
||||
"Q : Move left",
|
||||
"D : Move right",
|
||||
"A : Dash",
|
||||
"Espace : Jump",
|
||||
"V: Attack",
|
||||
"Escape : Pause / Menu",
|
||||
"Controller : Use the left joystick to move",
|
||||
"B: Dash",
|
||||
"A: Jump",
|
||||
"X: Attack",
|
||||
"Y: Pause / Menu",
|
||||
]
|
||||
|
||||
y_offset = 180
|
||||
line_spacing = 40
|
||||
|
||||
for line in instructions:
|
||||
text_surf = render_text_with_outline(
|
||||
line, self.text_font, (255, 255, 255), (0, 0, 0)
|
||||
)
|
||||
text_rect = text_surf.get_rect(
|
||||
center=(self.game_resources.WIDTH // 2, y_offset)
|
||||
)
|
||||
surface.blit(text_surf, text_rect)
|
||||
y_offset += line_spacing
|
||||
|
||||
self.blink_timer += 0.01
|
||||
alpha = abs(math.sin(self.blink_timer * self.blink_speed)) * 255
|
||||
|
||||
skip_text = render_text_with_outline(
|
||||
"Press any key to continue", self.text_font, (255, 220, 0), (0, 0, 0)
|
||||
)
|
||||
skip_text.set_alpha(int(alpha))
|
||||
skip_rect = skip_text.get_rect(
|
||||
center=(self.game_resources.WIDTH // 2, self.game_resources.HEIGHT - 100)
|
||||
)
|
||||
surface.blit(skip_text, skip_rect)
|
||||
|
||||
def handle_event(self, event):
|
||||
if (
|
||||
event.type == pygame.KEYDOWN
|
||||
or event.type == pygame.MOUSEBUTTONDOWN
|
||||
or event.type == pygame.JOYBUTTONDOWN
|
||||
):
|
||||
return "menu"
|
||||
return None
|
||||
@@ -23,6 +23,7 @@ from src.Database.CheckpointDB import CheckpointDB
|
||||
from src.Map.Editor.LevelEditor import LevelEditor
|
||||
from src.Menu.LevelEditorSelectionMenu import LevelEditorSelectionMenu
|
||||
from src.Map.Speedrun.SpeedrunTimer import SpeedrunTimer
|
||||
from src.Menu.InstructionsScreen import InstructionsScreen
|
||||
|
||||
|
||||
def initialize_game_resources():
|
||||
@@ -63,8 +64,9 @@ def initialize_game_resources():
|
||||
projectiles = pygame.sprite.Group()
|
||||
|
||||
# Game states initialization
|
||||
current_state = 0 # MENU
|
||||
current_state = 5 # INSTRUCTIONS
|
||||
current_menu = "main"
|
||||
instructions_screen = InstructionsScreen(game_resources)
|
||||
main_menu = Menu(game_resources)
|
||||
level_select_menu = None
|
||||
editor_select_menu = None
|
||||
@@ -99,6 +101,7 @@ def initialize_game_resources():
|
||||
joysticks,
|
||||
editor_select_menu,
|
||||
leaderboard_db,
|
||||
instructions_screen,
|
||||
)
|
||||
|
||||
|
||||
@@ -134,6 +137,16 @@ def handle_system_events(
|
||||
)
|
||||
# Update window dimensions
|
||||
ORIGINAL_WIDTH, ORIGINAL_HEIGHT = event.w, event.h
|
||||
elif event.type == pygame.JOYBUTTONDOWN:
|
||||
try:
|
||||
if event.button == 4: # Triangle sur la plupart des manettes
|
||||
if current_state in [1, 2]: # PLAYING, INFINITE
|
||||
current_state = 0 # MENU
|
||||
else:
|
||||
pygame.quit()
|
||||
sys.exit()
|
||||
except Exception as e:
|
||||
print(f"Error while handling joystick button: {e}")
|
||||
|
||||
return current_state, fullscreen, displaysurface, ORIGINAL_WIDTH, ORIGINAL_HEIGHT
|
||||
|
||||
@@ -645,7 +658,7 @@ def handle_death_screen(
|
||||
def handler():
|
||||
"""Main function that handles the game flow"""
|
||||
# Game state constants
|
||||
MENU, PLAYING, INFINITE, LEADERBOARD, DEATH_SCREEN = 0, 1, 2, 3, 4
|
||||
MENU, PLAYING, INFINITE, LEADERBOARD, DEATH_SCREEN, INSTRUCTIONS = 0, 1, 2, 3, 4, 5
|
||||
previous_state = None
|
||||
|
||||
# Initialize game resources and states
|
||||
@@ -667,6 +680,7 @@ def handler():
|
||||
joysticks,
|
||||
editor_select_menu,
|
||||
leaderboard_db,
|
||||
instructions_screen,
|
||||
) = initialize_game_resources()
|
||||
|
||||
# Initialize editor variables
|
||||
@@ -687,6 +701,7 @@ def handler():
|
||||
print(f"Error while getting events: {e}")
|
||||
pygame.joystick.quit()
|
||||
pygame.joystick.init()
|
||||
events = []
|
||||
continue
|
||||
|
||||
# Process events
|
||||
@@ -766,6 +781,13 @@ def handler():
|
||||
game_resources
|
||||
)
|
||||
|
||||
elif current_state == INSTRUCTIONS:
|
||||
for event in events:
|
||||
result = instructions_screen.handle_event(event)
|
||||
if result == "menu":
|
||||
current_state = MENU
|
||||
instructions_screen.draw(displaysurface)
|
||||
|
||||
# Process general game events (player death, projectiles, etc.)
|
||||
if event.type == USEREVENT:
|
||||
current_state, death_timer, checkpoint_data, projectiles = (
|
||||
@@ -929,6 +951,13 @@ def handler():
|
||||
elif death_result["action"] == "return_to_menu":
|
||||
current_state = death_result["current_state"]
|
||||
|
||||
elif current_state == INSTRUCTIONS:
|
||||
for event in events:
|
||||
result = instructions_screen.handle_event(event)
|
||||
if result == "menu":
|
||||
current_state = MENU
|
||||
instructions_screen.draw(displaysurface)
|
||||
|
||||
# Update display
|
||||
pygame.display.update()
|
||||
game_resources.FramePerSec.tick(game_resources.FPS)
|
||||
|
||||
Reference in New Issue
Block a user