Merge pull request 'Gameplay de la recherche dans les dossiers' (#44) from directory-search into main

Reviewed-on: #44
This commit is contained in:
CoCo_Sol 2023-10-30 00:48:58 +00:00 committed by Gitea
commit bf303714c5
No known key found for this signature in database
44 changed files with 440 additions and 5 deletions

Binary file not shown.

BIN
assets/krita/loupe.kra Normal file

Binary file not shown.

BIN
assets/sounds/slide.wav Normal file

Binary file not shown.

Binary file not shown.

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

View file

@ -0,0 +1,9 @@
{
"end_image": "dark_desktop.png",
"offset": {
"x": 0,
"y": 0
},
"frame_count": 31,
"fps": 60
}

View file

@ -1,8 +1,7 @@
{
"end_image": "directory.png",
"offset": {
"x": -48,
"y": -176
"x": 8,
"y": -83
},
"frame_count": 268,
"fps": 60

View file

@ -0,0 +1,8 @@
{
"offset": {
"x": 8,
"y": -83
},
"frame_count": 268,
"fps": 60
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -434,6 +434,7 @@ class Mouse:
self.pressed: set[int] = set()
self.released: set[int] = set()
self.position: Vec2 = Vec2(0.0, 0.0)
self.delta: Vec2 = Vec2(0.0, 0.0)
def is_button_pressed(self, button: int) -> bool:
"""
@ -677,6 +678,7 @@ def start_game(
keyboard.released.clear()
mouse.pressed.clear()
mouse.released.clear()
last_position = Vec2(mouse.position)
for event in pygame.event.get():
if event.type == pygame.QUIT:
world[Game].stop()
@ -704,6 +706,7 @@ def start_game(
((event.pos[0] - rect[0]) / rect[2]) * Display.WIDTH,
((event.pos[1] - rect[1]) / rect[3]) * Display.HEIGHT,
)
mouse.delta = mouse.position - last_position
# On vérifie le survol des textures et textes
for entity in world.query(Position, Texture):

View file

@ -4,13 +4,14 @@ Example de l'utilisation du moteur de jeu.
from engine import start_game
from scenes import classique, menu
from scenes import directory_search, menu, classique
start_game(
{
"menu": menu.SCENE,
"classique": classique.SCENE
"classique": classique.SCENE,
"histoire": directory_search.SCENE,
},
"menu",
title="Guess The Number",

37
src/plugins/smooth.py Normal file
View file

@ -0,0 +1,37 @@
"""
Un plugin permettant de faire des déplacements fluides des entités.
"""
from engine import Delta, Position, Scene, Vec2, World
class Target(Vec2):
"""
Composant donnant la position voulue de l'entité.
"""
class Speed(float):
"""
Composant donnant la vittesse de déplacement de l'entité.
"""
def __update_positions(world: World):
"""
Met à jour la position des entités pour se rapprocher de la position voulue.
"""
for entity in world.query(Position, Target):
position = entity[Position]
target = entity[Target]
speed = entity[Speed] if Speed in entity else Speed(10)
entity[Position] = Position(
position + (target - position) * world[Delta] * speed
)
PLUGIN = Scene(
[],
[__update_positions],
[],
)

View file

@ -0,0 +1,378 @@
"""
Scène du jeu dans lequel on se cache de Edmond dans les dossiers.
"""
from enum import Enum
import random
from engine import (
Animation,
Centered,
Delta,
Display,
Entity,
Hovered,
Mouse,
Order,
Position,
Scene,
Sound,
Text,
TextSize,
Texture,
Vec2,
World,
)
from plugins import smooth
LINES = 3
COLUMNS = 5
SPACING = 200
class State(Enum):
"""
Etat de la scène.
"""
MOVING = 0
SEARCHING = 1
GAME_OVER = 2
class SelectedDirectory:
"""
Une ressource qui stoque le dossier selectionné pour le déplacement.
"""
def __init__(self, entity: Entity, start_position: Vec2):
self.entity = entity
self.position = start_position
class AttackTimer(float):
"""
Ressource qui stoque un timer pour l'attaque.
"""
class AttackSpeed(float):
"""
Ressource qui dit le temps de l'attaque.
"""
class DirectoryPosition:
"""
La position d'un dossier dans la grille.
"""
def __init__(self, x: int, y: int):
self.x = x
self.y = y
def __eq__(self, value: object) -> bool:
if isinstance(value, DirectoryPosition):
return self.x == value.x and self.y == value.y
return False
def screen_position(self) -> Vec2:
"""
Calcule la position de l'entité sur l'ecran.
"""
size = Vec2(SPACING)
offset = -(size * Vec2(COLUMNS - 1, LINES - 1) / 2)
first_position = Vec2(Display.WIDTH / 2, Display.HEIGHT / 2) + offset
return first_position + Vec2(self.x, self.y) * size
class AttackPoint(DirectoryPosition):
"""
Composant qui marque un point d'attaque.
"""
class DirectoryName:
"""
Composant qui marque une entité comme étant le nom d'un dossier.
"""
def __init__(self, entity: Entity):
self.entity = entity
class UserDirectory:
"""
Composant qui marque le dossier que l'utilisateur doit protéger.
"""
class GameStarted:
"""
Une ressource qui permet de savoir que le jeu commence.
"""
def __change_folders_speeds(world: World, _e: Entity):
"""
Change les vitesses des dossiers.
"""
for entity in world.query(DirectoryPosition, smooth.Speed):
entity[smooth.Speed] = smooth.Speed(random.uniform(2.0, 2.5))
for entity in world.query(TextSize):
entity[TextSize] = TextSize(40)
def __remove_folders_speeds(world: World):
"""
Supprime les vitesses des dossiers.
"""
if GameStarted not in world and world[AttackTimer] >= 3.0:
for entity in world.query(DirectoryPosition, smooth.Speed):
del entity[smooth.Speed]
world[GameStarted] = GameStarted()
def __initialize_world(world: World):
"""
Initialise le monde de la scène.
"""
world[State] = State.MOVING
world[AttackTimer] = AttackTimer(0.0)
world[AttackSpeed] = AttackSpeed(5.0)
world.create_entity(
Position(),
Order(0),
Animation("fade_desktop", __change_folders_speeds),
)
names = [
"Classique",
"Menteur",
"Tricheur",
"Histoire",
"Je t'aime",
"Hello",
"Cheval",
"Defender",
"Dansons",
"Secrets",
"Edmond",
"Mon Amour",
"Melatonin",
"Films",
"Cinéma",
]
positions = [
Position(40 + (7 * 180) + 48, 35 + (5 * 166) + 38),
Position(40 + (5 * 180) + 48, 35 + (5 * 166) + 38),
Position(40 + (4 * 180) + 48, 35 + (4 * 166) + 38),
Position(40 + (3 * 180) + 48, 35 + (5 * 166) + 38),
Position(40 + (1 * 180) + 48, 35 + (5 * 166) + 38),
Position(40 + (6 * 180) + 48, 35 + (2 * 166) + 38),
Position(40 + (5 * 180) + 48, 35 + (3 * 166) + 38),
Position(40 + (4 * 180) + 48, 35 + (2 * 166) + 38),
Position(40 + (2 * 180) + 48, 35 + (4 * 166) + 38),
Position(40 + (1 * 180) + 48, 35 + (2 * 166) + 38),
Position(40 + (7 * 180) + 48, 35 + (1 * 166) + 38),
Position(40 + (5 * 180) + 48, 35 + (1 * 166) + 38),
Position(40 + (3 * 180) + 48, 35 + (0 * 166) + 38),
Position(40 + (2 * 180) + 48, 35 + (1 * 166) + 38),
Position(40 + (0 * 180) + 48, 35 + (1 * 166) + 38),
]
for y in range(LINES):
for x in range(COLUMNS):
position = DirectoryPosition(x, y)
entity = world.create_entity(
positions.pop(),
smooth.Speed(0),
Order(1),
Centered(),
Texture("directory.png"),
position,
)
if x == 2 and y == 1:
entity[UserDirectory] = UserDirectory()
entity[Texture] = Texture("user_directory.png")
world.create_entity(
Position(0, 0),
Order(1),
Centered(),
Text(names.pop()),
TextSize(0),
DirectoryName(entity),
)
def __attacks(world: World):
"""
Déclenche les attaques de Edmond.
"""
if world[State] == State.GAME_OVER:
return
world[AttackTimer] = AttackTimer(world[AttackTimer] + world[Delta])
timer = world[AttackTimer]
if timer >= world[AttackSpeed] and world[State] == State.MOVING:
world[State] = State.SEARCHING
for entity in world.query(AttackPoint):
position = entity[AttackPoint]
for directory_entity in world.query(DirectoryPosition):
if directory_entity[DirectoryPosition] == position:
if UserDirectory in directory_entity:
directory_entity[Animation] = Animation(
"search_directory_failed"
)
world[State] = State.GAME_OVER
else:
directory_entity[Animation] = Animation("search_directory")
del entity[AttackPoint]
del entity[Position]
del entity[Order]
del entity[Centered]
del entity[Texture]
elif timer >= world[AttackSpeed] + 4.5 and world[State] == State.SEARCHING:
world[State] = State.MOVING
for _ in range(10):
position = AttackPoint(
random.randint(0, COLUMNS - 1),
random.randint(0, LINES - 1),
)
world.create_entity(
position,
Position(position.screen_position()),
Order(50),
Centered(),
Texture("attack_point.png"),
)
world[AttackTimer] = AttackTimer(0.0)
world[AttackSpeed] = AttackSpeed(world[AttackSpeed] * 0.9)
def __move_directories(world: World):
"""
Permet de déplacer les dossiers avec la souris.
"""
# Si on n'est pas dans le bon state on annule
if GameStarted not in world or world[State] == State.GAME_OVER:
return
# On met à jour la séléction
mouse = world[Mouse]
for entity in world.query(Hovered, DirectoryPosition):
if Animation in entity:
continue
if mouse.is_button_pressed(1):
world[SelectedDirectory] = SelectedDirectory(entity, Vec2(mouse.position))
break
# Si un dossier est séléctionné
if SelectedDirectory in world:
selected_directory = world[SelectedDirectory]
selected_entity = selected_directory.entity
directory_position = selected_entity[DirectoryPosition]
# Vérification du relachement de la souris
if not mouse.is_button(1):
del world[SelectedDirectory]
return
# On calcule le déplacement de la souris
mouse_delta = mouse.position - selected_directory.position
# On annule si il y a pas eu de déplacement significatif de la souris
if mouse_delta.length < 40:
return
# Récupération du mouvement voulu
if abs(mouse_delta.x) >= abs(mouse_delta.y):
movement = (int(mouse_delta.x / abs(mouse_delta.x)), 0)
else:
movement = (0, int(mouse_delta.y / abs(mouse_delta.y)))
# Récupération des mouvements possible du dossier
movements: list[tuple[int, int]] = []
if directory_position.x != 0:
movements.append((-1, 0))
if directory_position.x != COLUMNS - 1:
movements.append((1, 0))
if directory_position.y != 0:
movements.append((0, -1))
if directory_position.y != LINES - 1:
movements.append((0, 1))
if len(movements) == 0:
return
# Si le mouvement n'est pas possible, on annule
if movement not in movements:
return
# Si l'entité est animé on annule
if Animation in selected_entity:
return
# On trouve l'autre dossier
for entity in world.query(DirectoryPosition, without=(Animation,)):
if entity != selected_entity and entity[
DirectoryPosition
] == DirectoryPosition(
directory_position.x + movement[0],
directory_position.y + movement[1],
):
other_directory = entity
break
else:
return
# On actualise la position de l'autre dossier
other_directory[DirectoryPosition].x -= movement[0]
other_directory[DirectoryPosition].y -= movement[1]
# On actualise la position du dossier
selected_entity[DirectoryPosition].x += movement[0]
selected_entity[DirectoryPosition].y += movement[1]
# On joue un son
world.create_entity(Sound("slide.wav"))
# On retire le dossier selectionné
del world[SelectedDirectory]
def __update_positions(world: World):
"""
Met à jour la position cible des dossiers.
"""
for entity in world.query(DirectoryPosition):
position = entity[DirectoryPosition]
entity[smooth.Target] = smooth.Target(position.screen_position())
entity[Order] = Order(position.y + 1)
def __update_directory_names(world: World):
"""
Met à jour la position des noms des dossiers.
"""
for entity in world.query(DirectoryName):
directory_entity = entity[DirectoryName].entity
entity[Position] = Position(directory_entity[Position] + Vec2(0, 75))
entity[Order] = directory_entity[Order]
SCENE = (
Scene(
[__initialize_world],
[__attacks, __move_directories, __update_positions, __remove_folders_speeds],
[],
)
+ smooth.PLUGIN
+ Scene(
[],
[__update_directory_names],
[],
)
)