def load_map(map_image=None): """ Function that loads a PIL image and reads it, pixel by pixel, to decode it into a tile map. Gets the map from the c.IMAGES["map"] object. Sets a map (two-dimensional list of Tiles), the width and height of the image loaded and the player starting point, x and y, from the file as variables in the g module. (5 items) If no starting point is found, return 0, 0 as the starting point. """ if map_image is None: # Load the image map_image = pygame.image.load(os.path.join(os.getcwd(), c.RES_FOLDER, c.IMAGES["map"].png)) g.map = [] # Variable for holding multi_tiles until after the primary generation. multi_tiles = [] width, height = map_image.get_size() player_start_x = 0 player_start_y = 0 for x in range(width): # Create a new vertical column for every pixel the image is wide. g.map.append([]) for y in range(height): # The pixel variable is the pixel we're currently checking. pixel = map_image.get_at((x, y))[:3] px_type = pixel_type(pixel, x, y) # If the pixel is the player start tile, save the location of that pixel. if px_type == "start_tile": player_start_x = x * c.TILE_SIZE player_start_y = y * c.TILE_SIZE px_type = c.DEFAULT_TILE # Check to see if it's a multi-tile and, if so, store that in a variable to be done last g.map[x].append(None) if c.IMAGES[px_type].multi_tile: multi_tiles.append([px_type, x, y]) tiles.make_tile(c.DEFAULT_TILE, x, y) else: # Make a new tile and add it to the map tiles.make_tile(px_type, x, y) # Sets the values to the global values g.width = width g.height = height g.player_start_x = player_start_x g.player_start_y = player_start_y # Create the multi-tiles for multi_tile in multi_tiles: px_type, x, y = multi_tile width, height = c.IMAGES[px_type].multi_tile if (g.map[x][y] and g.map[x][y].type == c.DEFAULT_TILE and tiles.area_is_free(x, y, width, height)): tiles.make_tile(px_type, x, y)
def main(): """ Main function, initializes various variables and contains the main program loop. Should not be called any other way than running the file or running launch. Takes no arguments and returns nothing. """ # initialize pygame pygame.init() # Make map maps.load_map(maps.generate_map()) # maps.load_map() # Initiate player g.special_entity_list["player"] = players.Player(g.player_start_x, g.player_start_y) # Creates a window just the size to fit all the tiles in the map file. pygame.display.set_icon(g.images["icon"].get()) pygame.display.set_caption("TileGame by ZeeQyu", "TileGame") g.screen = pygame.display.set_mode((g.width * c.TILE_SIZE, g.height * c.TILE_SIZE)) # A variable for skipping a single cycle after f.ex. accessing a menu, so that # the entities won't fly across the screen skip_cycle = False # Get time once initially and make time variables time_last_tick = time_prev = time.clock() time_start = time_cycles = time_updates = time_last_sleep = 0 # Main loop while True: # Make the screen update every frame if c.FORCE_UPDATE: g.force_update = True # Event checker. Allows closing of the program and passes keypresses to the player instance key_input.event_check() # Tick: Make sure certain things happen on a more regular basis than every frame # time_big_diff is the time the cycle took. # time_diff (defined below) is the simulated time difference that # the entities move after before ticking again in case of a lag spike. time_now = time.clock() time_big_diff = (time_now - time_prev) * c.GAME_SPEED time_prev = time_now # Skip the rest of this cycle if a menu was accessed until now if skip_cycle: skip_cycle = False continue # FPS meter (shown in console). # checks the amount of times this code is run every second and prints that every second. time_cycles += 1 if time_start + 1 < time_now: if time_updates == 1 and time_cycles == 1: time_updates = 1.0 / time_big_diff if c.NORMAL_DEBUG: print(time_start, "seconds from start,", time_cycles, "cycles,", time_updates, "fps") time_cycles = 0 time_updates = 0 time_start = time_now # What happens every tick? while time_big_diff > 0: if time_big_diff >= c.TICK_FREQ: time_diff = c.TICK_FREQ time_big_diff -= time_diff else: time_diff = time_big_diff time_big_diff = 0 if time_last_tick + c.TICK_FREQ <= time_now: time_last_tick = time_last_tick + c.TICK_FREQ # Tick all the entities (let them do whatever they do every tick for i in range(len(g.entity_list) - 1, -1, -1): entity = g.entity_list[i] if entity.tick() == "delete": del g.entity_list[i] g.force_update = True for entity in list(g.special_entity_list.values()): entity.tick() for tile in g.tick_tiles: g.map[tile[0]][tile[1]].tick() # Make sure the loop doesn't go too quickly and bog the processor down if time_last_sleep < c.SLEEP_TIME: time.sleep(c.SLEEP_TIME - time_last_sleep) # update all entities entity_has_moved = False if g.entity_list: for i in range(len(g.entity_list) - 1, -1, -1): entity = g.entity_list[i] entity.update(time_diff) # Check if any of them have moved if entity.has_moved(): entity_has_moved = True if g.special_entity_list: for entity in list(g.special_entity_list.values()): # Update all entities and check for if any of them is a package that just finished moving. # If so, skip the has_moved check for that entity. if entity.update(time_diff) == "deleted": continue if entity.has_moved(): entity_has_moved = True if "tile_target" in g.special_entity_list: while g.tile_target_selection[0] >= g.width: g.tile_target_selection[0] -= g.width while g.tile_target_selection[0] < 0: g.tile_target_selection[0] += g.width while g.tile_target_selection[1] >= g.height: g.tile_target_selection[1] -= g.height while g.tile_target_selection[1] < 0: g.tile_target_selection[1] += g.height g.special_entity_list["tile_target"].x = g.tile_target_selection[0] * c.TILE_SIZE g.special_entity_list["tile_target"].y = g.tile_target_selection[1] * c.TILE_SIZE if g.non_entity_list: for item in list(g.non_entity_list.values()): try: if item.update(time_diff): entity_has_moved = True except AttributeError: pass # Check if any tiles need to be updated. if g.tile_maker_queue: while g.tile_maker_queue: tiles.make_tile(*g.tile_maker_queue.pop()) # Update map buffer if needed if g.update_map: g.update_map = False g.force_update = True g.map_screen_buffer = maps.update_map() g.update_microtiles = False # If any entity moved, redraw the screen if entity_has_moved or g.force_update: g.force_update = False time_updates += 1 g.screen.fill(c.BACKGROUND_COLOR) # Draw the map buffer on the screen g.screen.blit(g.map_screen_buffer, (0, 0)) # Draw the objects for i in range(len(g.entity_list) - 1, -1, -1): entity = g.entity_list[i] entity.paint() for i in range(len(list(g.special_entity_list.values())) - 1, -1, -1): entity = list(g.special_entity_list.values())[i] entity.paint() for i in range(len(list(g.non_entity_list.values())) - 1, -1, -1): list(g.non_entity_list.values())[i].paint() del entity # Update the display pygame.display.flip()
def update(self, time_diff): """ Calls the superclass update and updates the state of the aim marker. Manages if the player is placing or destroying a block. """ super(Player, self).update(time_diff) # Update screen whenever aim marker changes states if self.removing_tile != self.last_removing_tile: self.last_removing_tile = self.removing_tile g.force_update = True # Get which tile the player is looking at aim_tile = self.get_aim_tile() x, y = aim_tile # If the aim tile has changed if aim_tile != self.last_aim_tile or self.update_aim_tile: self.update_aim_tile = False # Checks if the aim tile has a remove time (can be destroyed). # If so, assign that value to self.remove_timer. try: if c.IMAGES[g.map[x][y].type].destroy is not None: self.remove_timer = c.IMAGES[g.map[x][y].type].destroy[0] elif type(g.map[x][y]) == tiles.MultiTilePointer: # Finding out which tile the pointer is pointing to, and if that has a destroy value head_x, head_y = g.map[x][y].target multi_tile_head = g.map[head_x][head_y] if c.IMAGES[multi_tile_head.type].destroy is not None: self.remove_timer = c.IMAGES[multi_tile_head.type].destroy[0] else: self.remove_timer = None else: self.remove_timer = None except IndexError: self.remove_timer = None except: raise self.last_aim_tile = aim_tile # Placing tile if self.placing_tile and not self.removing_tile: try: if c.IMAGES[g.map[x][y].type].placeable: # If there is a special case for placing tiles, use that. Otherwise, use the default tiles.make_tile(c.DEFAULT_PLACE_TILE, x, y) # Ignore IndexErrors because the indices might be outside of the map except IndexError: pass except: raise # Removing tile if self.removing_tile and not self.placing_tile: # If the timer isn't None (is removable) if self.remove_timer is not None: # If remove_timer has reached 0 which means the countdown is done if self.remove_timer < 1: tiles.destroy_tile(x, y) self.update_aim_tile = True self.remove_timer = None return # Grabbing tile if self.toggle_grab: # If the grab button is pressed self.toggle_grab = False if self.following_entity is not None: x, y = self.get_aim_tile() if c.IMAGES[g.map[x][y].type].placeable: g.special_entity_list[self.following_entity].target_coords = [x*c.TILE_SIZE, y*c.TILE_SIZE] else: if g.map[x][y].type in c.PACKAGE_TILE_NAMES.keys(): tiles.make_tile(c.PACKAGE_TILE_NAMES[g.map[x][y].type], x, y) g.update_map = True units.Package(x*c.TILE_SIZE, y*c.TILE_SIZE, "player")
def _put_tile(tile_id): tiles.make_tile(tile_id, *g.special_entity_list["player"].get_aim_tile()) return True