예제 #1
0
    async def on_command_error(self, ctx, error):
        # pylint: disable=too-many-return-statements, arguments-differ, too-many-branches
        if hasattr(ctx.command, 'on_error'):
            return
        if isinstance(error, commands.CommandNotFound):
            args = remove_prefix(ctx.message.content,
                                 ctx.bot.global_prefix).split(' ')
            command = args[0]

            if ctx.guild is not None:  # DM only
                return
            games = [
                *filter(lambda g: ctx.author in g.players, self.games.values())]
            if len(games) == 0:
                return
            pl_game = games[0]
            if pl_game.phase != Phase.NIGHT:
                return
            player = pl_game.players[ctx.author]

            if not alive_or_recent_jester(player, pl_game) \
                    or not hasattr(player.role, 'action'):
                return
            valid_actions = [player.role.action, 'noaction'] if not isinstance(player.role.action, list) \
                else player.role.action + ['noaction']
            if command.lower() not in valid_actions:
                return
            await player.role.on_pm_command(ctx, pl_game, player, args)

            return  # ignore invalid commands

        elif isinstance(error, commands.MissingRequiredArgument):
            return await ctx.send(f'Missing required argument {error.param}')
        elif isinstance(error, (commands.ArgumentParsingError, commands.BadArgument)):
            return await ctx.send('Invalid input')
        elif isinstance(error, commands.CheckFailure):
            return await ctx.send(error)
        elif isinstance(error, commands.CommandOnCooldown):
            retry_after = round(error.retry_after)
            return await ctx.send('You are using this command too fast: try again in {} second{}'.format(retry_after, pluralize(retry_after)))

        elif isinstance(error, PhaseChangeError):
            # Inform users that game has ended and remove channel id from `self.games`.
            await ctx.send('There was an error incrementing the phase. The game has ended.')
            self.games.pop(ctx.channel.id, None)
            return self.logger.exception(error, exc_info=(type(error), error, error.__traceback__))

        await ctx.send(f'Uncaught exception: ```{error}```')

        if config.get('ENV', '') == "production":
            # End game only if `env` is set to 'production'.
            await ctx.send('\nThe game has ended.')
            self.games.pop(ctx.channel.id, None)

        self.logger.exception(error, exc_info=(
            type(error), error, error.__traceback__))
예제 #2
0
 def action_only_filter(player):
     if not alive_or_recent_jester(player, self.game):
         return False
     can_do, _ = player.role.can_do_action(self.game)
     return can_do
예제 #3
0
    async def increment_phase(self):
        # If it is day, `phase_t` should be equal to night_duration and vice versa.
        # `phase_duration` is used at the end of the function.
        # `phase_t` is used in day/night starting messages.
        if self.phase == Phase.DAY:
            phase_duration = self.config['night_duration']
            phase_t = round(phase_duration / 60, 1)
        else:
            # Set it to day duration for all other phases.
            # Note that it should almost always be `Phase.NIGHT`.
            phase_duration = self.config['day_duration']
            phase_t = round(phase_duration / 60, 1)

        # night loop is the same as the pregame loop
        if self.cycle == 0 or self.phase == Phase.NIGHT:
            # resolve night actions
            self.phase = Phase.STANDBY  # so the event loop doesn't mess things up here
            dead_players = await self.night_actions.resolve()

            for player in dead_players:
                role_text = 'We could not determine their role.' if player.role.cleaned else f'They were a {player.display_role}.'
                await self.channel.send(
                    f'{player.user.name} died last night. {role_text}\n')

            game_ended, winning_faction, independent_wins = self.check_endgame(
            )
            if game_ended:
                return await self.end(winning_faction, independent_wins)

            # clear visits
            for player in self.players:
                player.visitors.clear()

            # voting starts
            self.night_actions.reset()
            self.phase = Phase.DAY
            self.cycle = self.cycle + 1
            alive_players = self.players.filter(is_alive=True)
            # populate voting cache
            self.votes['nolynch'] = []
            self.votes['notvoting'] = []
            for player in alive_players:
                self.votes[player.user.id] = []
                self.votes['notvoting'].append(player)

            await self.channel.send(
                f'Day **{self.cycle}** will last {phase_t} minutes.'
                f' With {len(alive_players)} alive, it takes {self.majority_votes} to lynch.'
            )
        else:
            self.phase = Phase.STANDBY
            # remove all votes from every player
            self.votes.clear()
            await self.channel.send(
                f'Night **{self.cycle}** will last {phase_t} minutes. '
                'Send in those actions quickly!')

            # recently lynched jesters and alive players are allowed to send in actions
            for player in filter(lambda p: alive_or_recent_jester(p, self),
                                 self.players):
                if hasattr(player.role, 'on_night'):
                    await player.role.on_night(self.bot, player, self)

            self.phase = Phase.NIGHT

        self.phase_end_at = datetime.now() \
            + timedelta(seconds=phase_duration)