class Hub(pygame.sprite.Sprite): background = None def __init__(self, _engine, _map): super(Hub, self).__init__() if not Hub.background: Hub.background = loadImage('data/gfx/hub.png', alpha = True) self._engine = _engine self._map = _map self._minimap = Minimap(_engine, _map) self.screenUpdated() self.image = self.background.copy() self.rect = self.image.get_rect() def screenUpdated(self): self._resx, self._resy = self._engine.getResolution() self._minimap.screenUpdated() def draw(self, surface): surface.blit(self.image, (self.rect.left, self.rect.top)) def update(self): self.rect.centerx = self._resx / 2 self.image = self.background.copy() drawText(self.image, "%02d:%02d" % self._engine.timeLeft(), 13, (255, 255, 255), (40, 10)) drawText(self.image, "%02d fps" % self._engine.clock.get_fps(), 13, (255, 255, 255), (100, 10)) drawText(self.image, "Czolgi: %2d" % len(self._engine.players[0]['tanks']), 13, (255, 255, 255), (200, 10)) drawText(self.image, "Czolgi: %2d" % len(self._engine.players[1]['tanks']), 13, (255, 255, 255), (680, 10)) drawText(self.image, "%d : %d" % (self._engine.players[0]['score'], self._engine.players[1]['score']), 13, (255, 255, 255), (420, 10)) self._minimap.update() self._minimap.draw(self.image, (748, 10))
class Hub(pygame.sprite.Sprite): background = None def __init__(self, _engine, _map): super(Hub, self).__init__() if not Hub.background: Hub.background = loadImage('data/gfx/hub.png', alpha=True) self._engine = _engine self._map = _map self._minimap = Minimap(_engine, _map) self.screenUpdated() self.image = self.background.copy() self.rect = self.image.get_rect() def screenUpdated(self): self._resx, self._resy = self._engine.getResolution() self._minimap.screenUpdated() def draw(self, surface): surface.blit(self.image, (self.rect.left, self.rect.top)) def update(self): self.rect.centerx = self._resx / 2 self.image = self.background.copy() drawText(self.image, "%02d:%02d" % self._engine.timeLeft(), 13, (255, 255, 255), (40, 10)) drawText(self.image, "%02d fps" % self._engine.clock.get_fps(), 13, (255, 255, 255), (100, 10)) drawText(self.image, "Czolgi: %2d" % len(self._engine.players[0]['tanks']), 13, (255, 255, 255), (200, 10)) drawText(self.image, "Czolgi: %2d" % len(self._engine.players[1]['tanks']), 13, (255, 255, 255), (680, 10)) drawText( self.image, "%d : %d" % (self._engine.players[0]['score'], self._engine.players[1]['score']), 13, (255, 255, 255), (420, 10)) self._minimap.update() self._minimap.draw(self.image, (748, 10))
class Game(pyglet.window.Window): """The main game class.""" def __init__(self): self.focus = False # whether the mouse is locked or not self.debug = False # whether to start the debugger next frame self.setup_logger() self.setup_window() self.setup_keyhandler() self.setup_workers() self.setup_player() # Create a second window using PyGame for debugging or whatever it currently does. if config.Game.Minimap: self.minimap = Minimap(self.world_renderer, self.world_generator, self.world_client) if sys.platform == 'linux': os.nice(0) #pyglet.clock.set_fps_limit(variables.maximum_framerate) # set the maximum framerate # Removed in recent pyglet? This needs to be fixed or looked into # PyGlet does this weird thing where the on_draw method is only called if something happens. So if you don't press keys, the game appears to be running at 1 FPS. This prevents that and makes sure the game runs at least 60 FPS. Decreasing this number doesn't seem to make the game run faster. if config.Game.PreventSleep: pyglet.clock.schedule_interval(self.prevent_sleep, 1.0/60.0) # this prevents the application from 'smartly' sleeping when idle # Pre-generate a bunch of blocks and wait for them to finish so as to not clutter the logs. #for cx, cy in itertools.product(range(-5, 6), range(-5, 6)): # self.world_generator.request_chunk(cx, cy) self.log.info("Started.") self.times = [] if config.Debug.Game.FPS_SPAM: # Create the fps dictionary if we want to spam the current FPS to the console. self.fps = {} self.pregenerate() def setup_logger(self): # Create the logger. self.log = logging.getLogger() h = logging.StreamHandler() h.setLevel(config.Game.ConsoleLogLevel) fh = logging.FileHandler(config.LogFile) f = logging.Formatter(config.LogFormat) ff = logging.Formatter(config.LogFormat) h.setFormatter(f) fh.setFormatter(ff) self.log.addHandler(h) self.log.addHandler(fh) self.log.info("Started logging.") def setup_window(self): # Setup the window self.log.info("Initializing window...") super(Game, self).__init__(resizable=True) # initialize the window if config.Window.Width is not None and config.Window.Height is not None: self.set_size(config.Window.Width, config.Window.Height) # set the window size self.set_exclusive_mouse(False) # don't lock the cursor to this window to start with. self.set_vsync(variables.vsync) # turn off vsync so the framerate isn't limited at your refresh rate. Doesn't work on my computer anyways so I have it off. self.log.info("Initialized window.") # Initially set all the GL flags and what not. There are cases where this needs to be set every frame (like having two PyGlet windows. That's why the Minimap is written in PyGame. self.log.info("Configuring OpenGL...") glEnable(GL_DEPTH_TEST) # gpu can tell when stuff is infront or behind glDepthFunc(GL_LEQUAL) # anything closer should be drawn glEnable(GL_CULL_FACE) # don't draw faces that are behind something glFrontFace(GL_CCW) # used to determine the 'front' of a 2d shape glCullFace(GL_BACK) # remove the backs of faces glDepthRange(0, 1) # Show as much depth wise as possible self.log.info("Configured OpenGL.") def setup_keyhandler(self): # setup keyboard event handling so we can ask if a key is currently pressed rather than only knowing when a key is pressed. self.log.info("Setting up key state handler...") self.keys = pyglet.window.key.KeyStateHandler() # allows for a is_this_key_pressed check self.push_handlers(self.keys) # tells pyglet that it should keep track of when keys are pressed self.log.info("Setup key state handler.") def setup_workers(self): # Setup the world data storange. if sys.platform == 'linux': # Set the niceness a little lower on Linux so the main thread runs smooth at all times. os.nice(2) self.log.info("Setting up world data server...") self.world_server = world_data.WorldDataServer(self.log) # Stores the world data. self.world_server.start() # Start the server. self.world_client = self.world_server.get_main_client() # Get the client that is used by the main thread and to create more clients. self.log.info("Setup world data server.") if sys.platform == 'linux': # Set the niceness back to normal. os.nice(0) # Create some more clients for the various processes we will start. self.log.info("Getting extra world data clients for other processes...") generator_world_client = self.world_client.new_client("World Generator")[config.WorldRequestData.NewClient] renderer_world_client = self.world_client.new_client("World Renderer")[config.WorldRequestData.NewClient] self.log.info("Got extra world data clients for other processes.") # Create and start the world generator process. self.log.info("Creating and starting world generator...") if sys.platform == 'linux': os.nice(10) self.world_generator = world_generator.WorldGenerator(generator_world_client, self.log) # generates new chunks self.world_generator.start() self.log.info("Created and started world generator.") # Create and start the world renderer process. self.log.info("Creating and starting world renderer...") if sys.platform == 'linux': os.nice(8) self.world_renderer = WorldRenderer(renderer_world_client, self.log) # draws the world self.world_renderer.start() self.log.info("Created and started world renderer.") def setup_player(self): # Create the player object. self.log.info("Creating player...") self.player = player.PlayerManager(self.world_client) # the player and/or perspective self.log.info("Created player.") def pregenerate(self): # Generate and attempt to render an initial distance if the config file says so. This happens before drawing. if config.WorldGenerator.InitialDistance > 0: print("Generating initial radius of {}.".format(config.WorldGenerator.InitialDistance)) self.generate_radius(0, 0, config.WorldGenerator.InitialDistance) if config.WorldRenderer.InitialDistance > 0: print("Rendering initial radius of {}.".format(config.WorldRenderer.InitialDistance)) self.generate_radius(0, 0, config.WorldRenderer.InitialDistance) def generate_radius(self, cx, cy, radius): """Request to generate all the chunks in a radius around (cx, cy).""" r = range(-radius, radius+1) for ox, oy in itertools.product(r, r): x = ox + cx y = oy + cy self.world_generator.request_chunk(x, y) def render_radius(self, cx, cy, radius): """Request to render all the chunks in a radius around (cx, cy).""" r = range(-radius, radius+1) for ox, oy in itertools.product(r, r): x = ox + cx y = oy + cy if not self.world_renderer.is_rendered(x, y): self.world_renderer.request_chunk(x, y) def prevent_sleep(self,dt): """ prevents pyglet from not updating the screen when the application is idle. it makes for weird behaviour if this isn't used. """ pass def generate_view_sequence(self, cx, cy, view_distance): """This is intended to return the sequence to request chunks. However right now just the radius functions are being used instead. So if you want to generate things furthest to closest, this would be for that. But it's not used right now.""" seq = [] for distance in range(view_distance): if distance == 0: seq.append((cx, cy)) continue; for length in range(distance): if length == 0: seq.append((cx-distance, cy)) seq.append((cx+distance, cy)) else: seq.append((cx+distance, cy-length)) seq.append((cx-distance, cy-length)) seq.append((cx+distance, cy+length)) seq.append((cx-distance, cy+length)) for height in range(distance): if height == 0: seq.append((cx, cy-distance)) seq.append((cx, cy+distance)) else: seq.append((cx-height, cy+distance)) seq.append((cx-height, cy-distance)) seq.append((cx+height, cy+distance)) seq.append((cx+height, cy-distance)) return seq def generate_view(self): """ Requests chunks to be generated, rendered, and continue rendering any pre-calculated chunks. """ if config.Debug.Game.TimeRenderingChunks: before = time() self.world_renderer.render_queued() # Actually draw any chunks that were pre-calculated. if config.Debug.Game.TimeRenderingChunks: after = time() res = round(after - before, 5) if res >= 0.0005: print("Took {:.4} seconds to load_finished_chunks.".format(round(after - before, 4))) self.times.append(res) cx, cy = self.world_client.abs_block_to_chunk_block(*self.player.standing_on())[0] # Get the chunk the player is in. # Generate all the chunks in the generation distance. self.generate_radius(cx, cy, config.WorldGenerator.Distance) self.render_radius(cx, cy, config.WorldRenderer.Distance) # Using a funky pattern because why not. #for x, y in self.generate_view_sequence(cx, cy, config.WorldGenerator.Distance): #self.world_generator.request_chunk(x, y) #for x, y in self.generate_view_sequence(cx, cy, config.WorldRenderer.Distance): #self.world_renderer.request_chunk(x, y) def check_user_input(self): """ Check for keys that are currently pressed and act accordingly. This is not for when a key is pressed for the first time. This is only if you want to know when a key is held down. See on_key_press() and on_key_release(). """ x = 0.0 y = 0.0 z = 0.0 # player movement if self.keys[pyglet.window.key.LEFT] or self.keys[pyglet.window.key.A]: x += variables.move_speed[0] #self.log.debug("Moving left.") if self.keys[pyglet.window.key.RIGHT] or self.keys[pyglet.window.key.D]: x -= variables.move_speed[0] #self.log.debug("Moving right.") if self.keys[pyglet.window.key.UP] or self.keys[pyglet.window.key.W]: y += variables.move_speed[2] #self.log.debug("Moving forward.") if self.keys[pyglet.window.key.DOWN] or self.keys[pyglet.window.key.S]: y -= variables.move_speed[2] #self.log.debug("Moving backward.") if self.keys[pyglet.window.key.SPACE]: z += variables.move_speed[1] #self.log.debug("Moving up") if self.keys[pyglet.window.key.LSHIFT]: z -= variables.move_speed[1] #self.log.debug("Moving down") # only try to move the player if a button was pressed if x or y or z: self.player.move(x,y,z) def on_mouse_scroll(self, x, y, scroll_x, scroll_y): """Is called when the scroll wheel is used.""" if scroll_y < 0: variables.move_speed[0] -= 1 variables.move_speed[1] -= 1 variables.move_speed[2] -= 1 elif scroll_y > 0: variables.move_speed[0] += 1 variables.move_speed[1] += 1 variables.move_speed[2] += 1 for i, m in enumerate(variables.move_speed): if m < 0: variables.move_speed[i] = 0.001 if variables.debug.print_move_speed: print("New move speed: {}, {}, {}".format(round(variables.move_speed[0], 4), round(variables.move_speed[1], 4), round(variables.move_speed[2], 4))) def on_mouse_press(self, x, y, button, modifiers): """Is called when a mouse button is pressed.""" if button == pyglet.window.mouse.LEFT: if not self.focus: self.log.debug("Taking focus.") self.set_exclusive_mouse(True) self.focus = True def on_mouse_motion(self,x,y,dx,dy): """Is called when the mouse moves in the window.""" if self.focus: self.player.look(-dy*variables.mouse_sensitivity[0],dx*variables.mouse_sensitivity[1]) def on_key_press(self,symbol,modifiers): """Is called when a key is pressed; and only when a key is pressed down, not called repeatedly when the key is held. See check_user_input() for held keys.""" #if symbol == pyglet.window.key.F: #self.player.flying() if symbol == pyglet.window.key.ESCAPE: # Let the mouse out of the window. self.log.debug("Dropping focus.") self.focus = False self.set_exclusive_mouse(False) elif symbol == pyglet.window.key.PLUS: # Increase the number of blocks drawn per frame. config.WorldRenderer.MaxBlocksPerFrame += 10 print("MaxBlocksPerFrame: {}".format(config.WorldRenderer.MaxBlocksPerFrame)) elif symbol == pyglet.window.key.MINUS: # Decrease the number of blocks drawn per frame. config.WorldRenderer.MaxBlocksPerFrame = max(0, config.WorldRenderer.MaxBlocksPerFrame - 10) print("MaxBlocksPerFrame: {}".format(config.WorldRenderer.MaxBlocksPerFrame)) elif symbol == pyglet.window.key.D and modifiers & pyglet.window.key.MOD_ALT: # Open the debugger on the beginning of the next frame. self.set_exclusive_mouse(False) self.focus = False self.debug = True elif symbol == pyglet.window.key.O: # Return the chunk the player is standing in and whether it has been requested to be rendered. cx, cy = self.world_client.abs_block_to_chunk_block(*self.player.standing_on())[0] print("Chunk {}, {}".format(cx, cy)) if (cx, cy) in self.world_renderer.requested: print("Chunk is in requested") def on_resize(self, width, height): """Called when window is resized.""" config.Window.Width = width config.Window.Height = height super(Game, self).on_resize(width, height) print("Resizing window: {}, {}".format(config.Window.Width, config.Window.Height)) def on_close(self): """Called when the game closes or is killed with C-c on the command line.""" self.close() self.world_generator.stop() self.world_server.stop() self.world_renderer.stop() if config.Game.Minimap: self.minimap.close() if len(self.times) > 0: average = round(sum(self.times) / len(self.times), 5) print("Average load_finished_chunks time: {}".format(average)) def on_draw(self): """Called every frame.""" # Print the FPS spam if it's enabled. if __debug__ and config.Debug.Game.FPS_SPAM: offset = 10000 if dt > 0: self.fps[round(time()*offset)] = 1.0 / dt / 100 #self.fps.append(pyglet.clock.get_fps()) # actually calculating the framerate seems to be more accurate. while time() - list(self.fps.keys())[0]/offset >= config.Debug.Game.FPS_SPAM_SPAN: del self.fps[list(self.fps.keys())[0]] if len(self.fps) > 0: avgerage = round(sum(self.fps.values()) / len(self.fps), 2) print("Average FPS: {}".format(avgerage)) minimum = round(min(self.fps.values()), 2) print("Minimum FPS: {}".format(minimum)) # if the debug flag is set, start the debugger; only when the app isn't running with the -O flag. if __debug__ and self.debug: self.debug = False pdb.set_trace() self.check_user_input() self.clear() # Clear the screen glHint(GL_PERSPECTIVE_CORRECTION_HINT, GL_NICEST) # Maybe change OpenGL settings to look nicer??? self.player.draw_perspective() # Set the FOV, move the player 'camera', and looking direction. self.generate_view() # Do all the generating of chunks and rendering of chunks. self.world_renderer.draw() # Draw the world. # Update the second window if it's enabled. if config.Game.Minimap: self.minimap.update() self.minimap.draw()
def main(): pygame.init() # menu check server or client mennu() #zoom zoom = ZOOM kof = 0 scale = 1 #draw displey screen = pygame.display.set_mode(DISPLAY) # minimap minimap = Surface((MINIMAPSIZE,MINIMAPSIZE)) # generation first position (maps, x, y) = generationmap(zoom) # get player start position midx = WIN_WIDTH/2 midy = WIN_HEIGHT/2 # minimap create minmap = Minimap() #create this user myplayer = player.Player(midx, midy, True, False, CANNON) play_parts = pygame.sprite.Group() play_parts.add(myplayer) #this user bullets player_bul = pygame.sprite.Group() zoom1 = ZOOM + 0.0 # clock for fps timer = pygame.time.Clock() # zoom press iszoomincres = False iszoomdecres = False # socket create sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) sock.connect((HOST, PORT)) #Buttons upispres = 0 p = False pausa = createbuttons() cannon = createbuttons() shild = createbuttons() # lists for reciven and sending recivepl = [] reciveen = [] reckilen = [] mustdelete = [] damagesend = [] sendparts = [] mybulsend = [] # send list of this user bullets data = "" while True: #draw background on the screen screen.fill(BACKGROUND_COLOR) # if zoom pressed zoom increes if iszoomincres == True: if scale<MAXSCALE: zoom1 += 2 scale = zoom1/ZOOM # if zoom pressed zoom decrease elif iszoomdecres == True: if scale>MINSCALE: zoom1 -= 2 scale = zoom1/ZOOM #remove and zooming player for enemy in reciveen: for kilen in reckilen: if enemy[3] == kilen: reciveen.remove(enemy) x = enzooming(enemy[:3], myplayer.rect.center, myplayer.myposx, myplayer.myposy, scale) pldraw(screen, x, EN_COLOR) #SHILD # check shilds #shild_dam((0,0), play_parts) # shild restore #shild_rest(play_parts) # SHOTTING and all for this player bullets # and killed enemies mybulsend = [] damagesend = [] #update and draw bullets for bul in player_bul: bul.updat() x = bzooming((bul.rect.x, bul.rect.y), myplayer.rect.center, 0, 0, scale) drawscbull(screen, x, scale) # check if bullet find a target for encord in reciveen: (xmin, xmax, ymin, ymax) = findsquere(encord) #if bullet in the enemy squere kill bullet if bul.rect.x >= xmin+3 and bul.rect.x <= xmax-3: if bul.rect.y >= ymin+3 and bul.rect.y <= ymax-3: damagesend.append(encord[3]) reciveen.remove(encord) bul.kill() #add bullet in send list mybulsend.append((bul.rect.x+int(myplayer.myposx), bul.rect.y+int(myplayer.myposy), bul.id, bul.num)) #CREATE PACKAGE FOR SEND #send thisplayer package data = "PL" myplayer.shiftmove() data += pickle.dumps(myplayer.sendp) #serialize thisplayer bullets data += "BUL" data += pickle.dumps(mybulsend) #serialize damage of thisplayer bullets data += "DAM" data += pickle.dumps(damagesend) data += "PAR" data += pickle.dumps(sendparts) #send and recive package sock.send(data) rec = sock.recv(BUFF) #DECODE PACKAGE if rec[:2] == "PL": rec = rec[2:] cords = rec.split("BUL") #players players = pickle.loads(cords[0]) for pl in players: if pl[3]!= myplayer.id: for myp in recivepl: if pl[3]== myp[3]: recivepl.remove(myp) recivepl.append(pl) cor = cords[1].split("ENE") #take bullet other player and zoomed bullets = pickle.loads(cor[0]) if bullets != []: for b in bullets: temp = False for pl in play_parts: if pl.id == b[2]: temp = True if temp == False: x = bzooming(b[:2], myplayer.rect.center, myplayer.myposx, myplayer.myposy, scale) drawscbull(screen, x, scale) co = cor[1].split("KIL") #take enemy cords enemies = pickle.loads(co[0]) for enemy in enemies: for myenemy in reciveen: if enemy[3] == myenemy[3]: reciveen.remove(myenemy) reciveen.append(enemy) cor = co[1].split("ENB") # take id killen enemies reckilen = pickle.loads(cor[0]) #take enemy bullets and zoomed enbul = pickle.loads(cor[1]) for b in enbul: x = bzooming(b, myplayer.rect.center, myplayer.myposx, myplayer.myposy, scale) drawscbull(screen, x, scale) # move recived players for p in recivepl: if p[4] == False: move(p,myplayer.myposx, myplayer.myposy) p[4] = True x = enzooming(p[:3], myplayer.rect.center, myplayer.myposx, myplayer.myposy, scale) pldraw(screen, x, PL_COLOR) # draw recived enemies for myenemy in reciveen: if myenemy[4] == False: myenemy = move(myenemy,myplayer.myposx, myplayer.myposy) myenemy[4] = True #draw and update thisplayer object pos = mouse.get_pos() for pl in play_parts: pl.update(0, 1, myplayer) if pl.id == myplayer.id: pl.draw(screen, pos, scale) else: x = enzooming(pl.points, myplayer.rect.center, myplayer.myposx, myplayer.myposy, scale) pldraw(screen, x, PL_COLOR) # ALL EVENTS for gamemode for e in pygame.event.get(): # check multipress keystate = pygame.key.get_pressed() # exit if e.type == QUIT: sock.close() pygame.quit() sys.exit() # start moving and rotating elif e.type == KEYDOWN: if keystate[K_ESCAPE]: pygame.quit() sys.exit() if keystate[K_UP]: for part in play_parts: part.ismoveup = True if keystate[K_DOWN]: for part in play_parts: part.ismovedown = True if keystate[K_LEFT]: for part in play_parts: part.isrotateleft = True if keystate[K_RIGHT]: for part in play_parts: part.isrotateright = True if keystate[K_1]: iszoomincres = True if keystate[K_2]: iszoomdecres = True # stop moving and rotating elif e.type == KEYUP: if e.key == K_UP: for part in play_parts: part.ismoveup = False if e.key == K_DOWN: for part in play_parts: part.ismovedown = False if e.key == K_LEFT: for part in play_parts: part.isrotateleft = False if e.key == K_RIGHT: for part in play_parts: part.isrotateright = False if e.key == K_1: iszoomincres = False if e.key == K_2: iszoomdecres = False #player shoot elif e.type == pygame.MOUSEBUTTONDOWN: # if button press (CREATEMODE) if e.type == pygame.MOUSEBUTTONDOWN: # mouse position pos = pygame.mouse.get_pos() p = startpause(p, pausa, pos) # if pausa reset all player objects angle if p == True: print myplayer.points alfa = myplayer.alfa if alfa%360!=0: alfa = alfa%360 for part in play_parts: part.alfa = 0 part.rottoanhle(-alfa, myplayer) part.update(0, 1 , myplayer) #pausa createmode begin while p == True: screen.fill((100,100,100)) #print myplayer.rect update_display(screen, cannon, 100, 2, WIN_WIDTH/2) update_display(screen, shild, 140, 2, WIN_WIDTH/2) update_display(screen, pausa, 10, 2, 0) pos = pygame.mouse.get_pos() (p,upispres, mypl) = createmode(screen, p, pausa, cannon, shild, pos, upispres, play_parts, ZOOM, kof, myplayer) if mypl != 0: play_parts.add(mypl) if upispres == CANNON: drawpoligon(screen, pos) elif upispres == SHILD: drawrect(screen, pos) for pl in play_parts: pl.draw(screen, pos, 1) pygame.display.flip() # player shoot for pl in play_parts: if pl.ptype == CANNON: player_bul.add(pl.shoot(pos)) #pausa button update_display(screen, pausa, 10, 1,0) drawtext(screen, timer) minmap.draw(screen, reciveen, myplayer, recivepl) pygame.display.flip()
class Game: """ Class that holds game info, handles user inputs and draws every game component on the screen """ def __init__(self): # X and Y start position self.posX = 3.0 self.posY = 10.0 # Initial direction vector self.dirX = -1.0 self.dirY = 0.0 # The 2d raycaster version of camera plane self.planeX = 0.0 self.planeY = 0.66 self.textures = [] self.sprites = {} # Load some textures and sprites into Spritesheet instances self.textures.append(SpriteSheet("assets/eagle.png")) #1 self.textures.append(SpriteSheet("assets/redbrick.png")) #2 self.textures.append(SpriteSheet("assets/purplestone.png")) #3 self.textures.append(SpriteSheet("assets/greystone.png")) #4 self.textures.append(SpriteSheet("assets/bluestone.png")) #5 self.textures.append(SpriteSheet("assets/mossy.png")) #6 self.textures.append(SpriteSheet("assets/wood.png")) #7 self.textures.append(SpriteSheet("assets/colorstone.png")) #8 self.projectile_image = SpriteSheet("assets/projectile.png") self.player_image = SpriteSheet("assets/player.png") # Load static floor image self.floor_img = pygame.image.load("assets/floor.png").convert() self.minimap = Minimap(5) self.font = pygame.font.Font('freesansbold.ttf', 10) self.font_large = pygame.font.Font('freesansbold.ttf', 26) self.time = 0 # time of current frame self.oldTime = 0 #time of previous frame self.frameTime = 0.0 self.my_id = -1 self.sprites[self.my_id] = [] self.shoot = False self.is_connected = False self.done = False self.game_map = c.game_map self.show_scoreboard = False self.scoreboard = Scoreboard() self.message = "" self.old_message_time = 0 self.message_time = 0 self.scoreboard_data = {} self.zBuffer = [] self.show_cursor = False def input_handle(self): """ Handles user keyboard and mouse inputs """ events = pygame.event.get() # speed modifiers moveSpeed = self.frameTime * 5.0 # The constant value is in squares/second rotSpeed = self.frameTime * 3.0 # The constant value is in radians/second key = pygame.key.get_pressed() # Move forward if no wall in front of you if key[pygame.K_w]: if(self.game_map[int(self.posX + self.dirX * moveSpeed)][int(self.posY)] == False): self.posX += self.dirX * moveSpeed if(self.game_map[int(self.posX)][int(self.posY + self.dirY * moveSpeed)] == False): self.posY += self.dirY * moveSpeed # Move backwards if no wall behind you if key[pygame.K_s]: if(self.game_map[int(self.posX - self.dirX * moveSpeed)][int(self.posY)] == False): self.posX -= self.dirX * moveSpeed if(self.game_map[int(self.posX)][int(self.posY - self.dirY * moveSpeed)] == False): self.posY -= self.dirY * moveSpeed if key[pygame.K_d]: oldDirX = self.dirX self.dirX = self.dirX * cos(-pi/2) - self.dirY * sin(-pi/2) self.dirY = oldDirX * sin(-pi/2) + self.dirY * cos(-pi/2) oldPlaneX = self.planeX self.planeX = self.planeX * cos(-pi/2) - self.planeY * sin(-pi/2) self.planeY = oldPlaneX * sin(-pi/2) + self.planeY * cos(-pi/2) if(self.game_map[int(self.posX + self.dirX * moveSpeed)][int(self.posY)] == False): self.posX += self.dirX * (moveSpeed/2) if(self.game_map[int(self.posX)][int(self.posY + self.dirY * moveSpeed)] == False): self.posY += self.dirY * (moveSpeed/2) oldDirX = self.dirX self.dirX = self.dirX * cos(pi/2) - self.dirY * sin(pi/2) self.dirY = oldDirX * sin(pi/2) + self.dirY * cos(pi/2) oldPlaneX = self.planeX self.planeX = self.planeX * cos(pi/2) - self.planeY * sin(pi/2) self.planeY = oldPlaneX * sin(pi/2) + self.planeY * cos(pi/2) if key[pygame.K_a]: oldDirX = self.dirX self.dirX = self.dirX * cos(-pi/2) - self.dirY * sin(-pi/2) self.dirY = oldDirX * sin(-pi/2) + self.dirY * cos(-pi/2) oldPlaneX = self.planeX self.planeX = self.planeX * cos(-pi/2) - self.planeY * sin(-pi/2) self.planeY = oldPlaneX * sin(-pi/2) + self.planeY * cos(-pi/2) if(self.game_map[int(self.posX - self.dirX * moveSpeed)][int(self.posY)] == False): self.posX -= self.dirX * moveSpeed if(self.game_map[int(self.posX)][int(self.posY - self.dirY * moveSpeed)] == False): self.posY -= self.dirY * moveSpeed oldDirX = self.dirX self.dirX = self.dirX * cos(pi/2) - self.dirY * sin(pi/2) self.dirY = oldDirX * sin(pi/2) + self.dirY * cos(pi/2) oldPlaneX = self.planeX self.planeX = self.planeX * cos(pi/2) - self.planeY * sin(pi/2) self.planeY = oldPlaneX * sin(pi/2) + self.planeY * cos(pi/2) # Rotate to the right if key[pygame.K_RIGHT]: # Both camera direction and camera plane must be rotated oldDirX = self.dirX self.dirX = self.dirX * cos(-rotSpeed) - self.dirY * sin(-rotSpeed) self.dirY = oldDirX * sin(-rotSpeed) + self.dirY * cos(-rotSpeed) oldPlaneX = self.planeX self.planeX = self.planeX * cos(-rotSpeed) - self.planeY * sin(-rotSpeed) self.planeY = oldPlaneX * sin(-rotSpeed) + self.planeY * cos(-rotSpeed) # Rotate to the left if key[pygame.K_LEFT]: # Both camera direction and camera plane must be rotated oldDirX = self.dirX self.dirX = self.dirX * cos(rotSpeed) - self.dirY * sin(rotSpeed) self.dirY = oldDirX * sin(rotSpeed) + self.dirY * cos(rotSpeed) oldPlaneX = self.planeX self.planeX = self.planeX * cos(rotSpeed) - self.planeY * sin(rotSpeed) self.planeY = oldPlaneX * sin(rotSpeed) + self.planeY * cos(rotSpeed) for event in events: if event.type == pygame.QUIT: self.done = True pygame.quit() elif event.type == pygame.KEYDOWN: if key[pygame.K_SPACE]: self.shoot = True if not self.is_connected: self.sprites[self.my_id].append(Sprite(self.posX, self.posY, self.dirX, self.dirY, self.projectile_image, 0.4)) elif event.type == pygame.MOUSEBUTTONDOWN: if event.button == 1: self.shoot = True if not self.is_connected: self.sprites[self.my_id].append(Sprite(self.posX, self.posY, self.dirX, self.dirY, self.projectile_image, 0.4)) if event.button == 3: if not self.show_cursor: pygame.mouse.set_visible(True) pygame.event.set_grab(False) self.show_cursor = True elif self.show_cursor: pygame.mouse.set_visible(False) pygame.event.set_grab(True) self.show_cursor = False if key[pygame.K_ESCAPE]: self.done = True pygame.quit() self.show_scoreboard = False if key[pygame.K_TAB]: self.show_scoreboard = True (movement_x, movement_y) = pygame.mouse.get_rel() if movement_x: rotSpeed *= (-movement_x/25) oldDirX = self.dirX self.dirX = self.dirX * cos(rotSpeed) - self.dirY * sin(rotSpeed) self.dirY = oldDirX * sin(rotSpeed) + self.dirY * cos(rotSpeed) oldPlaneX = self.planeX self.planeX = self.planeX * cos(rotSpeed) - self.planeY * sin(rotSpeed) self.planeY = oldPlaneX * sin(rotSpeed) + self.planeY * cos(rotSpeed) def cast_sprites(self, dict_sprites, screen): """ Cast sprites into a surface Args: dict_sprites (dict): dictionary containing all the sprites, including players and projectiles screen (surface): the pygame surface to draw the sprites """ sprites = [] # Check for you own sprite so you dont cast your player for player_id, sprites_list in dict_sprites.items(): for sprite in sprites_list: if not (int(player_id) == self.my_id and sprite.is_player): sprites.append(sprite) # TODO: Sort sprites from far to close # After sorting the sprites, do the projection and draw them for sprite in sprites: # Translate sprite position to relative to camera spriteX = sprite.x - self.posX spriteY = sprite.y - self.posY # Transform sprite with the inverse camera matrix # [ planeX dirX ] -1 [ dirY -dirX ] # [ ] = 1/(planeX*dirY-dirX*planeY) * [ ] # [ planeY dirY ] [ -planeY planeX ] invDet = 1.0 / (self.planeX * self.dirY - self.dirX * self.planeY) # Required for correct matrix multiplication transformX = invDet * (self.dirY * spriteX - self.dirX * spriteY) transformY = invDet * (-self.planeY * spriteX + self.planeX * spriteY) # This is actually the depth inside the screen, that what Z is in 3D, the distance of sprite to player, matching sqrt(spriteDistance[i]) if transformY == 0: transformY = 0.001 spriteScreenX = int((c.SCREEN_WIDTH / 2) * (1 + transformX / transformY)) # Parameters for scaling and moving the sprites uDiv = sprite.uDiv vDiv = sprite.vDiv vMove = sprite.vMove vMoveScreen = int(vMove / transformY) # Calculate height of the sprite on screen spriteHeight = abs(int(c.SCREEN_HEIGHT / (transformY))) / vDiv # Using "transformY" instead of the real distance prevents fisheye # Calculate lowest and highest pixel to fill in current stripe drawStartY = -spriteHeight / 2 + c.SCREEN_HEIGHT / 2 + vMoveScreen if drawStartY < 0: drawStartY = 0 drawEndY = spriteHeight / 2 + c.SCREEN_HEIGHT / 2 + vMoveScreen if drawEndY >= c.SCREEN_HEIGHT: drawEndY = c.SCREEN_HEIGHT - 1 # Calculate width of the sprite spriteWidth = abs( int (c.SCREEN_HEIGHT / (transformY))) / uDiv drawStartX = -spriteWidth / 2 + spriteScreenX drawEndX = spriteWidth / 2 + spriteScreenX if drawEndX >= c.SCREEN_WIDTH: drawEndX = c.SCREEN_WIDTH - 1 # Get sprite image size image_width = sprite.image.get_width() image_height = sprite.image.get_height() for stripe in range(int(drawStartX), int(drawEndX)): texX = int(256 * (stripe - (-spriteWidth / 2 + spriteScreenX)) * image_width / spriteWidth) / 256 if transformY > 0 and drawStartX >= 0 and drawEndX < c.SCREEN_WIDTH and transformY < self.zBuffer[stripe]: tmp_image1 = sprite.image.get_image(int(round(texX)), 0, 1, image_height) tmp_image = pygame.transform.scale(tmp_image1, (1, int(spriteHeight))) # Darken sprites that are far from the player darken_percent = (1 - (spriteHeight*30/c.SCREEN_HEIGHT)) dark = pygame.Surface(tmp_image.get_size(), pygame.SRCALPHA).convert_alpha() darkness = (darken_percent*255) if darkness > 255: darkness = 255 elif darkness < 0: darkness = 0 dark.blit(tmp_image, (0 , 0)) dark.fill((darkness, darkness, darkness, 0), None, pygame.BLEND_RGBA_SUB) #print("drawx: {} drawy: {}".format(drawStartX, drawStartY)) screen.blit(dark, (stripe , drawStartY)) middle_stripe = int(drawStartX + (drawEndX - drawStartX)//2) if stripe == middle_stripe and sprite.is_player: ratio = 4 * (spriteHeight / image_height) text_default = self.font_large.render(sprite.name, True, c.GREEN) scaled_width = int(ratio*text_default.get_width()) scaled_height = int(ratio*text_default.get_height()) text = pygame.transform.scale(text_default, (scaled_width, scaled_height)) screen.blit(text, (middle_stripe - text.get_width()//2, drawStartY - text.get_height())) # print("{} {} {}".format(drawEndX//2, drawStartY, inside)) if not self.is_connected: sprite.move() if self.game_map[int(sprite.x)][int(sprite.y)]: self.sprites[self.my_id].remove(sprite) def draw(self, screen): """ Draws all the game components Args: screen (surface): the surface to draw the game on """ pygame.draw.rect(screen, c.BLACK, (0, 0, c.SCREEN_WIDTH, c.SCREEN_HEIGHT//2)) floor_size = self.floor_img.get_size() if floor_size[0] != c.SCREEN_WIDTH or floor_size[1] != c.SCREEN_HEIGHT//2: self.floor_img = pygame.transform.scale(self.floor_img, (c.SCREEN_WIDTH, c.SCREEN_HEIGHT//2)) self.floor_img.convert() screen.blit(self.floor_img, (0, c.SCREEN_HEIGHT//2, c.SCREEN_WIDTH, c.SCREEN_HEIGHT//2)) self.zBuffer = [] for x in range(0, c.SCREEN_WIDTH): # Calculate ray position and direction cameraX = 2 * x / c.SCREEN_WIDTH - 1 # x-coordinate in camera space rayDirX = self.dirX + self.planeX*cameraX rayDirY = self.dirY + self.planeY*cameraX # Which box of the map we're in mapX = int(self.posX) mapY = int(self.posY) # Length of ray from current position to next x or y-side sideDistX = 0.0 sideDistY = 0.0 # Length of ray from one x or y-side to next x or y-side if rayDirX == 0: rayDirX = 0.001 if rayDirY == 0: rayDirY = 0.001 deltaDistX = abs(1 / rayDirX) deltaDistY = abs(1 / rayDirY) perpWallDist = 0.0 # What direction to step in x or y-direction (either +1 or -1) stepX = -1 stepY = -1 hit = 0 # was there a wall hit? side = 0 # was a NS or a EW wall hit? # Calculate step and initial sideDist if rayDirX < 0: stepX = -1 sideDistX = (self.posX - mapX) * deltaDistX else: stepX = 1 sideDistX = (mapX + 1.0 - self.posX) * deltaDistX if rayDirY < 0: stepY = -1 sideDistY = (self.posY - mapY) * deltaDistY else: stepY = 1 sideDistY = (mapY + 1.0 - self.posY) * deltaDistY # Perform DDA while hit == 0: # jump to next map square, OR in x-direction, OR in y-direction if sideDistX < sideDistY: sideDistX += deltaDistX mapX += stepX side = 0 else: sideDistY += deltaDistY mapY += stepY side = 1 # Check if ray has hit a wall if(self.game_map[mapX][mapY] > 0): hit = 1 # Calculate distance of perpendicular ray (Euclidean distance will give fisheye effect!) if side == 0: perpWallDist = (mapX - self.posX + (1 - stepX) / 2) / rayDirX else: perpWallDist = (mapY - self.posY + (1 - stepY) / 2) / rayDirY # Calculate height of line to draw on screen if perpWallDist == 0: perpWallDist = 0.000001 lineHeight = int(c.SCREEN_HEIGHT / perpWallDist) if lineHeight > 10*c.SCREEN_HEIGHT: lineHeight = 10*c.SCREEN_HEIGHT # Calculate lowest and highest pixel to fill in current stripe drawStart = -lineHeight / 2 + c.SCREEN_HEIGHT / 2 if drawStart < 0: drawStart = 0 drawEnd = lineHeight / 2 + c.SCREEN_HEIGHT / 2 if drawEnd >= c.SCREEN_HEIGHT: drawEnd = c.SCREEN_HEIGHT - 1 # Texturing calculations texNum = self.game_map[mapX][mapY] - 1 # 1 subtracted from it so that texture 0 can be used! # Calculate value of wallX wallX = 0.0 # Where exactly the wall was hit if side == 0: wallX = self.posY + perpWallDist * rayDirY else: wallX = self.posX + perpWallDist * rayDirX wallX -= floor((wallX)) # X coordinate on the texture texX = int(wallX * c.TEX_WIDTH) if(side == 0 and rayDirX > 0): texX = c.TEX_WIDTH - texX - 1 if(side == 1 and rayDirY < 0): texX = c.TEX_WIDTH - texX - 1 # TODO: an integer-only bresenham or DDA like algorithm could make the texture coordinate stepping faster # How much to increase the texture coordinate per screen pixel step = 1.0 * c.TEX_HEIGHT / lineHeight # Starting texture coordinate texPos = (drawStart - c.SCREEN_HEIGHT / 2 + lineHeight / 2) * step image1 = self.textures[texNum].get_image(int(round(texX)), 0, 1, 64 ) image2 = pygame.transform.scale(image1, (1, lineHeight)) darken_percent = (1 - (lineHeight*5/c.SCREEN_HEIGHT)) dark = pygame.Surface(image2.get_size()).convert_alpha() darkness = (darken_percent*255) if darkness > 255: darkness = 255 elif darkness < 0: darkness = 0 dark.fill((0, 0, 0, darkness)) screen.blit(image2, ((x * 1), c.SCREEN_HEIGHT // 2 - lineHeight // 2)) screen.blit(dark, ((x * 1), c.SCREEN_HEIGHT // 2 - lineHeight // 2)) # SET THE ZBUFFER FOR THE SPRITE CASTING self.zBuffer.append(perpWallDist) # Cast Sprites and Players: self.cast_sprites(self.sprites, screen) # Draw Minimap self.minimap.draw(screen, self.game_map, self.posX, self.posY, self.sprites) # Timing for input and FPS counter self.oldTime = self.time self.time = pygame.time.get_ticks() self.frameTime = (self.time - self.oldTime) / 1000.0 # Frametime is the time this frame has taken, in seconds fps = 1.0 / self.frameTime text = self.font.render("FPS: {:.2f}".format(fps), True, c.WHITE) screen.blit(text, (10, 10)) # Draw Scoreboard if self.show_scoreboard: self.scoreboard.draw(screen, self.sprites, self.scoreboard_data) # Draw the crosshair cross_size = 20 pygame.draw.line(screen, c.RED, (c.SCREEN_WIDTH//2 - cross_size//2, c.SCREEN_HEIGHT//2 + 20), (c.SCREEN_WIDTH//2 + cross_size//2, c.SCREEN_HEIGHT//2 + 20)) pygame.draw.line(screen, c.RED, (c.SCREEN_WIDTH//2, c.SCREEN_HEIGHT//2 - cross_size//2 + 20), (c.SCREEN_WIDTH//2, c.SCREEN_HEIGHT//2 + cross_size//2 + 20)) # Draw server message and empty the variable after some time if self.message: self.message_time = pygame.time.get_ticks() if self.message_time - self.old_message_time > 5000: self.old_message_time = self.message_time self.message = "" if not self.message: self.old_message_time = pygame.time.get_ticks() text = self.font.render(self.message, True, c.WHITE) screen.blit(text, (c.SCREEN_WIDTH//2, 10))