class Room: '''Container for all in-game elements''' def __init__(self, floor_data, parent_game): self._scenario_ = TileMapLayer(floor_data, mask=get_color_mask()) self._camera_ = Camera(self._scenario_) self._game_ = parent_game self._game_objects_ = {} self._decorations_ = {} self.block = self._compute_walls_collisions_() self._spawns_ = self._get_spawns_() @property def initial_objects(self): '''List of all objects defined in the map initially''' return self._scenario_.objects @property def game_objects(self): '''Map of current-living objects''' return self._game_objects_ def _compute_walls_collisions_(self): block = [] for y in range(self._scenario_.map_height): row = [] for x in range(self._scenario_.map_width): row.append(self._scenario_.get_cell_at(x, y) in BLOCK_CELLS) block.append(row) return block def _get_spawns_(self): spawns = {} for candidate in self._game_objects_.values(): if isinstance(candidate, Spawn): spawns[candidate.spawn] = candidate.position return spawns @property def camera(self): '''Room camera''' return self._camera_ def set_camera_target(self, game_object): '''Change target of the camera''' self._camera_.set_target(game_object) @property def tilemaps(self): '''Map layers''' return self._scenario_ def spawn(self, game_object): '''Spawn new object at a spawn zone''' spawn_zone = game_object.spawn if game_object.spawn in self._spawns_ else DEFAULT_SPAWN self.spawn_at(game_object, self._spawns_[spawn_zone]) def spawn_at(self, game_object, position): '''Spawn new object at a given position''' self._game_objects_[game_object.identifier] = game_object self._game_objects_[game_object.identifier].position = position self._game_objects_[game_object.identifier].room = self self._spawns_.update(self._get_spawns_()) def spawn_decoration(self, decoration_id, position): '''Spawn decoration''' decoration = game.decoration.new(decoration_id, position) self._decorations_[decoration.identifier] = decoration self._decorations_[decoration.identifier].room = self def kill(self, game_object): '''Kill an object''' identifier = game_object if isinstance(game_object, str) else game_object.identifier if identifier == self._game_.identifier: self._game_.player.attribute.update( self._game_objects_[identifier].attribute) if identifier in self._game_objects_: self._game_objects_[identifier].room = None del self._game_objects_[identifier] elif identifier in self._decorations_: self._decorations_[identifier].room = None del self._decorations_[identifier] if identifier == self._game_.identifier: self._game_.end_current_room() def open_door(self, door_identifier): '''Open a existing door''' door_position = self._search_door_(door_identifier) if not door_position: return doors = self._adjacent_doors_(door_position) for door in doors: self.kill(door) self.send_event(('kill_object', door)) def _search_door_(self, door_identifier): if door_identifier not in self._game_objects_: return None y = 0 for row in self.block: x = 0 for cell in row: if cell == door_identifier: return (x, y) x += 1 y += 1 return None def _adjacent_doors_(self, location, visited=None): if not visited: visited = [] x, y = location if location in visited: return [] visited.append((x, y)) if (y not in range(len(self.block))) or (x not in range( len(self.block[0]))): return [] identifier = self.block[y][x] door = self._game_objects_[identifier] if not isinstance(door, Door): return [] doors = [door.identifier] door_tile_id = door.attribute[TILE_ID] for dir_x, dir_y in _DOOR_DIRECTION_[door_tile_id]: doors += self._adjacent_doors_((x + dir_x, y + dir_y), visited) return doors def update(self): '''A game loop iteration''' for game_object in list(self._game_objects_.values()): game_object.update() if not game_object.acting: self.kill(game_object) if game_object.body: self.check_collisions_with(game_object) def render(self): '''Draw a frame''' self._camera_.update() self._scenario_.render(*self._camera_.position) for game_object in self._game_objects_.values(): game_object.render(*self._camera_.position) for decoration in list(self._decorations_.values()): decoration.render(*self._camera_.position) def check_collisions_with(self, game_object): '''Compute collisions for all game objects''' if not game_object.body: return for other_game_object in list(self._game_objects_.values()): if (other_game_object is game_object) or (not other_game_object.body): continue if game_object.body.collides_with(other_game_object): self.send_event(('collision', game_object.identifier, other_game_object.identifier)) def send_event(self, event): '''Send event to orchestrator''' self._game_.send_event(event)
class Game: def __init__(self): init() font.init() display.set_caption(TITLE) key.set_repeat() self.playing = False self.screen = display.set_mode((WIDTH, HEIGHT)) self.clock = time.Clock() self.dt = self.clock.tick(FPS) / 1000 self.labels = list() self.infos = list() self.all_sprites = Group() self.adventure = Home(self) self.camera = Camera(self.adventure.map.width, self.adventure.map.height) self.score = Score("Renato") self.game_over = False def update_adventure(self, adventure): self.show_ended_screen() self.adventure.player.kill() self.labels = list() self.infos = list() self.adventure = adventure self._update_camera() self.show_go_screen() self.score.start_adventure(adventure.level.level_score) def run(self): self.playing = True while self.playing: self.events() self.update() self.draw() if self.game_over: break if self.adventure.finish: self.show_end_adventure_screen() break def update(self): # update info self.infos = list() self.adventure.update() self.all_sprites.update() self.camera.update(self.adventure.player) def draw(self): if not self.adventure.has_menu: self.screen.blit(self.adventure.map_image, self.camera.apply_rect(self.adventure.map_rect)) for sprite in self.all_sprites: self.screen.blit(sprite.image, self.camera.apply(sprite)) for label in self.labels: self.screen.blit(label.surface, self.camera.apply_rect(label.rect)) for info in self.infos: self.screen.blit(info.surface, info.rect) for zombie in self.adventure.zombies: zombie.draw_health() display.flip() def events(self): events = key_event.get() if not self.adventure.has_menu: for event in events: if event.type == QUIT: self.__quit__() if event.type == KEYDOWN: if event.key == K_ESCAPE: self.__quit__() self.adventure.events(events) def show_start_screen(self): pass def show_go_screen(self): self.update() start_display = Surface((WIDTH, HEIGHT), SRCALPHA) for alpha in range(255, 0, -1): start_display.fill((0, 0, 0, alpha)) self.screen.blit(self.adventure.map_image, self.camera.apply_rect(self.adventure.map_rect)) self.screen.blit(start_display, (0, 0)) display.flip() time.delay(10) def show_ended_screen(self): self.update() start_display = Surface((WIDTH, HEIGHT), SRCALPHA) for alpha in range(255): start_display.fill((0, 0, 0, alpha)) self.screen.blit(self.adventure.map_image, self.camera.apply_rect(self.adventure.map_rect)) self.screen.blit(start_display, (0, 0)) display.flip() time.delay(10) def show_game_over_screen(self): game_over_display = Surface((WIDTH, HEIGHT), SRCALPHA) game_over_text = Label('Game Over', 'assets/fonts/blocks.ttf', 20, (HEIGHT / 2) - 100, font_size=72) score_text = Label('For save the score you need finish adventure', 'assets/fonts/future_narrow.ttf', 20, (HEIGHT / 2) - 120, font_size=24) return_text = Label('You will return to home', 'assets/fonts/future_narrow.ttf', 20, (HEIGHT / 2) - 160, font_size=24) for alpha in range(255): game_over_display.fill((0, 0, 0, alpha)) self.screen.blit(game_over_display, (0, 0)) self.screen.blit(game_over_text.surface, game_over_text.rect) self.screen.blit(score_text.surface, score_text.rect) self.screen.blit(return_text.surface, return_text.rect) display.flip() time.delay(10) def show_end_adventure_screen(self): end_surface = Surface((WIDTH, HEIGHT), SRCALPHA) end_text = Label('Good Nice!', 'assets/fonts/blocks.ttf', 20, (HEIGHT / 2) - 100, font_size=72) score_text = Label(f'Score: {self.score.calculate_score()}', 'assets/fonts/future_narrow.ttf', 20, (HEIGHT / 2) - 120, font_size=24) return_text = Label('You will return to home and see you score ranking', 'assets/fonts/future_narrow.ttf', 20, (HEIGHT / 2) - 160, font_size=24) for alpha in range(255): end_surface.fill((0, 0, 0, alpha)) self.screen.blit(end_surface, (0, 0)) self.screen.blit(end_text.surface, end_text.rect) self.screen.blit(score_text.surface, score_text.rect) self.screen.blit(return_text.surface, return_text.rect) display.flip() time.delay(10) def _update_camera(self): self.camera = Camera(self.adventure.map.width, self.adventure.map.height) @staticmethod def __quit__(): quit() sys.exit()
class VaniaGame: """ An attempt to make a 'Metroidvania' style platform game. """ def __init__(self): pygame.init() os.environ['SDL_VIDEO_CENTERED'] = '1' pygame.display.set_caption("Vania") world_size = [4096, 4096] self.camera = Camera((400, 300), (800, 600), world_size) pygame.display.set_icon(pygame.image.load('images/icon.png')) self.screen = pygame.display.set_mode(self.camera.dimensions, 0, 0) self.fonts = { "default_12": pygame.font.Font("data/fonts/Pavanam-Regular.ttf", 12), "default_12_bold": pygame.font.Font("data/fonts/Pavanam-Regular.ttf", 12), "default_14": pygame.font.Font("data/fonts/Pavanam-Regular.ttf", 14), "default_14_bold": pygame.font.Font("data/fonts/Pavanam-Regular.ttf", 14), "default_16": pygame.font.Font("data/fonts/Pavanam-Regular.ttf", 16), "default_16_bold": pygame.font.Font("data/fonts/Pavanam-Regular.ttf", 16), "default_32": pygame.font.Font("data/fonts/Pavanam-Regular.ttf", 32), "default_64": pygame.font.Font("data/fonts/Pavanam-Regular.ttf", 64), "julee_128": pygame.font.Font(os.path.abspath("data/fonts/Julee-Regular.ttf"), 128), "roboto_14_bold": pygame.font.Font("data/fonts/Roboto-Bold.ttf", 14) } self.fonts["default_12_bold"].set_bold(True) self.fonts["default_14_bold"].set_bold(True) self.fonts["default_16_bold"].set_bold(True) self.background = pygame.Surface(self.camera.dimensions) self.background.fill(pygame.Color("#c4dbf3")) grid_square_size = 64 world_filling_number_of_grid_squares = [ int(world_size[0] / grid_square_size), int(world_size[1] / grid_square_size) ] self.collision_grid = CollisionGrid( world_filling_number_of_grid_squares, grid_square_size) self.moving_sprites_group = pygame.sprite.Group() self.ui_sprites_group = pygame.sprite.Group() self.player = Player(self.moving_sprites_group, self.ui_sprites_group, self.fonts, self.collision_grid, self.camera) self.player_health_ui = UIPlayerHealthBar( (20, self.camera.dimensions[1] - 30), self.fonts) self.tiled_level = TiledLevel(self.collision_grid, world_filling_number_of_grid_squares, self.moving_sprites_group, self.ui_sprites_group) self.editor = MapEditor(self.tiled_level, self.fonts, self.collision_grid, self.camera) self.main_menu = MainMenu(self.fonts, self.camera) self.is_main_menu = True self.is_editor = False self.start_game = False self.gravity = 800.0 self.clock = pygame.time.Clock() self.fps_counter = FPSCounter(self.fonts) self.running = True def run(self): while self.running: frame_time = self.clock.tick(60) time_delta = min(0.1, frame_time / 1000.0) # TODO: make these three states into state classes and use a state machine dictionary to switch between them if self.is_main_menu: is_main_menu_and_index = self.main_menu.run(self.screen) if is_main_menu_and_index[0] == 0: self.is_main_menu = True elif is_main_menu_and_index[0] == 1: self.is_main_menu = False self.start_game = True self.camera.reset_camera_tracking() self.camera.position[0] = 316.0 self.camera.position[1] = 596.0 self.player.respawn() self.tiled_level.reset_entities() elif is_main_menu_and_index[0] == 2: self.is_main_menu = False self.is_editor = True elif is_main_menu_and_index[0] == 3: self.running = False elif self.is_editor: self.is_editor = self.editor.run(self.screen, self.background, time_delta) if not self.is_editor: self.is_main_menu = True else: for event in pygame.event.get(): if event.type == QUIT: self.is_main_menu = True if event.type == KEYDOWN: if event.key == K_ESCAPE: self.is_main_menu = True self.player.process_events(event) if self.player.has_exited_level: self.player.has_exited_level = False self.is_main_menu = True self.camera.set_target_position(self.player.world_position) self.camera.update(time_delta) self.tiled_level.update(time_delta, self.camera) self.moving_sprites_group.update(time_delta, self.gravity, self.camera) self.collision_grid.update_shape_grid_positions() self.collision_grid.check_collisions() self.screen.blit(self.background, (0, 0)) self.tiled_level.draw_back_layers(self.screen) self.moving_sprites_group.draw(self.screen) self.tiled_level.draw_front_layers(self.screen) # draw the collision shapes # self.tiled_level.draw_collision_shapes(self.screen, self.camera) # self.player.draw_collision_shapes(self.screen, self.camera) # for sprite in self.moving_sprites_group: # if sprite.type == "enemy": # sprite.draw_debug_info(self.screen, self.camera) self.player_health_ui.update(self.player) self.player_health_ui.draw(self.screen) self.ui_sprites_group.update() self.ui_sprites_group.draw(self.screen) self.fps_counter.update(time_delta) self.fps_counter.draw(self.screen, self.camera) pygame.display.flip()
class Game: def __init__(self): pg.init() pg.mixer.init() self.screen = pg.display.set_mode((WIDTH, HEIGHT)) pg.display.set_caption(TITLE) self.clock = pg.time.Clock() self.running = True self.double_speed = False self.number_of_levels = 4 self.navigator = Navigator(self) self.drawer = Drawer(self.screen) self.creator = Creator() self.init_player() self.init_sprite_lists() self.camera = Camera(self.player, self.creator.shiftable, self.creator.texts) self.is_tutorial = False self.current_level = 0 def init_sprite_lists(self): self.all_sprites = self.creator.all_sprites self.platform_list = self.creator.platforms self.tile_list = self.creator.tiles self.enemy_list = self.creator.enemies self.coin_list = self.creator.coins self.player.collide_list = self.creator.player_collide_list self.bullet_list = self.creator.bullets def init_player(self): self.player = self.creator.player self.player.weapon = Weapon(self.creator) def stop(self): self.playing = False self.running = False def new(self, level): """ Calls level or final level building and playing loop """ self.current_level = level if level == self.number_of_levels - 1: self.creator.build_level_final(self.player) self.run() elif 0 <= level < self.number_of_levels - 1: self.creator.build_level(self.player) self.run() else: return def run(self): self.playing = True while self.playing: self.clock.tick(FPS) self.events() self.update() if self.playing: self.draw() def update(self): """ Handles screen updates """ if self.player.hp <= 0: self.player.kill() self.navigator.show_go_screen() for coin in pg.sprite.spritecollide(self.player, self.coin_list, False): coin.kill() self.player.money += 30 for enemy in pg.sprite.spritecollide(self.player, self.enemy_list, False): if abs(enemy.rect.x - self.player.rect.x) <= self.player.rect.width / 2 \ and self.player.rect.bottom == enemy.rect.bottom: self.player.receive_damage(1) if isinstance(enemy, AirEnemy) \ and abs(enemy.rect.x - self.player.rect.x) <= self.player.rect.width / 2 \ and abs(enemy.rect.y - self.player.rect.y) <= self.player.rect.height / 2: if not self.is_tutorial: self.player.receive_damage(1) if isinstance(enemy, SlimeBlock) \ and abs(enemy.rect.x - self.player.rect.x) <= self.player.rect.width / 2 \ and abs(enemy.rect.y - self.player.rect.y) <= self.player.rect.height / 2: if not self.is_tutorial: self.player.receive_damage(1) pg.sprite.groupcollide(self.bullet_list, self.platform_list, True, False) pg.sprite.groupcollide(self.bullet_list, self.tile_list, True, False) collision_dict = pg.sprite.groupcollide(self.bullet_list, self.enemy_list, True, False) for bullet in collision_dict: for enemy in collision_dict[bullet]: enemy.hp -= bullet.damage self.all_sprites.update() self.camera.update() def events(self): """ Handles key events """ for event in pg.event.get(): if event.type == pg.QUIT: pg.quit() sys.exit() if event.type == pg.KEYDOWN: if event.key == pg.K_SPACE: self.player.fire() if event.key == pg.K_LEFT: if self.double_speed: self.player.go_left_fast() else: self.player.go_left() if event.key == pg.K_RIGHT: if self.double_speed: self.player.go_right_fast() else: self.player.go_right() if event.key == pg.K_LSHIFT: if not self.double_speed: self.double_speed = True else: self.double_speed = False if event.key == pg.K_UP: self.player.jump() if event.type == pg.KEYUP: if event.key == pg.K_LEFT and self.player.vel_x < 0: self.player.stop() if event.key == pg.K_RIGHT and self.player.vel_x > 0: self.player.stop() self.all_sprites.update() if abs((self.creator.right_wall.rect.x - self.creator.right_wall.rect.width / 2) - (self.player.rect.x + self.player.rect.width / 2)) <= 25: if self.is_tutorial: self.navigator.show_tutorial_done_screen() else: if self.current_level < self.number_of_levels - 1: self.navigator.go_to_store() elif self.is_boss_level(): if not self.creator.boss.alive(): self.navigator.show_congrats_screen() def draw_stats_bar(self): self.drawer.draw_player_stats(self.player) if self.is_boss_level(): self.drawer.draw_health_bar( self.creator.boss, 700, 20, 5) def draw(self): self.screen.fill(RED) self.draw_stats_bar() self.all_sprites.draw(self.screen) if self.is_tutorial: self.blit_texts() pg.display.flip() def blit_texts(self): for sf, rect in self.creator.texts: self.screen.blit(sf, rect.midtop) def reset(self): self.player.hp = 15 self.player.money = 0 self.running = True self.playing = True pass def tutorial(self): self.is_tutorial = True self.creator.build_tutorial_level(self.player, self.screen) self.run() def is_boss_level(self): if self.is_tutorial: return False return self.current_level == self.number_of_levels - 1