Overflown/gamedata/game.py

307 lines
12 KiB
Python

import pygame, os, math, time
import gamedata.definitions as lib
import gamedata.scenes as scenes
import gamedata.objects.gameloop as gameloop
from gamedata.objects.particles import Particle
class Game():
def __init__(self):
self.datadir = lib.get_save_dir("slime")
self.DISPLAY_WIDTH, self.DISPLAY_HEIGHT = 1280, 720
self.window = pygame.Surface((self.DISPLAY_WIDTH,self.DISPLAY_HEIGHT))
self.realwindow = pygame.display.set_mode((self.DISPLAY_WIDTH,self.DISPLAY_HEIGHT))
pygame.display.set_caption("Overflown")
pygame.init()
pygame.mixer.init()
self.fps = 60
font = "gamedata/font.ttf"
self.fontfile = pygame.font.Font(font,25)
self.fontfilesmall = pygame.font.Font(font,18)
self.fontfilebig = pygame.font.Font(font,60)
self.logs = []
self.running = True
self.init_inputs()
self.gameloop = gameloop.GameLoop() # Je crée une boucle de jeu
def load_image(filename):
return pygame.image.load(filename).convert_alpha()
self.sprite_lib = self.init_assets("gamedata/assets/",load_image) # Dico qui contient chaques images du dossier assets
pygame.display.set_icon(self.sprite_lib["icon.png"])
self.sound_lib = self.init_assets("gamedata/sounds/",pygame.mixer.Sound) # Pareil, mais pour les musiques / sons
self.fontmap = self.sprite_lib["fontmap.png"]
# Chargement des niveaux
def loadlvldata(mapfolder):
mapdico = {}
mapdico["name"] = mapfolder.split(os.sep)[-1]
mapdico["cover"] = None
mapdico["backgrounds"] = []
mapdico["data"] = None
mapdico["filler"] = None
mapdico["tilesets"] = {}
scanner = os.scandir(path=mapfolder)
for i in scanner: # Je check tout les fichiers du dossier
name = i.name
try:
if name.endswith(".png"):
if name=="cover.png":
mapdico["cover"] = pygame.image.load(i.path)
elif name.startswith("background"):
mapdico["backgrounds"].append({"sprite":pygame.image.load(i.path).convert_alpha(),"name":i.path})
elif name=="filler.png":
mapdico["filler"] = pygame.image.load(i.path).convert_alpha()
else:
mapdico["tilesets"][i.name] = pygame.image.load(i.path).convert_alpha()
except:
self.log("Erreur",mapfolder,name,"Fichier invalide")
if name=="map.json":
try:
with open(i.path,"r") as jsonfile:
mapdico["data"] = lib.json.loads(jsonfile.read())
except:
self.log("Erreur",mapfolder,name)
if mapdico["data"]:
mapdico["backgrounds"].sort(key=lambda x: x["name"])
return mapdico
return None
self.levels_lib = self.init_assets("gamedata/maps/",loadlvldata,recursive=False) # Je charge le dossier de maps
self.sound_volumes = {}
for i in self.sound_lib.keys():
self.sound_volumes[i] = 1
self.spriteLists = {} # Tiendra des listes préarrangées
self.scenes = scenes # Je stoque mes modules à l'intérieur de ma classe pour y avoir accès partout
self.scene = None
self.lib = lib
self.pygame = pygame
self.math = math
self.clock = pygame.time.Clock()
self.elapsedtime = 0
self.globals = {} # Un dico pour ranger toute les valeurs globales, pour communiquer entre objets par exemples
self.globals["camerax"] = 0
self.globals["cameray"] = 0
self.globals["scamerax"] = 0
self.globals["scameray"] = 0
self.globals["players"] = []
self.globals["hitpose"] = False
self.globals["finishedlevels"] = [] # Levels where the player went to the end
self.globals["completedlevels"] = [] # Levels where the player kicked all non-respawnable ennemies
self.scaleCamera()
settings = {"sfx":1,"bgm":1}
self.globals["bgmvolume"] = settings["bgm"]
self.globals["sfxvolume"] = settings["sfx"]
self.reinit_volumes()
self.pasttime = time.time()
# Je charge la scene de base
scenes.overworld(self)
def set_camera(self,posx,posy):
self.globals["camerax"], self.globals["cameray"] = posx,posy
def log(*args):
args[0].logs.append(" ".join(args[1:]))
print(" ".join(args[1:]))
def reinit_volumes(self):
for i in self.sound_lib.keys(): # J'applique de base les volumes
if i.startswith("sfx/"):
self.sound_lib[i].set_volume(self.globals["sfxvolume"]*self.sound_volumes[i])
if i.startswith("bgm/"):
self.sound_lib[i].set_volume(self.globals["bgmvolume"]*self.sound_volumes[i])
def set_volume(self,sound,newvolume):
self.sound_volumes[sound] = newvolume
self.reinit_volumes()
def game_loop(self):
self.dt = 1/self.fps
self.elapsedtime += self.dt
self.pasttime = time.time()
self.check_events() # Détecte les entrées clavier
self.window.fill((0)*3) # Remplis l'écran de noir
if self.gameloop: # Si j'ai une boucle de jeu, la lancer
self.gameloop.step(self) # La logique de la boucle
self.gameloop.draw(self) # L'affichage de la boucle
if self.scene :
self.scene(self)
self.scene = False
pygame.display.update() # Mettre à jour l'affichage
self.clock.tick(self.fps)
def init_inputs(self):
self.inputs = {}
self.inputs["unicode"] = ""
self.inputs["mouse"] = {
"pos" : [0,0], # Position
"rel" : [0,0], # Mouvement relatif
"click" : 0 # Timer du click
}
self.inputs["keys"] = {
"escape":{
"timer" : 0, # Timer de la touche
"realtime" : 0, # Temps réel préssé
"pressed" : False,
"keycode" : pygame.K_ESCAPE # Code pygame de la touche en question
},
"backspace":{
"timer" : 0, # Timer de la touche
"realtime" : 0, # Temps réel préssé
"pressed" : False,
"keycode" : pygame.K_BACKSPACE # Code pygame de la touche en question
},
"enter":{
"timer" : 0, # Timer de la touche
"realtime" : 0, # Temps réel préssé
"pressed" : False,
"keycode" : pygame.K_RETURN # Code pygame de la touche en question
},
"left":{
"timer" : 0, # Timer de la touche
"realtime" : 0, # Temps réel préssé
"pressed" : False,
"keycode" : pygame.K_q # Code pygame de la touche en question
},
"right":{
"timer" : 0, # Timer de la touche
"realtime" : 0, # Temps réel préssé
"pressed" : False,
"keycode" : pygame.K_d # Code pygame de la touche en question
},
"up":{
"timer" : 0, # Timer de la touche
"realtime" : 0, # Temps réel préssé
"pressed" : False,
"keycode" : pygame.K_z # Code pygame de la touche en question
},
"down":{
"timer" : 0, # Timer de la touche
"realtime" : 0, # Temps réel préssé
"pressed" : False,
"keycode" : pygame.K_s # Code pygame de la touche en question
}
}
self.no_unicode = [pygame.K_ESCAPE,pygame.K_BACKSPACE,pygame.K_RETURN]
def check_events(self):
self.inputs["unicode"] = ""
for event in pygame.event.get():
if event.type == pygame.QUIT:
self.running = False
if event.type == pygame.KEYDOWN:
for i in self.inputs["keys"].keys():
if event.key == self.inputs["keys"][i]["keycode"]: # Vérifie si une des touches du dico est préssée
self.inputs["keys"][i]["pressed"] = True
else:
if event.key not in self.no_unicode:
self.inputs["unicode"] = event.unicode # Je récupère la "lettre" de la touche préssée
if event.type == pygame.KEYUP:
for i in self.inputs["keys"].keys():
if event.key == self.inputs["keys"][i]["keycode"]: # Vérifie si une des touches du dico est préssée
self.inputs["keys"][i]["pressed"] = False
self.inputs["mouse"]["pos"] = pygame.mouse.get_pos() # Position
self.inputs["mouse"]["rel"] = pygame.mouse.get_rel() # Déplacement par rapport à la frame précédente
# Augmente le timer si la touche est préssée, le reset sinon
for i in self.inputs["keys"].keys():
if self.inputs["keys"][i]["pressed"]:
self.inputs["keys"][i]["timer"]+=1
self.inputs["keys"][i]["realtime"]+=self.dt
else:
self.inputs["keys"][i]["timer"]=0
self.inputs["keys"][i]["realtime"]=0
# Timer du click
boutons = pygame.mouse.get_pressed()
if boutons[0]: # Si click gauche
self.inputs["mouse"]["click"]+=1
else:
self.inputs["mouse"]["click"] = 0
def init_assets(self,path,function,recursive=True):
dico = {}
self.scan_dir(path,path,dico,function,recursive)
return dico
def scan_dir(self,dirpath,origin,dico,function,recursive=True):
scanner = os.scandir(path=dirpath)
for i in scanner: # Je passe à travers toutes les données d'un dossier, fichiers et sous dossiers compris
# i.path est le chemin du fichier, par exemple
# Si c'est une image, je l'importe et l'ajoute à la librairie
finalpath = i.path.replace("\\","/")
if i.is_file() or not recursive:
finalpath = finalpath.replace(origin,'') # J'enleve l'origine (dans ce cas 'assets/' car c'est redontant, tout les sprites sont dedans
dico[finalpath] = function(i.path)
# Si c'est un dossier, je répete l'opération mais à l'intérieur de celui ci
if i.is_dir() and recursive:
self.scan_dir(i.path,origin,dico,function,recursive)
scanner.close()
def getSpriteDir(self,directory,ext=".png",assetdir="sprite_lib"):
keys = (directory,ext,assetdir)
assetdir = getattr(self,assetdir) # Si je ne précise pas quel type de ressource, je cherche dans mes sprites
if keys in self.spriteLists.keys():
return self.spriteLists[keys]
else:
# Organise les sprites
sprite_list = []
index = 0
while directory+str(index)+ext in assetdir.keys():
sprite_list.append(assetdir[directory+str(index)+ext])
index+=1
# Le stoque pour éviter de les réorganiser à chaque fois
self.spriteLists[keys] = sprite_list
return sprite_list
def addParticle(self,sprites,posx,posy,velx=0,vely=0,modvelx=0,modvely=0,flipx=False,flipy=False,fps=15):
p = Particle(self,sprites,posx,posy,velx,vely,modvelx,modvely,flipx,flipy,fps)
self.gameloop.summon(p)
def scaleCamera(self,neww=None,newh=None):
if not neww:
neww=self.DISPLAY_WIDTH
if not newh:
newh=self.DISPLAY_HEIGHT
self.globals["cameraw"] = neww
self.globals["camerah"] = newh
self.globals["tempsubsurface"] = pygame.Surface((neww,newh))
def getchar(self,char,width=9,height=9):
chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@\"#&()*:;?!abcdefghijklmnopqrstuvwxyz"
charsperline = self.fontmap.get_width()//width
result = None
if char in chars:
index = chars.index(char)
line = index//charsperline
column = index%charsperline
result = self.pygame.Surface((width,height),flags=self.pygame.SRCALPHA)
result.blit(self.fontmap,[-width*column,-height*line])
return result
def getchars(self,chars):
result = []
for i in chars:
result.append(self.getchar(i))
return result