class QuestManager: """ Manages the quest currently running in a given channel. """ def __init__(self, channel): self.channel = channel self.channel_settings = self.channel.channel_manager.channel_settings[self.channel.owner] self.quest = None self.party = [] self.quest_timer = None self.quest_state = QuestState.ready self.commands = CommandSet() # Channel is guaranteed to be initialized at this point if self.channel_settings['quest_enabled']: self.enable_questing() else: self.disable_questing() def start_quest_advance_timer(self, duration=settings.QUEST_DURATION): """ Starts a timer until the quest advances. :param duration: int - Number of seconds until the current quest advances. """ self.kill_quest_advance_timer() self.quest_timer = Timer(duration, self.quest_advance) def kill_quest_advance_timer(self): """ Stops the quest advancement timer and clears it from the update loop and from this manager. """ if self.quest_timer: # Removes the timer from the update loop self.quest_timer.cancel() self.quest_timer = None def enable_questing(self): """ Enables quest party formation. """ self.kill_quest_advance_timer() self.quest_state = QuestState.ready self.commands = CommandSet(exact_match_commands={ '!quest': self.create_party}) def disable_questing(self): """ Disables quest mode. Any currently running quest is canceled. Don't have to clear out instance variables since they will be overwritten by the next quest that is started. """ self.kill_quest_advance_timer() self.quest_state = QuestState.disabled self.commands = CommandSet(exact_match_commands={ '!quest': self.disabled_message}) def quest_cooldown(self): """ Puts quest mode on cooldown. """ self.quest_state = QuestState.on_cooldown self.commands = CommandSet(exact_match_commands={ '!quest': self.recharging_message}) self.start_quest_advance_timer(self.channel_settings['quest_cooldown']) def quest_advance(self): """ Advances the current quest. """ if self.quest_state is QuestState.on_cooldown: self.enable_questing() elif self.quest_state is QuestState.forming_party: self.start_quest(random.choice(QUEST_LIST[len(self.party)])(self)) elif self.quest_state is QuestState.active: self.quest.advance() def start_quest(self, quest): """ Starts a random quest depending on the number of party members. :param quest: Quest - The quest that we are preparing """ self.quest_state = QuestState.active self.quest = quest self.commands = CommandSet(children={self.quest.commands}) self.quest.advance(self.quest.starting_segment) def disabled_message(self, display_name): """ Tells users that !quest is currently disabled. :param display_name: str - The user that requested quest mode. """ self.channel.send_msg( 'Sorry {}, questing is currently disabled. Ask a mod to type !queston to re-enable questing.'.format( display_name)) def recharging_message(self, display_name): """ Tells users that !quest is currently on cooldown. :param display_name: str - The user that requested quest mode. """ self.channel.send_msg('Sorry {}, quests take {} seconds to recharge. ({} seconds remaining.)'.format( display_name, self.channel_settings['quest_cooldown'], self.quest_timer.remaining())) def create_party(self, display_name): """ Creates a party with the given player as the leader. :param display_name: str - The user that started the quest. """ self.quest_state = QuestState.forming_party self.party = [display_name] self.commands = CommandSet(exact_match_commands={ '!quest': self.join_quest}) self.channel.send_msg(display_name + ' wants to attempt a quest. Type "!quest" to join!') self.start_quest_advance_timer(settings.QUEST_DURATION) def join_quest(self, display_name): """ Joins the existing quest party. :param display_name: str - The user trying to join the quest party. """ if display_name not in self.party: self.party.append(display_name) # The index of the quest list is the max number of players, so if we've reached it then start the quest if len(self.party) >= len(QUEST_LIST) - 1: self.quest_advance()