Mode menteur #45

Merged
CoCo_Sol merged 10 commits from menteur into main 2023-10-30 00:57:21 +00:00
54 changed files with 470 additions and 7 deletions
Showing only changes of commit 24629974d2 - Show all commits

Binary file not shown.

BIN
assets/krita/loupe.kra Normal file

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

BIN
assets/sounds/slide.wav Normal file

Binary file not shown.

BIN
assets/sounds/win_sound.wav Normal file

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 342 KiB

After

Width:  |  Height:  |  Size: 342 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 336 KiB

After

Width:  |  Height:  |  Size: 336 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 333 KiB

After

Width:  |  Height:  |  Size: 333 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 324 KiB

After

Width:  |  Height:  |  Size: 324 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 322 KiB

After

Width:  |  Height:  |  Size: 322 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 318 KiB

After

Width:  |  Height:  |  Size: 318 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 317 KiB

After

Width:  |  Height:  |  Size: 317 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 309 KiB

After

Width:  |  Height:  |  Size: 309 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 307 KiB

After

Width:  |  Height:  |  Size: 307 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 301 KiB

After

Width:  |  Height:  |  Size: 301 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 296 KiB

After

Width:  |  Height:  |  Size: 296 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 292 KiB

After

Width:  |  Height:  |  Size: 292 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 286 KiB

After

Width:  |  Height:  |  Size: 286 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 282 KiB

After

Width:  |  Height:  |  Size: 282 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 276 KiB

After

Width:  |  Height:  |  Size: 276 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 272 KiB

After

Width:  |  Height:  |  Size: 272 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 268 KiB

After

Width:  |  Height:  |  Size: 268 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 265 KiB

After

Width:  |  Height:  |  Size: 265 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 260 KiB

After

Width:  |  Height:  |  Size: 260 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 256 KiB

After

Width:  |  Height:  |  Size: 256 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 249 KiB

After

Width:  |  Height:  |  Size: 249 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 246 KiB

After

Width:  |  Height:  |  Size: 246 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 KiB

After

Width:  |  Height:  |  Size: 244 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 240 KiB

After

Width:  |  Height:  |  Size: 240 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 KiB

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 232 KiB

After

Width:  |  Height:  |  Size: 232 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 230 KiB

After

Width:  |  Height:  |  Size: 230 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 KiB

After

Width:  |  Height:  |  Size: 226 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 225 KiB

After

Width:  |  Height:  |  Size: 225 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 222 KiB

After

Width:  |  Height:  |  Size: 222 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

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.

Before

Width:  |  Height:  |  Size: 6 KiB

After

Width:  |  Height:  |  Size: 6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 216 KiB

After

Width:  |  Height:  |  Size: 216 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 5.4 KiB

View file

@ -6,6 +6,7 @@ Un moteur de jeu inspiré de bevy.
import json
import math
import os
import random
from typing import Callable, Optional, Sequence, SupportsFloat, TypeVar, Union
from time import time
import pygame
@ -433,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:
"""
@ -592,6 +594,12 @@ class Sound:
stop_on_remove: bool = False,
) -> None:
self.name = name
if os.path.isdir(f"assets/sounds/{name}"):
list_files = os.listdir(f"assets/sounds/{name}")
random_file = random.choice(list_files)
self.name = f"{name}/{random_file}"
self.volume = volume
self.loop = loop
self.callback = callback
@ -670,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()
@ -697,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,7 +4,7 @@ Example de l'utilisation du moteur de jeu.
from engine import start_game
from scenes import classique, menteur, menu
from scenes import classique, menteur, menu, directory_search
start_game(
@ -12,7 +12,9 @@ start_game(
"menu": menu.SCENE,
"classique": classique.SCENE,
"menteur": menteur.SCENE
"histoire": directory_search.SCENE,
},
"menu",
title="Guess The Number",
)
# type: ignore

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

@ -2,7 +2,7 @@
Definit un plugin qui crée un texte avec les touches frappées
"""
from engine import Keyboard, Scene, Text, World
from engine import Keyboard, Scene, Sound, Text, World
class Typing(str):
@ -20,10 +20,12 @@ def __update(world: World):
text = entity[Text]
for key in keyboard.pressed:
if key == "backspace":
world.create_entity(Sound("click"))
text = text[:-1]
if key.startswith("["): # pavé numerique
key = key[1]
if key in entity[Typing]:
world.create_entity(Sound("click"))
text += key
entity[Text] = Text(text)

View file

@ -9,12 +9,14 @@ from engine import (
Clickable,
Color,
Display,
Entity,
Game,
HoveredTexture,
Keyboard,
Order,
Position,
Scene,
Sound,
Text,
TextSize,
Texture,
@ -113,7 +115,7 @@ def __initialize_world(world: World):
Order(11),
Position(150, 150),
Texture("classique/arrow.png"),
Clickable(lambda world, _: world[Game].change_scene("menu")),
Clickable(on_menu_button),
HoveredTexture("classique/arrow_hover.png"),
)
@ -123,12 +125,22 @@ def __initialize_world(world: World):
world[IsRunning] = IsRunning()
def on_menu_button(world: World, entity: Entity):
"""
Fonction qui s'execute quand on clique sur un bouton.
"""
world[Game].change_scene("menu")
entity[Sound] = Sound("click")
def _update(world: World):
"""
Verifie si le nombre donné est le meme que celui que l'on a choisi.
Boucle du jeu.
"""
world.create_entity(Sound("menu_click.wav"))
# si le jeu s'est arrete.
if IsRunning not in world:
# on relance le jeu.
@ -169,6 +181,12 @@ def end_game(world: World, state: str):
"""
del world[IsRunning] # le jeu est fini.
# On joue le son
if state == "Gagné":
world.create_entity(Sound("win_sound.wav"))
else:
world.create_entity(Sound("lose_sound.wav"))
# On affiche le message de fin.
for entity_text in world.query(TextDialogue):
entity_text[Text] = Text(f"{state} !")

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],
[],
)
)

View file

@ -36,7 +36,7 @@ def on_click_butons(world: World, entity: Entity, name: str):
"""
Fonction qui s'execute quand on clique sur un bouton.
"""
entity[Sound] = Sound("click.wav")
entity[Sound] = Sound("click")
world[Game].change_scene(name)