Exemplo n.º 1
0
class GameState(object):

    """Represents the game state"""

    def __init__(self):
        """Simple init of variables"""
        self.arena = None

        self._player_queue = udeque()
        self._sticky_actions = {}
        self._action_queue = deque()

        self._lookup = {
            'B': Block,
            '.': DestructibleBlock,
            'S': SpawnPoint,
        }

        self._flames = []

        self.arena_load(["arenas", "default.bmm"])

    def arena_load(self, filename_list):
        """Load an arena from a file"""
        lines = []

        with codecs.open(os.sep.join(filename_list), 'r', 'UTF-8') as fp:
            for line in fp:
                lines.append(line.rstrip())

        self.arena = Arena(max((len(line) for line in lines)), len(lines))

        for row, line in enumerate(lines):
            for col, char in enumerate(line):
                if char == ' ':
                    continue

                self._lookup[char](state=self, coords=(col, row))

    def __str__(self):
        """Produce a string representation of the arena in the same format as the map files"""
        chars = []
        old_y = 0
        for _, y, l in self.arena:
            if y != old_y:
                chars.append('\n')
                old_y = y

            chars.append(str(max(l + [GameObject(state=None, coords=None)],
                                 key=lambda o: o.ZINDEX)))

        chars.append('\n')

        return ''.join(chars)

    def __repr__(self):
        return str(self)

    def player_add(self, player):
        """Add a player to the game state"""
        self._player_queue.appendleft(player)

    def player_remove(self, player):
        """Remove a player from the game state"""
        player.remove()

    def spawn(self):
        """Spawn the players into the arena"""
        p_no = 0
        for x, y, _ in self.arena:
            if self.arena.coords_have_class((x, y), SpawnPoint):
                try:
                    player = self._player_queue.pop()
                except (IndexError):
                    break

                p_no += 1
                player.spawn(p_no, state=self, coords=(x, y))
                self._sticky_actions[player] = None

    def tick(self, count=1):
        """Step to the next game state: this is an example and is used for testing"""
        for _ in xrange(count):
            self._flames_process()
            self._actions_process()
            self._bombs_process()

    def action_add(self, player, action):
        """Add player actions to a queue for processing"""
        self._action_queue.appendleft((player, action))

    def _actions_process(self):
        """Process queued actions or fall back to sticky actions"""
        unexecuted = deque()
        had_turn = []
        while self._action_queue:
            player, action = self._action_queue.pop()
            if player in had_turn:
                unexecuted.appendleft((player, action))
            else:
                self._player_action(player, action)
                self._player_sticky(player, action)
                had_turn.append(player)

        for player in self._sticky_actions:
            if player not in had_turn:
                self._player_action(player, self._sticky_actions[player])

        self._action_queue = unexecuted

    def _player_action(self, player, action):
        """Perform player action"""
        try:
            px, py = player.coords
        except TypeError:
            # coords are invalid, player is not in the arena: noop
            return

        try:
            if action == Player.BOMB:
                player.drop_bomb()
            elif action == Player.UP:
                player.move((px, py-1))
            elif action == Player.DOWN:
                player.move((px, py+1))
            elif action == Player.LEFT:
                player.move((px-1, py))
            elif action == Player.RIGHT:
                player.move((px+1, py))
        except IndexError:
            self._sticky_actions[player] = None

    def _player_sticky(self, player, action):
        """Add actions to "sticky" lookup, if applicable"""
        if action == Player.BOMB:
            self._sticky_actions[player] = None
        else:
            self._sticky_actions[player] = action

    def _bombs_process(self):
        """Tick bombs"""
        for p in self._sticky_actions:
            for b in p._bombs_live[:]: # copy the list so we can safely iterate
                if b in p._bombs_live:
                    b.tick()

    def _flames_process(self):
        """Tick the flames"""
        for f in self._flames[:]: # copy the list so we can safely iterate
            f.tick()