Example #1
0
#!/usr/bin/env python3
from Dungeon import Dungeon

dungeon = Dungeon()
hallway = dungeon.create()
d1 = dungeon.newRoom(hallway, "D")
d2 = dungeon.newRoom(d1, "D")
d3 = dungeon.newRoom(d2, "D")
d4 = dungeon.newRoom(d3, "D")
d5 = dungeon.newRoom(d4, "D")
bedroom = dungeon.newRoom(hallway, "N")
bedroom2 = dungeon.newRoom(hallway, "W")
bedroom3 = dungeon.newRoom(bedroom, "E")
bedroom4 = dungeon.newRoom(bedroom3, "D")
room5 = dungeon.newRoom(bedroom4, "N")
room6 = dungeon.newRoom(room5, "W")
room7 = dungeon.newRoom(room6, "U")
class Game:
    """This class is main Game class.

    """
    def __init__(self):
        """__init__ method for Game class.

        """
        # initialise game window, settings etc.
        pygame.mixer.pre_init(44100, -16, 2, 2048)
        pygame.mixer.init()
        pygame.init()
        pygame.mouse.set_visible(False)
        pygame.key.set_repeat(10, cfg.KEY_DELAY)

        self.actual_screen = pygame.display.set_mode(
            (cfg.S_WIDTH, cfg.S_HEIGHT))
        #self.actual_screen = pygame.display.set_mode((cfg.S_WIDTH, cfg.S_HEIGHT), pygame.FULLSCREEN)

        self.screen = pygame.Surface((cfg.WIDTH, cfg.HEIGHT))

        self.clock = pygame.time.Clock()
        self.running = True
        self.loaded = False
        self.dircheck = None

        # booleans for drawing the hit rects and other debug stuff
        self.debug = False
        self.draw_vectors = False
        self.show_player_stats = False
        self.caption = ''

        self.key_down = None
        self.state = 'GAME'
        self.in_transition = False

        self.loadAssets()
        self.clearcount = 0
        self.check = True
        self.timer = 0
        self.levelcleared = 1
        self.pistolpick = True
        self.machinegunpick = False
        self.lastweapon = 'MachineGun'
        self.spritecheck = []

    def keyDown(self, events, game):
        """Game class method for checking keys presses.

        Args:
            events (<Event>): key press events.
            game (Integrate.Game): Integrate.Game class object.

        """
        for event in events:
            if event.type == pygame.KEYUP:
                if event.key == cfg.KEY_RIGHT:
                    game.player.RightCheck = False
                if event.key == cfg.KEY_UP:
                    game.player.UpCheck = False
                if event.key == cfg.KEY_LEFT:
                    game.player.LeftCheck = False
                if event.key == cfg.KEY_DOWN:
                    game.player.DownCheck = False

    def transitRoom(self, game, dungeon, offset=pygame.math.Vector2(0, 0)):
        """Game class method for room transition.

        Args:
            game (Integrate.Game): Integrate.Game class object.
            dungeon (Dungeon.Dungeon): Dungeon.Dungeon object

        """
        # get the index of the next and the previous room
        index_next = dungeon.current_room_index
        index_prev = game.prev_room
        # select the rooms based on the indices
        room_prev = dungeon.room_matrix[index_prev[0]][index_prev[1]]
        room_next = dungeon.room_matrix[index_next[0]][index_next[1]]

        # remove all sprite from the previous room
        room_prev.object_data = []

        for sprite in game.all_sprites:
            if sprite != game.player and sprite not in game.item_drops:
                if hasattr(sprite, 'data'):
                    room_prev.object_data.append(sprite.data)
                sprite.kill()

        if room_next.visited == False:
            # if room not visited, get the object data from the initial layout
            data = room_next.layout
            for d in data:
                try:
                    if offset == (0, 0):
                        self.create(game, d)
                    else:
                        self.create(game, d, offset)
                except Exception:
                    traceback.print_exc()
                    pass

            room_next.visited = True

        else:
            # if room already visited, get the objects from the stored data
            data = room_next.object_data
            for d in data:
                try:
                    if offset == (0, 0):
                        self.create(game, d)
                    else:
                        self.create(game, d, offset)
                except Exception:
                    traceback.print_exc()
                    pass

        # set the dungeon's current room based on room index
        dungeon.room_current = dungeon.room_matrix[
            dungeon.current_room_index[0]][dungeon.current_room_index[1]]

    def create(self, game, data, offset=pygame.math.Vector2(0,
                                                            cfg.GUI_HEIGHT)):
        """Game class method for room transition.

        Args:
            game (Integrate.Game): Integrate.Game class object.
            data (dict{'id', 'name', 'x', 'y', 'width', 'height'}): objects data

        """
        # takes a dictionary of sprite properties
        name = data['name']
        # capitalizing first letter
        name_1 = name[0].capitalize()
        name_2 = name[1:]
        name = name_1 + name_2

        #instantiate the sprite
        spr = module_dict[name](game,
                                (data['x'] + offset.x, data['y'] + offset.y),
                                (data['width'], data['height']))

        for key, value in data.items():
            try:
                setattr(spr, key, value)
            except:
                print('cant set value of {0} for {1}'.format(key, spr))

        if hasattr(spr, 'on_create'):
            # do initialisation stuff after ___init__()
            spr.on_create()

        spr.data = data

    def loadAssets(self):
        """Game class method to load all assets.

        """
        #loading assets (images, sounds)
        self.imageLoader = ImageLoader(self)
        self.soundLoader = snd.Sound()
        try:
            self.imageLoader.load_assets()
            self.soundLoader.load()
        except Exception:
            traceback.print_exc()
            self.running = False

    def new(self):
        """Game class method to start a new game.

        """
        # start a new game
        # initialise sprite groups
        self.all_sprites = pygame.sprite.LayeredUpdates()
        self.walls = pygame.sprite.LayeredUpdates()
        self.gui = pygame.sprite.LayeredUpdates()
        self.enemies = pygame.sprite.LayeredUpdates()
        self.item_drops = pygame.sprite.LayeredUpdates()

        # instantiate dungeon
        self.dungeon = Dungeon(self, cfg.DUNGEON_SIZE)
        if self.loaded:
            self.dungeon.tileset = self.saveGame.data['tileset']
        else:
            self.dungeon.create()

        self.WSign = self.imageLoader.WarnSign
        self.WSign2 = self.imageLoader.WarnSign2

        self.inventory = Inventory(self)

        # spawn the player in the middle of the room
        self.player = Player(self, (cfg.WIDTH // 2, cfg.TILESIZE * 12))

        self.currentpistol = Pistol(self, self.player)
        self.currentmachine = MachineGun(self, self.player)

        if self.pistolpick == True:
            self.player.itemA = self.currentpistol

        elif self.machinegunpick == True:
            self.player.itemA = self.currentmachine

        # load settings
        if self.loaded:
            self.loadSavefile()

        # spawn the new objects (invisible)
        self.prev_room = self.dungeon.current_room_index
        self.transitRoom(self, self.dungeon)

        # create a background image from the tileset for the current room
        self.background = self.tileRoom(self, self.imageLoader.tileset,
                                        self.dungeon.current_room_index)

        self.run()

    def checkFight(self, game):
        """Game class method to check distance from door and closes the doors 
           and opens them when player defeats all enemies in the room
        
        """
        room = game.dungeon.room_current
        if len(game.enemies) > 0:
            room.cleared = False
        else:
            if not room.is_doors_shut:
                room.cleared = True
                return

        # check if the room's doors are closed
        if room.is_doors_shut == False:
            # check player's position
            margin_x = 5 * cfg.TILESIZE_SMALL
            margin_y = 5.5 * cfg.TILESIZE_SMALL + cfg.GUI_HEIGHT
            rect = pygame.Rect((margin_x, margin_y),
                               (cfg.WIDTH - 2 * margin_x,
                                cfg.HEIGHT - cfg.GUI_HEIGHT - margin_y))
            if rect.colliderect(game.player.hit_rect):
                # player is far enough in the room to shut the doors
                room.shut_doors()
                game.soundLoader.get['roomLocked'].play()

        else:
            if len(game.enemies) == 0:
                # if all enemies are defeated, open the doors
                room.open_doors()
                game.soundLoader.get['roomCleared'].play()
                room.cleared = True

    def get_inputs(self, game):
        """Game class method to load all assets store keyboard inputs in a key map
        
        """
        game.keys = {
            'A': False,
            'B': False,
            'X': False,
            'Y': False,
            'L': False,
            'BACK': False,
            'START': False,
            'STICK_L_PRESSED': False,
            'STICK_R_PRESSED': False,
            'STICK_R': pygame.math.Vector2(0, 0),
            'STICK_L': pygame.math.Vector2(0, 0),
            'DPAD': pygame.math.Vector2(0, 0),
            'DPAD_MENU': pygame.math.Vector2(0, 0)
        }

        key = game.keys

        # get keyboard keys
        get_keys = pygame.key.get_pressed()
        if get_keys[cfg.KEY_RIGHT]:
            game.player.RightCheck = True
        elif get_keys[cfg.KEY_LEFT]:
            game.player.LeftCheck = True
        if get_keys[cfg.KEY_DOWN]:
            game.player.DownCheck = True
        elif get_keys[cfg.KEY_UP]:
            game.player.UpCheck = True

        get_mkeys = pygame.mouse.get_pressed()

        key['A'] = get_mkeys == cfg.KEY_A or key['A']
        key['B'] = game.key_down == cfg.KEY_B or key['B']
        key['X'] = game.key_down == cfg.KEY_ENTER or key['X']

        key['START'] = get_keys[cfg.KEY_MENU]

    def run(self):
        """Game class method to start running infinite loop until quit.

        """
        # game loop
        self.playing = True
        while self.playing:
            #reset game screen
            self.screen = pygame.Surface((cfg.WIDTH, cfg.HEIGHT))
            self.dt = self.clock.tick(cfg.FPS) / 1000
            self.events()
            self.get_inputs(self)
            self.update()
            self.draw()

    def events(self):
        """Game class method to loop game events.

        """
        # game loop events
        self.event_list = pygame.event.get()
        for event in self.event_list:
            if event.type == pygame.QUIT:
                if self.playing:
                    self.playing = False
                self.running = False
                pygame.quit()
                quit()
            if event.type == pygame.KEYDOWN:
                # Key events
                if event.key == pygame.K_r and self.debug:
                    self.loaded = False
                    self.new()

                if event.key == pygame.K_h:
                    self.debug = not self.debug

                if event.key == pygame.K_k and self.debug:
                    # kill all enemies
                    for e in self.enemies:
                        e.hp = 0

    def screenWrap(self, player, dungeon):
        """Game class method to check if the player goes outside the screen
           if they do, set their new position based on where they went

        """
        index = list(dungeon.current_room_index)
        direction = ''
        new_pos = pygame.math.Vector2(player.hit_rect.center)
        if player.hit_rect.left < cfg.TILESIZE:
            direction = (-1, 0)
            player.vel = pygame.math.Vector2(0, 0)
            new_pos.x = cfg.WIDTH - player.hit_rect.width - cfg.TILESIZE
            index[1] -= 1
        elif player.hit_rect.right > cfg.WIDTH - cfg.TILESIZE:
            player.vel = pygame.math.Vector2(0, 0)
            direction = (1, 0)
            new_pos.x = player.hit_rect.width + cfg.TILESIZE
            index[1] += 1
        elif player.hit_rect.top < cfg.GUI_HEIGHT + cfg.TILESIZE:
            player.vel = pygame.math.Vector2(0, 0)
            direction = (0, -1)
            new_pos.y = cfg.HEIGHT - player.hit_rect.height - cfg.TILESIZE
            index[0] -= 1
        elif player.hit_rect.bottom > cfg.HEIGHT - cfg.TILESIZE:
            player.vel = pygame.math.Vector2(0, 0)
            direction = (0, 1)
            new_pos.y = player.hit_rect.height + cfg.GUI_HEIGHT + cfg.TILESIZE
            index[0] += 1

        try:
            return direction, index, new_pos
        except Exception:
            traceback.print_exc()

    def update(self):
        """Game class method update game situation.

        """

        if self.pistolpick == True:
            self.player.itemA = self.currentpistol

        elif self.machinegunpick == True:
            self.player.itemA = self.currentmachine

        if self.debug:
            self.caption = (str(round(self.clock.get_fps(), 2)))

        else:
            self.caption = cfg.TITLE

        pygame.display.set_caption(self.caption)

        # check for key presses
        self.key_down = self.keyDown(self.event_list, self)

        if self.state == 'GAME':
            pygame.mouse.set_visible(False)
            self.all_sprites.update()

            self.inventory.update()
            # check for room transitions on screen exit (every frame)
            self.direction, self.new_room, self.new_pos = self.screenWrap(
                self.player, self.dungeon)

            if self.direction == UP:
                self.dircheck = UP
            elif self.direction == DOWN:
                self.dircheck = DOWN
            elif self.direction == LEFT:
                self.dircheck = LEFT
            elif self.direction == RIGHT:
                self.dircheck = RIGHT

            if self.new_room != self.dungeon.current_room_index:
                self.prev_room = self.dungeon.current_room_index
                self.dungeon.current_room_index = self.new_room
                self.state = 'TRANSITION'

            # When in a fight, shut the doors:
            if not self.dungeon.room_current.cleared:
                self.checkFight(self)

            if self.check:
                if self.dungeon.room_current.cleared:
                    a = self.clearcount
                    self.clearcount = self.clearcount + 1
                    self.spritecheck = []
                    if a + 1 == self.clearcount:
                        self.check = False

        elif self.state == 'MENU' or self.state == 'MENU_TRANSITION':
            pygame.mouse.set_visible(True)
            self.inventory.update()

        elif self.state == 'TRANSITION':
            #self.RoomTransition(self.new_pos, self.direction)
            # ^this went into draw()
            pass

        elif self.state == 'CUTSCENE':
            self.walls.update()

    def WinSit(self):
        """Game class method to check if player won then show next screen.

        """
        global Dx, Dy

        if self.dungeon.room_current.type == "endboss" and self.dungeon.room_current.cleared:
            pygame.mouse.set_visible(True)
            self.state = 'WIN'
            self.actual_screen.blit(self.imageLoader.WinImg, (0, 0))
            self.Quitbtn = Button(400, 450, 150, 50, 'Quit', (200, 20, 20, 0),
                                  (180, 0, 0, 0), self.imageLoader.font1,
                                  self.imageLoader.font2)
            self.Nextbtn = Button(200, 450, 150, 50, 'Next', (200, 20, 20, 0),
                                  (180, 0, 0, 0), self.imageLoader.font1,
                                  self.imageLoader.font2)
            self.Quitbtn.show(self.imageLoader.WinImg)
            self.Nextbtn.show(self.imageLoader.WinImg)
            if self.Quitbtn.is_mouse_clicked():
                pygame.quit()
                quit()
            if self.Nextbtn.is_mouse_clicked():
                self.levelcleared += 1
                self.clearcount = 0
                self.check = True
                cfg.DUNGEON_SIZE = (Dx, Dy)
                self.state = 'GAME'
                self.new()
                Dx = Dx + 1
                Dy = Dy + 1

    def GameOverr(self):
        """Game class method to check if player is dead and game is over.

        """
        global Dx, Dy
        if self.player.Dead == True:
            self.state = 'GAME OVER'
            pygame.mouse.set_visible(True)
            self.actual_screen.blit(self.imageLoader.GameOverImg, (0, 0))
            self.Quitbtn = Button(400, 450, 150, 50, 'Quit', (200, 20, 20, 0),
                                  (180, 0, 0, 0), self.imageLoader.font1,
                                  self.imageLoader.font2)
            self.Nextbtn = Button(200, 450, 150, 50, 'Restart',
                                  (200, 20, 20, 0), (180, 0, 0, 0),
                                  self.imageLoader.font1,
                                  self.imageLoader.font2)
            self.Quitbtn.show(self.imageLoader.GameOverImg)
            self.Nextbtn.show(self.imageLoader.GameOverImg)
            if self.Quitbtn.is_mouse_clicked():
                pygame.quit()
                quit()
            if self.Nextbtn.is_mouse_clicked():
                self.levelcleared = 1
                self.clearcount = 0
                Dx = 5
                Dy = 5
                self.pistolpick = True
                self.machinegunpick = False
                self.check = True
                cfg.DUNGEON_SIZE = (Dx, Dy)
                self.state = 'GAME'
                self.new()

    def draw(self):
        """Game class method to draw

        """
        if self.state != 'TRANSITION':
            # draw the background (tilemap)
            self.screen.blit(self.background, (0, cfg.GUI_HEIGHT))
            # call additional draw methods (before drawing)
            for sprite in self.all_sprites:
                if hasattr(sprite, 'draw_before'):
                    sprite.draw_before()
            # draw the sprites
            self.all_sprites.draw(self.screen)

            for sprite in self.all_sprites:
                if hasattr(sprite, 'draw_after'):
                    sprite.draw_after()

            # for machine gun with player
            if self.machinegunpick and self.player.itemA == self.currentmachine:
                if self.player.itemA.check and self.player.mana < 9.8:
                    self.WSignRect = self.WSign.get_rect()
                    self.WSignRect.center = pygame.mouse.get_pos(
                    ) - pygame.math.Vector2(0, 30)
                    self.screen.blit(self.WSign, self.WSignRect)

                if self.player.itemA.check and self.player.mana > 9.8:
                    self.WSignRect = self.WSign2.get_rect()
                    self.WSignRect.center = pygame.mouse.get_pos(
                    ) - pygame.math.Vector2(0, 30)
                    self.screen.blit(self.WSign2, self.WSignRect)

                if self.player.mana < 0.2:
                    self.cursor = self.imageLoader.CursorMain1
                elif self.player.mana < 1.4:
                    self.cursor = self.imageLoader.cursor[0]
                elif self.player.mana < 2.8:
                    self.cursor = self.imageLoader.cursor[1]
                elif self.player.mana < 4.2:
                    self.cursor = self.imageLoader.cursor[2]
                elif self.player.mana < 5.6:
                    self.cursor = self.imageLoader.cursor[3]
                elif self.player.mana < 7:
                    self.cursor = self.imageLoader.cursor[4]
                elif self.player.mana < 8.4:
                    self.cursor = self.imageLoader.cursor[5]
                elif self.player.mana < 9.8:
                    self.cursor = self.imageLoader.cursor[6]
                elif self.player.mana < 11.4:
                    self.cursor = self.imageLoader.cursor[7]

                self.cursorrect = self.cursor.get_rect()
                self.cursorrect.center = pygame.mouse.get_pos()
                self.screen.blit(self.cursor, self.cursorrect)

            else:
                self.cursor = self.imageLoader.CursorMain1
                self.cursorrect = self.cursor.get_rect()
                self.cursorrect.center = pygame.mouse.get_pos()
                self.screen.blit(self.cursor, self.cursorrect)

        else:
            self.RoomTransition(self.new_pos, self.direction)
            if not self.dungeon.room_current.cleared:
                self.check = True

        # ----- DEBUG STUFF ----- #
        # draw hitboxes in debug mode
        if self.debug:
            for sprite in self.all_sprites:
                if hasattr(sprite, 'hit_rect'):
                    pygame.draw.rect(self.screen, cfg.CYAN, sprite.hit_rect, 1)
                if hasattr(sprite, 'interact_rect'):
                    pygame.draw.rect(self.screen, cfg.GREEN,
                                     sprite.interact_rect, 1)

        # draw the inventory
        self.drawGUI()
        self.screen = pygame.transform.scale(self.screen,
                                             (cfg.S_WIDTH, cfg.S_HEIGHT))
        self.actual_screen.blit(self.screen, (0, 0))
        self.WinSit()
        self.GameOverr()
        pygame.display.update()

    def drawGUI(self):
        """Game class method draw inventory with map.

        """
        self.inventory.map_img = self.dungeon.render_rooms_map()
        self.inventory.draw()

    def tileRoom(self, game, tileset, index):
        """Game class method render room tiles.

        """
        image = pygame.Surface((cfg.WIDTH, cfg.HEIGHT - cfg.GUI_HEIGHT))
        data = game.dungeon.room_matrix[index[0]][index[1]].tiles

        for i in range(len(data)):
            for j in range(len(data[i])):
                x = j * cfg.TILESIZE
                y = i * cfg.TILESIZE
                try:
                    image.blit(tileset[data[i][j]], (x, y))
                except Exception:
                    traceback.print_exc()
                    return

        return image

    def RoomTransition(self, new_pos, direction):
        """Game class method for room transition.

        Args:
            new_pos (tuple): tuple with x,y corrdinates for the new room for player
            direction (Up, DOWN, LEFT OR RIGHT): direction of start in new room

        """
        if not self.in_transition:
            # store the old background image temporarily
            self.old_background = self.background

            # blit the background and sprites to prevent flickering
            self.screen.blit(self.old_background, (0, cfg.GUI_HEIGHT))
            self.all_sprites.draw(self.screen)

            # build the new room
            self.background = self.tileRoom(self, self.imageLoader.tileset,
                                            self.dungeon.current_room_index)

            # scroll the new and old background
            # start positions for the new bg are based on the direction the
            # player is moving
            start_positions = {
                UP: pygame.math.Vector2(0, -(cfg.HEIGHT - cfg.GUI_HEIGHT * 2)),
                DOWN: pygame.math.Vector2(0, cfg.HEIGHT),
                LEFT: pygame.math.Vector2(-cfg.WIDTH, cfg.GUI_HEIGHT),
                RIGHT: pygame.math.Vector2(cfg.WIDTH, cfg.GUI_HEIGHT)
            }

            self.bg_pos1 = start_positions[direction]
            # pos2 is the old bg's position that gets pushed out of the screen
            self.bg_pos2 = pygame.math.Vector2(0, cfg.GUI_HEIGHT)
            self.in_transition = True

            self.transitRoom(self, self.dungeon, self.bg_pos1)

        else:
            if self.bg_pos1 != (0, cfg.GUI_HEIGHT):
                # moves the 2 room backrounds until the new background is at (0,0)
                # PROBLEM: Only works with certain scroll speeds!

                # calculate the move vector based on the direction
                move = (pygame.math.Vector2(0, 0) -
                        direction) * cfg.SCROLLSPEED

                # move the background surfaces
                self.bg_pos1 += move
                self.bg_pos2 += move

                if direction == UP:
                    self.bg_pos1.y = min(cfg.GUI_HEIGHT, self.bg_pos1.y)
                elif direction == DOWN:
                    self.bg_pos1.y = max(cfg.GUI_HEIGHT, self.bg_pos1.y)
                elif direction == LEFT:
                    self.bg_pos1.x = min(0, self.bg_pos1.x)
                elif direction == RIGHT:
                    self.bg_pos1.x = max(0, self.bg_pos1.x)

                # move the sprites during transition
                for sprite in self.all_sprites:
                    sprite.rect.topleft += move
                    sprite.hit_rect.topleft += move
                    sprite.pos += move

                self.screen.blit(self.old_background, self.bg_pos2)
                self.screen.blit(self.background, self.bg_pos1)
                self.all_sprites.draw(self.screen)

            else:
                # update the player's position
                self.player.pos = pygame.math.Vector2(new_pos)
                self.player.hit_rect.center = pygame.math.Vector2(new_pos)
                self.player.spawn_pos = pygame.math.Vector2(new_pos)
                self.player.rect.bottom = self.player.hit_rect.bottom

                # end transtition
                self.in_transition = False
                self.state = 'GAME'

                # blit the background and sprites to prevent flickering
                self.screen.blit(self.background, (0, cfg.GUI_HEIGHT))
                self.all_sprites.draw(self.screen)
Example #3
0
#!/usr/bin/env python3
from Dungeon import Dungeon

dungeon = Dungeon()
entrance = dungeon.create()
room1 = dungeon.newRoom(entrance, "N")

# Create more rooms by adding more "dungeon.newRoom(room, direction)"
# like the line below. "room" must be a room that you've already created.
# "direction" must be one of:
#    "N" - North
#    "S" - South
#    "E" - East
#    "W" - West
#    "U" - Up (above)
#    "D" - Down (below)
# Create one or two more rooms, then explore you dungeon to make sure
# that it is as you expect, then create more rooms.