Пример #1
0
class Level():
    def __init__(self, file_path, logger=None, testing=False):
        if logger is None:
            logging.basicConfig(level=logging.INFO,
                            format='%(asctime)s %(message)s')
            logger = logging.getLogger(__name__)
        self.logger=logger
        self.logger.info("Loading Level" )
        self.terrain = Terrain(TERRAIN_TO_FILE, NODE_SIZE, BACKGROUND)
        with open(file_path) as f:
            lines = f.read().split("\n")
            dimensions = lines[0]
            row,column = self.parse_dimensions(dimensions)
            self.rows = row
            self.columns = column
            self.graph = Graph(row, column, self.logger)
            cond1 = len(lines[1:]) == row or len(lines[1:]) - 1 == row 
            assert cond1 ,'File has mismatching dimensions'
            r = 0
            for line in lines[1:]:
                if line != "":
                    nodes = line.split(",")
                    cond2 = len(nodes) == column 
                    assert cond2, 'File has mismatching dimensions'
                    c = 0
                    for node in nodes:
                        n = Node(string_object=node,
                                  images=self.terrain,
                                  logger=self.logger)
                        self.graph.set_node(r, c, n)
                        c += 1
                r += 1
        self.logger.info("Done loading level")
        if not testing:
            sys.setrecursionlimit(self.graph.columns * self.graph.rows)

    def save(self, file_path):
        '''
        a method to save the level to a specific file
        Parameters:
            file_path: where to save the file (os.path)
        Returns:
            None
        '''
        self.logger.info("Saving file %s" % file_path)
        with open(file_path, 'w') as f:
            f.write(str(self.rows) + 'X' + str(self.columns) + '\n')
            for row in range(0, self.rows):
                line = []
                for node in self.graph.iterate(row):
                    line.append(str(node))
                self.logger.debug(line)
                f.write(",".join(line) + '\n')
        self.logger.info("Done Saving file %s" % file_path)
        return

    def parse_dimensions(self, dimensions):
        '''
        a method to parse the dimensions string
        Parameters:
            dimensions: the dimensions (string)
        Returns:
            row: the number of nodes in each row (int)
            column: the number of node in each column (int)
        '''
        self.logger.debug(dimensions)
        dimensions = dimensions.split("X")
        self.logger.debug(dimensions)
        assert len(dimensions) == 2, 'Invalid Dimensions of file'
        row = int(dimensions[0])
        column = int(dimensions[1])
        return (row, column)

    def draw(self, surface):
        '''
        a method that draws the level
        Parameters:
            surface: the pygame display screen (surface)
        Returns:
            None
        '''
        self.graph.draw(surface)

    def update_node(self,x, y, terrain, player=None):
        '''
        a method that given the (x,y) determines updates the node at
        that position to given terrain type
        Parameters:
            x: the x position on the screen (int)
            y: the y position on the screen (int)
            terrain: the terrain type (int)
            player: the player the node belongs to (Player)
        Returns:
            None
        '''
        x = x - x % NODE_SIZE
        y = y -  y % NODE_SIZE
        if player is not None:
            player = player.get_id()
        n = Node(x=x, y=y, terrain=terrain, player=player, images=self.terrain)
        row = y // NODE_SIZE
        column = x // NODE_SIZE
        self.graph.set_node(row, column, n)
        return

    def add_castle(self, x, y, player):
        '''
        a method to add a castle
        Parameters:
            x: the position (int)
            y: the y position (int)
            player: the player who the castle belongs to (player)
        Returns:
            added: True if able to add castle False otherwise (boolean)
        '''
        x = x - x % NODE_SIZE
        y = y -y % NODE_SIZE
        row = y // NODE_SIZE
        column = x // NODE_SIZE
        added = False
        row_cond = row > 2 and row < self.rows - 2
        col_cond = column > 2 and column < self.columns - 2
        outer_edge = row_cond and col_cond
        if player is not None:
            p = player.get_id()
            self.logger.info("Player's Castle: %i" % p)
        self.logger.info("Outer edge condition: %s" % outer_edge)
        if player is not None and outer_edge and self.check_castle(row, column):
            nodes = []
            i = 0
            for (r, c) in castle_spot(row, column):
                nodes.append(Node(x=c*NODE_SIZE, y=r*NODE_SIZE, terrain=CASTLE,
                            player=p, images=self.terrain))
                self.graph.set_node(r, c, nodes[i])
                i += 1
            added = True
            player.add_castle(nodes[0])
        return added

    def check_castle(self, row, column):
        '''
        a method to check if can add the castle to that spot
        Parameters:
            row: the row index (int)
            column: the column index (int)
        Return:
            valid: True if castle is valid, False otherwise (boolean)
        '''
        valid = True
        for (r,c) in castle_spot(row, column):
            try:
                node_id = self.graph.get_node_id(r, c)
                if not self.graph.available(node_id):
                    self.logger.info("Node not available: %d" % node_id)
                    valid = False
            except:
                self.logger.info("Invalid node id")
                valid = False
            if not valid:
                break
        self.logger.info("Add castle: %s" % valid)
        return valid

    def add_cannon(self, x, y, player, game=True):
        '''
        a method to add a cannon to the level and the player
        Parameters:
            x: the x position (int)
            y: the y position (int)
            player: the player cannon (player)
            game: True if part of game, False if want to add manually
        Returns:
            True if cannon was added
            False otherwise
        '''
        x = x - x % NODE_SIZE
        y = y -  y % NODE_SIZE
        add = True
        try:
            cannon = Node(x=x, y=y, terrain=CANNON,
                          images=self.terrain,player=player.get_id())
            row = y // NODE_SIZE
            column = x // NODE_SIZE
            node = self.graph.get_node(row, column)
            if node.get_type() not in CANBUILD:
                add = False
            if game and node.is_painted():
                add = False
        except:
            add = False
        if add:
            self.graph.set_node(row, column, cannon)
            player.add_cannon(cannon)
        return add

    def destroy_node(self, x, y):
        '''
        a method that destroys the node
        Parameters:
            x: the x position (int)
            y: the y position (int)
        Returns:
            None
        '''
        row = (y) // NODE_SIZE
        column = (x) // NODE_SIZE
        self.graph.destroy_node(row, column)

    def add_piece(self, player):
        '''
        a method used to add the piece of the player to the level
        Parameters:
            piece: the building piece to add (Piece)
        Returns:
            True if able to add piece, False otherwise (boolean)
        '''
        added = False
        # check if able to add piece
        valid = True
        piece = player.get_piece()
        for point in piece.return_points():
            column = point[0] 
            row = point[1]
            column = column // NODE_SIZE
            row = row // NODE_SIZE
            try:
                node = self.graph.get_node(row, column)
                if node.get_type() not in CANBUILD:
                    valid = False
                    break
            except AssertionError:
                valid = False
                break
        self.logger.debug(" Able to add piece: %s" % valid)
        if valid:
            added = True
            for point in piece.return_points():
                column = point[0]
                row = point[1]
                x = column - column % NODE_SIZE
                y = row - row % NODE_SIZE
                column = column // NODE_SIZE
                row = row // NODE_SIZE
                add_node = Node(x=x, y=y, terrain=BLOCK,
                                images=self.terrain, player=player.get_id())
                self.graph.set_node(row, column, add_node)
        return added

    def update(self):
        '''
        a method to update the level
        Parameters:
            None:
        Returns:
            None
        '''
        self.graph.paint()

    def add_players_castles(self, player):
        '''
        a method that takes the levels castles and adds them to the player
        Parameters:
            player: the player to add to (Player)
        Returns:
            None
        '''
        pid = player.get_id()
        self.logger.info("Adding Castles to player: %d" % pid)
        for node in self.graph.iterate():
            right_player = node.get_player() == pid
            if node.get_type() == CASTLE and right_player:
                player.add_castle(node)
        return

    def cleanup(self):
        '''
        a method that cleanup the level of all destroyed squares
        Parameters:
            None
        Returns:
            None
        '''
        for node in self.graph.iterate():
            if node.get_state() == DESTROYED:
                (x, y) = node.get()
                self.update_node(x, y, GRASS)