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 self.clock.tick(self.fps) if self.scene : self.scene(self) self.scene = False pygame.display.update() # Mettre à jour l'affichage 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