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 self.logs = [] self.running = True 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["AZERTY"] = True self.globals["pause"] = 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.globals["speedrunlevels"] = [] # Levels where the player finished fast enough self.globals["overworld"] = True self.globals["nblevels"] = 0 self.globals["levelname"] = None self.globals["allunlocked"] = False self.globals["bgms"] = ["city.ogg","docks.ogg","isle.ogg","isle.ogg","docks.ogg","city.ogg"] self.init_inputs() self.scaleCamera() settings = {"sfx":1,"bgm":0.2} self.globals["bgmvolume"] = settings["bgm"] self.globals["sfxvolume"] = settings["sfx"] self.reinit_volumes() self.pasttime = time.time() # Je charge la scene de base scenes.boot(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["keys"] = { "escape":{ "keycode" : [pygame.K_ESCAPE,pygame.K_RETURN] # Code pygame de la touche en question }, "left":{ "keycode" : [pygame.K_LEFT] # Code pygame de la touche en question }, "right":{ "keycode" : [pygame.K_RIGHT] # Code pygame de la touche en question }, "up":{ "keycode" : [pygame.K_SPACE,pygame.K_UP] # Code pygame de la touche en question }, "down":{ "keycode" : [pygame.K_DOWN] # Code pygame de la touche en question } } if self.globals["AZERTY"]: # Azerty controls self.inputs["keys"]["left"]["keycode"].append(pygame.K_q) self.inputs["keys"]["right"]["keycode"].append(pygame.K_d) self.inputs["keys"]["up"]["keycode"].append(pygame.K_z) self.inputs["keys"]["down"]["keycode"].append(pygame.K_s) else: # Qwerty controls self.inputs["keys"]["left"]["keycode"].append(pygame.K_a) self.inputs["keys"]["right"]["keycode"].append(pygame.K_d) self.inputs["keys"]["up"]["keycode"].append(pygame.K_w) self.inputs["keys"]["down"]["keycode"].append(pygame.K_s) for key in self.inputs["keys"].keys(): self.inputs["keys"][key]["timer"] = 0 self.inputs["keys"][key]["realtime"] = 0 self.inputs["keys"][key]["pressed"] = False self.inputs["keys"][key]["counted"] = False self.inputs["joysticks"] = {} def check_events(self): 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 in self.inputs["keys"][i]["keycode"]: # Vérifie si une des touches du dico est préssée self.inputs["keys"][i]["pressed"] = True if event.type == pygame.KEYUP: for i in self.inputs["keys"].keys(): if event.key in self.inputs["keys"][i]["keycode"]: # Vérifie si une des touches du dico est préssée self.inputs["keys"][i]["pressed"] = False if event.type == pygame.JOYDEVICEADDED: index = event.device_index self.inputs["joysticks"][index] = pygame.joystick.Joystick(index) if event.type == pygame.JOYDEVICEREMOVED: index = event.instance_id del(self.inputs["joysticks"][index]) # Check for controller input for i in self.inputs["keys"].keys(): self.inputs["keys"][i]["counted"] = False for joy in self.inputs["joysticks"].values(): nbhats = joy.get_numhats() for i in range(nbhats): x,y = joy.get_hat(i) if x==-1: self.inputs["keys"]["left"]["counted"] = True if y==1: self.inputs["keys"]["up"]["counted"] = True if x==1: self.inputs["keys"]["right"]["counted"] = True if y==-1: self.inputs["keys"]["down"]["counted"] = True nbaxis = joy.get_numaxes() if nbaxis>=2: x = joy.get_axis(0) y = joy.get_axis(1) deadzone = 0.2 if x<-deadzone: self.inputs["keys"]["left"]["counted"] = True if x>deadzone: self.inputs["keys"]["right"]["counted"] = True if y>deadzone*3: self.inputs["keys"]["down"]["counted"] = True nbbuttons = joy.get_numbuttons() for i in range(8): if nbbuttons>=i and joy.get_button(i)==1: if i==0: # A button self.inputs["keys"]["up"]["counted"] = True if i>0 and i<=3: # B,X, and Y buttons self.inputs["keys"]["down"]["counted"] = True if i>5: # Start and Select self.inputs["keys"]["escape"]["counted"] = True for i in self.inputs["keys"].keys(): self.inputs["keys"][i]["counted"] = self.inputs["keys"][i]["counted"] or self.inputs["keys"][i]["pressed"] # 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]["counted"]: 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 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,depth=0): p = Particle(self,sprites,posx,posy,velx,vely,modvelx,modvely,flipx,flipy,fps) p.depth = depth 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)) surface = pygame.Surface((len(result)*9,9),flags=pygame.SRCALPHA) for i in range(len(result)): if result[i]: surface.blit(result[i],(i*9,0)) return surface