added a ton of comments
This commit is contained in:
parent
46923152e4
commit
0a00482562
Binary file not shown.
Before Width: | Height: | Size: 934 KiB After Width: | Height: | Size: 934 KiB |
77
game.py
77
game.py
@ -1,9 +1,8 @@
|
|||||||
# Example file showing a basic pygame "game loop"
|
# Here we import everything we need
|
||||||
import pygame
|
import pygame
|
||||||
import math
|
import math
|
||||||
import csv
|
import csv
|
||||||
import random
|
import random
|
||||||
import datetime
|
|
||||||
|
|
||||||
# pygame setup
|
# pygame setup
|
||||||
pygame.init()
|
pygame.init()
|
||||||
@ -12,11 +11,9 @@ clock = pygame.time.Clock()
|
|||||||
running = True
|
running = True
|
||||||
dt = 0
|
dt = 0
|
||||||
|
|
||||||
player_sprite = pygame.Rect(100,100,50,50)
|
|
||||||
mur = pygame.Rect(500,100,100,100)
|
|
||||||
|
|
||||||
class Waypoint:
|
class Waypoint:
|
||||||
def __init__(self, name: str) -> None:
|
def __init__(self, name: str) -> None:
|
||||||
|
"""Create the corresponding waypoint according to the letter specified in name: str"""
|
||||||
self.name = name
|
self.name = name
|
||||||
match name.capitalize():
|
match name.capitalize():
|
||||||
case "A":
|
case "A":
|
||||||
@ -53,10 +50,12 @@ class Waypoint:
|
|||||||
self.connection = ("A", "G")
|
self.connection = ("A", "G")
|
||||||
|
|
||||||
def get_new_connected(self)-> object:
|
def get_new_connected(self)-> object:
|
||||||
|
"""Return a new waypoint connected to the current one"""
|
||||||
letter = random.choice(self.connection)
|
letter = random.choice(self.connection)
|
||||||
return Waypoint(letter)
|
return Waypoint(letter)
|
||||||
|
|
||||||
def get_direction(self, pointA: object, pointB: object)-> str:
|
def get_direction(self, pointA: object, pointB: object)-> str:
|
||||||
|
"""Helper that returns the direction: str from pointA: Waypoint, to pointB: Waypoint"""
|
||||||
a = pointA.name
|
a = pointA.name
|
||||||
b = pointB.name
|
b = pointB.name
|
||||||
if (a == "H" and b == "A") or (a == "G" and b == "E") or (a == "E" and b == "B") or (a == "F" and b == "D") or (a == "D" and b == "C"):
|
if (a == "H" and b == "A") or (a == "G" and b == "E") or (a == "E" and b == "B") or (a == "F" and b == "D") or (a == "D" and b == "C"):
|
||||||
@ -68,10 +67,18 @@ class Waypoint:
|
|||||||
elif (a == "B" and b == "A") or (a == "C" and b == "B") or (a == "D" and b == "E") or (a == "G" and b == "H") or (a == "F" and b == "G"):
|
elif (a == "B" and b == "A") or (a == "C" and b == "B") or (a == "D" and b == "E") or (a == "G" and b == "H") or (a == "F" and b == "G"):
|
||||||
return "west"
|
return "west"
|
||||||
else:
|
else:
|
||||||
|
# If the direction is impossible
|
||||||
raise Exception("crash: POINT A and B impossible")
|
raise Exception("crash: POINT A and B impossible")
|
||||||
|
|
||||||
class Pnj:
|
class Pnj:
|
||||||
def __init__(self, x: int, y: int, sprite_path: str, direction: str, objectif: Waypoint, speed: int) -> None:
|
def __init__(self, x: int, y: int, sprite_path: str, direction: str, objectif: Waypoint, speed: int) -> None:
|
||||||
|
"""Create a single pnj with the following attributes:
|
||||||
|
- x: int, x coordinate
|
||||||
|
- y: int, y coordinate
|
||||||
|
- sprite_path: str, the path to the sprite of the pnj
|
||||||
|
- direction: str, the direction can be "north", "south", "east", or "west"
|
||||||
|
- objectif: Waypoint, the goal of the pnj
|
||||||
|
- speed: int, the desired speed of the pnj"""
|
||||||
self.x = x
|
self.x = x
|
||||||
self.y = y
|
self.y = y
|
||||||
self.sprite = pygame.transform.scale_by(pygame.image.load(sprite_path), 3.0)
|
self.sprite = pygame.transform.scale_by(pygame.image.load(sprite_path), 3.0)
|
||||||
@ -103,7 +110,7 @@ class Pnj:
|
|||||||
|
|
||||||
def check_objectif(self):
|
def check_objectif(self):
|
||||||
"""
|
"""
|
||||||
Vérifie que l'objectif n'est pas atteint et renvoie true si un l'est, s'occupe aussi de réassigner un nouvel objectig
|
Vérifie que l'objectif n'est pas atteint et renvoie true si un l'est, s'occupe aussi de réassigner un nouvel objectif
|
||||||
"""
|
"""
|
||||||
match self.direction:
|
match self.direction:
|
||||||
case "north":
|
case "north":
|
||||||
@ -133,6 +140,7 @@ class Pnj:
|
|||||||
return False
|
return False
|
||||||
|
|
||||||
def kill(self):
|
def kill(self):
|
||||||
|
"""Kill the PNJ by replacing it with a blood splatter"""
|
||||||
self.sprite = pygame.transform.scale_by(pygame.image.load("assets/blood.png"), 3.0)
|
self.sprite = pygame.transform.scale_by(pygame.image.load("assets/blood.png"), 3.0)
|
||||||
self.alive = False
|
self.alive = False
|
||||||
player.killcounter += 1
|
player.killcounter += 1
|
||||||
@ -146,32 +154,36 @@ class Pnj:
|
|||||||
|
|
||||||
class Village:
|
class Village:
|
||||||
def __init__(self, nb_pnj: int) -> None:
|
def __init__(self, nb_pnj: int) -> None:
|
||||||
|
"""This class defines the village of pnj in it's globality and nb_pnj (int) determines how many pnj should be generated"""
|
||||||
self.liste_pnj = []
|
self.liste_pnj = []
|
||||||
|
# here we generate each pnj randomly and store it in liste_pnj
|
||||||
for i in range(nb_pnj):
|
for i in range(nb_pnj):
|
||||||
start_waypoint = Waypoint(random.choice(("A", "B", "C", "D", "E", "F", "G", "H")))
|
start_waypoint = Waypoint(random.choice(("A", "B", "C", "D", "E", "F", "G", "H")))
|
||||||
objective_waypoint = start_waypoint.get_new_connected()
|
objective_waypoint = start_waypoint.get_new_connected()
|
||||||
self.liste_pnj.append(Pnj(start_waypoint.x, start_waypoint.y, "assets/pnj/pnj"+str(random.randint(1,8))+".png", start_waypoint.get_direction(start_waypoint, objective_waypoint), objective_waypoint, random.randint(1, 4)))
|
self.liste_pnj.append(Pnj(start_waypoint.x, start_waypoint.y, "assets/pnj/pnj"+str(random.randint(1,8))+".png", start_waypoint.get_direction(start_waypoint, objective_waypoint), objective_waypoint, random.randint(1, 4)))
|
||||||
|
|
||||||
def ajouter_pnj_random(self)-> None:
|
|
||||||
pass
|
|
||||||
|
|
||||||
def update_pnj(self)-> None:
|
def update_pnj(self)-> None:
|
||||||
|
"""Make each pnj move a little bit"""
|
||||||
for p in self.liste_pnj:
|
for p in self.liste_pnj:
|
||||||
p.avance()
|
p.avance()
|
||||||
|
|
||||||
def get_village_sprites(self)->list:
|
def get_village_sprites(self)->list:
|
||||||
|
"""Method that returns a list of tuple containing the coordinates and sprite of a pnj
|
||||||
|
The tuple is in this shape: (x, y, sprite)"""
|
||||||
village_sprites = []
|
village_sprites = []
|
||||||
for p in self.liste_pnj:
|
for p in self.liste_pnj:
|
||||||
village_sprites.append((p.x, p.y, p.sprite))
|
village_sprites.append((p.x, p.y, p.sprite))
|
||||||
return village_sprites
|
return village_sprites
|
||||||
|
|
||||||
def check_kill(self)->None:
|
def check_kill(self)->None:
|
||||||
|
"""Check for each pnj if he is being killed by the player"""
|
||||||
player_rect = pygame.Rect(player.x-50, player.y-50, 100, 100)
|
player_rect = pygame.Rect(player.x-50, player.y-50, 100, 100)
|
||||||
for p in self.liste_pnj:
|
for p in self.liste_pnj:
|
||||||
if p.alive and player_rect.collidepoint(p.x, p.y):
|
if p.alive and player_rect.collidepoint(p.x, p.y):
|
||||||
p.kill()
|
p.kill()
|
||||||
|
|
||||||
def final_boss(self)->None:
|
def final_boss(self)->None:
|
||||||
|
"""Called when all original pnj where killed, it create an army of really fast knight"""
|
||||||
for i in ("A", "B", "C", "D", "E", "F", "G", "H"):
|
for i in ("A", "B", "C", "D", "E", "F", "G", "H"):
|
||||||
start_waypoint = Waypoint(i)
|
start_waypoint = Waypoint(i)
|
||||||
objective_waypoint = start_waypoint.get_new_connected()
|
objective_waypoint = start_waypoint.get_new_connected()
|
||||||
@ -179,6 +191,8 @@ class Village:
|
|||||||
|
|
||||||
class Player:
|
class Player:
|
||||||
def __init__(self)-> None:
|
def __init__(self)-> None:
|
||||||
|
"""This class defines how the player can behave,
|
||||||
|
it should be created as soon as possible and only once in the game"""
|
||||||
self.x = 800
|
self.x = 800
|
||||||
self.y = 900
|
self.y = 900
|
||||||
self.mov_speed = 8
|
self.mov_speed = 8
|
||||||
@ -187,9 +201,11 @@ class Player:
|
|||||||
self.killcounter = 0
|
self.killcounter = 0
|
||||||
|
|
||||||
def rotate(self, angle: math.degrees)-> None:
|
def rotate(self, angle: math.degrees)-> None:
|
||||||
|
"""Rotate the player to the desired angle"""
|
||||||
self.angle += angle*self.rotate_speed
|
self.angle += angle*self.rotate_speed
|
||||||
|
|
||||||
def move(self, mov: int)-> None:
|
def move(self, mov: int)-> None:
|
||||||
|
"""Move the player in the direction he is looking or backward if mov is negative"""
|
||||||
new_x = self.x + self.mov_speed*mov*math.cos(math.radians(self.angle))
|
new_x = self.x + self.mov_speed*mov*math.cos(math.radians(self.angle))
|
||||||
new_y = self.y + self.mov_speed*mov*math.sin(math.radians(self.angle))
|
new_y = self.y + self.mov_speed*mov*math.sin(math.radians(self.angle))
|
||||||
if(game.test_collision(pygame.Rect(new_x-50, new_y-50, 100, 100), game.collisions) == False):
|
if(game.test_collision(pygame.Rect(new_x-50, new_y-50, 100, 100), game.collisions) == False):
|
||||||
@ -203,6 +219,8 @@ class Player:
|
|||||||
|
|
||||||
class Game:
|
class Game:
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
"""The main class that defines the game and it's behaviour,
|
||||||
|
it should be created only once during the game, as soon as possible"""
|
||||||
self.is_paused = False
|
self.is_paused = False
|
||||||
self.pnj_number = 20
|
self.pnj_number = 20
|
||||||
pygame.font.init()
|
pygame.font.init()
|
||||||
@ -211,23 +229,30 @@ class Game:
|
|||||||
self.load_collisions()
|
self.load_collisions()
|
||||||
|
|
||||||
def load_sprites(self):
|
def load_sprites(self):
|
||||||
|
"""Useful to load sprites in memory, should be called as soon as possible to avoid errors"""
|
||||||
self.perso_sprite = pygame.image.load("assets/Horse2.png")
|
self.perso_sprite = pygame.image.load("assets/Horse2.png")
|
||||||
self.map_sprite = pygame.image.load("assets/map3.png")
|
self.map_sprite = pygame.image.load("assets/map3.png")
|
||||||
|
|
||||||
def load_collisions(self):
|
def load_collisions(self):
|
||||||
|
"""Load all collisions stored in the sheets located in assets/collisions.csv,
|
||||||
|
should be called as soon as possible to avoid errors"""
|
||||||
self.collisions = []
|
self.collisions = []
|
||||||
with open('assets/collisions.csv', 'r', newline='') as file:
|
with open('assets/collisions.csv', 'r', newline='') as file:
|
||||||
reader = csv.DictReader(file)
|
reader = csv.DictReader(file)
|
||||||
for row in reader:
|
for row in reader:
|
||||||
self.collisions.append(pygame.Rect(float(row["starting_point_x"]), float(row["starting_point_y"]), float(row["len_x"]), float(row["len_y"])))
|
self.collisions.append(pygame.Rect(float(row["starting_point_x"]), float(row["starting_point_y"]), float(row["len_x"]), float(row["len_y"])))
|
||||||
|
|
||||||
def test_collision(self, objet: pygame.Rect, l_collisions) -> bool:
|
def test_collision(self, objet: pygame.Rect, l_collisions: list) -> bool:
|
||||||
|
"""This is an helper to check if there is collision between an object (pygame.Rect) and a list
|
||||||
|
of collisions containing pygame.Rect"""
|
||||||
for col in l_collisions:
|
for col in l_collisions:
|
||||||
if objet.colliderect(col):
|
if objet.colliderect(col):
|
||||||
return True
|
return True
|
||||||
return False
|
return False
|
||||||
|
|
||||||
def display_debug_text(self):
|
def display_debug_text(self):
|
||||||
|
"""This is only for debuging and should be called in display_all() if in debug mode,
|
||||||
|
but not on the final game"""
|
||||||
angle_surface = self.font.render("angle: "+str(player.angle), False, (0, 0, 0))
|
angle_surface = self.font.render("angle: "+str(player.angle), False, (0, 0, 0))
|
||||||
x_surface = self.font.render("x: "+str(player.x), False, (0, 0, 0))
|
x_surface = self.font.render("x: "+str(player.x), False, (0, 0, 0))
|
||||||
y_surface = self.font.render("y: "+str(player.y), False, (0, 0, 0))
|
y_surface = self.font.render("y: "+str(player.y), False, (0, 0, 0))
|
||||||
@ -239,7 +264,10 @@ class Game:
|
|||||||
#pygame.draw.rect(screen, "blue",pygame.Rect(player.x-50, player.y-50, 100, 100))
|
#pygame.draw.rect(screen, "blue",pygame.Rect(player.x-50, player.y-50, 100, 100))
|
||||||
|
|
||||||
def display_ui(self):
|
def display_ui(self):
|
||||||
|
"""Handle the display of the killcounter and time,
|
||||||
|
should be called at each frames"""
|
||||||
color = "black"
|
color = "black"
|
||||||
|
|
||||||
if player.killcounter < game.pnj_number:
|
if player.killcounter < game.pnj_number:
|
||||||
kill_surface = self.font.render("Kills: "+str(player.killcounter)+"\\"+str(game.pnj_number), False, color)
|
kill_surface = self.font.render("Kills: "+str(player.killcounter)+"\\"+str(game.pnj_number), False, color)
|
||||||
else:
|
else:
|
||||||
@ -255,6 +283,7 @@ class Game:
|
|||||||
screen.blit(time_surface, (740,0))
|
screen.blit(time_surface, (740,0))
|
||||||
|
|
||||||
def check_input(self):
|
def check_input(self):
|
||||||
|
"""Here we check all possible user input and execut and action acordingly"""
|
||||||
keys = pygame.key.get_pressed()
|
keys = pygame.key.get_pressed()
|
||||||
if keys[pygame.K_z]:
|
if keys[pygame.K_z]:
|
||||||
_ = player.move(1)
|
_ = player.move(1)
|
||||||
@ -266,31 +295,33 @@ class Game:
|
|||||||
player.rotate(1)
|
player.rotate(1)
|
||||||
|
|
||||||
def draw_player(self):
|
def draw_player(self):
|
||||||
#pygame.draw.rect(screen, "red", pygame.Rect(player.x, player.y, 50, 50))
|
"""Draw the player after applying a rotozoom in the center of the screen"""
|
||||||
img = pygame.transform.rotozoom(self.perso_sprite, -player.angle-90, 1)
|
img = pygame.transform.rotozoom(self.perso_sprite, -player.angle-90, 1)
|
||||||
screen.blit(img, (540-img.get_rect().centerx, 360-img.get_rect().centery))
|
screen.blit(img, (540-img.get_rect().centerx, 360-img.get_rect().centery))
|
||||||
|
|
||||||
def draw_village(self):
|
def draw_village(self):
|
||||||
|
"""Draw each sprite of the village"""
|
||||||
for s in village.get_village_sprites():
|
for s in village.get_village_sprites():
|
||||||
screen.blit(s[2], (540-player.x + s[0] - s[2].get_rect().centerx, 360-player.y + s[1] - s[2].get_rect().centery))
|
screen.blit(s[2], (540-player.x + s[0] - s[2].get_rect().centerx, 360-player.y + s[1] - s[2].get_rect().centery))
|
||||||
|
|
||||||
def display_all(self):
|
def display_all(self):
|
||||||
# fill the screen with a color to wipe away anything from last frame
|
"""This is a helper that will render everything needed on the screen"""
|
||||||
|
# fill the screen with a color to wipe away anything from last frame and draw the map
|
||||||
screen.fill("gray")
|
screen.fill("gray")
|
||||||
screen.blit(self.map_sprite, (540-player.x, 360-player.y))
|
screen.blit(self.map_sprite, (540-player.x, 360-player.y))
|
||||||
|
|
||||||
self.draw_village()
|
self.draw_village()
|
||||||
self.draw_player()
|
self.draw_player()
|
||||||
|
|
||||||
# We display it at the end so it's on top of all
|
# We display them at the end so it's on top of all
|
||||||
# self.display_debug_text()
|
#self.display_debug_text()
|
||||||
|
|
||||||
self.display_ui()
|
self.display_ui()
|
||||||
|
|
||||||
# flip() the display to put your work on screen
|
# flip() the display to put our work on screen
|
||||||
pygame.display.flip()
|
pygame.display.flip()
|
||||||
|
|
||||||
def game_over(self)->None:
|
def game_over(self)->None:
|
||||||
|
"""Create a gameover loop displaying the scoreboard and an image"""
|
||||||
screen.blit(pygame.image.load("assets/gameover.png"), (0,0))
|
screen.blit(pygame.image.load("assets/gameover.png"), (0,0))
|
||||||
seconds = pygame.time.get_ticks()/1000
|
seconds = pygame.time.get_ticks()/1000
|
||||||
seconds = seconds % (24 * 3600)
|
seconds = seconds % (24 * 3600)
|
||||||
@ -308,32 +339,34 @@ class Game:
|
|||||||
if event.type == pygame.QUIT:
|
if event.type == pygame.QUIT:
|
||||||
exit()
|
exit()
|
||||||
|
|
||||||
|
# Here we create the principal objects that will always be used during the game
|
||||||
player = Player()
|
player = Player()
|
||||||
game = Game()
|
game = Game()
|
||||||
#pnj = Pnj(800, 900, "assets/MiniPeasant.png", "south", Waypoint("H"), 3)
|
|
||||||
village = Village(game.pnj_number)
|
village = Village(game.pnj_number)
|
||||||
|
|
||||||
# start music
|
# start music with -1 meaning infinite loop
|
||||||
pygame.mixer.music.load("assets/music.mp3")
|
pygame.mixer.music.load("assets/music.mp3")
|
||||||
pygame.mixer.music.play(-1)
|
pygame.mixer.music.play(-1)
|
||||||
|
|
||||||
|
# Infinite game loop
|
||||||
while running:
|
while running:
|
||||||
# poll for events
|
# poll for events
|
||||||
# pygame.QUIT event means the user clicked X to close your window
|
# pygame.QUIT event means the user clicked X on the window
|
||||||
for event in pygame.event.get():
|
for event in pygame.event.get():
|
||||||
if event.type == pygame.QUIT:
|
if event.type == pygame.QUIT:
|
||||||
running = False
|
running = False
|
||||||
# make all the pnj move and do their pnj stuff
|
# make all the pnj move and do their pnj stuff
|
||||||
village.update_pnj()
|
village.update_pnj()
|
||||||
|
|
||||||
|
# Here we check all inputs from the user and do what is requiered
|
||||||
game.check_input()
|
game.check_input()
|
||||||
|
|
||||||
|
# Here we display everything to the screen
|
||||||
game.display_all()
|
game.display_all()
|
||||||
|
|
||||||
# limits FPS to 60
|
# limits FPS to 60
|
||||||
# dt is delta time in seconds since last frame, used for framerate-
|
# dt is delta time in seconds since last frame
|
||||||
# independent physics.
|
|
||||||
dt = clock.tick(60) / 1000
|
dt = clock.tick(60) / 1000
|
||||||
|
|
||||||
|
# cleanly quit the pygame instance
|
||||||
pygame.quit()
|
pygame.quit()
|
Loading…
Reference in New Issue
Block a user