예제 #1
0
    def get_progress(self):
        total_count = 0

        # Handle exploration, all bits set if completed.
        if self.is_exploration_quest() and self.get_quest_state() == QuestState.QUEST_REWARD:
            return total_count ^ 0xFFFFFFFF

        # Creature or gameobject.
        req_creature_or_go = QuestHelpers.generate_req_creature_or_go_list(self.quest)
        req_creature_or_go_count = QuestHelpers.generate_req_creature_or_go_count_list(self.quest)
        for index, creature_or_go in enumerate(req_creature_or_go):
            if req_creature_or_go[index] > 0:
                current_count = eval(f'self.db_state.mobcount{index + 1}')
                required = req_creature_or_go_count[index]
                # Consider how many bits the previous creature required.
                offset = index * req_creature_or_go_count[index - 1] if index > 0 else 0

                for i in range(required):
                    if i < current_count:  # Turn on actual kills
                        total_count += (1 & 1) << (1 * i) + offset
                    else:  # Fill remaining 0s (Missing kills)
                        total_count += 0 << (1 * i) + offset

                # Debug, enable this to take a look on whats happening at bit level.
                # Logger.debug(f'{bin(mob_kills)[2:].zfill(32)}')

        return total_count
예제 #2
0
    def can_complete_quest(self):
        if QuestHelpers.is_instant_complete_quest(self.quest):
            return True

        # Check for required kills / gameobjects.
        required_creature_go = QuestHelpers.generate_req_creature_or_go_count_list(self.quest)
        for i in range(4):
            current_value = eval(f'self.db_state.mobcount{i + 1}')
            if current_value < required_creature_go[i]:
                return False

        # Check for required items.
        required_items = QuestHelpers.generate_req_item_count_list(self.quest)
        for i in range(4):
            current_value = eval(f'self.db_state.itemcount{i + 1}')
            if current_value < required_items[i]:
                return False

        # Handle exploration.
        if self.quest.QuestFlags & QuestFlags.QUEST_FLAGS_EXPLORATION:
            if self.get_quest_state() != QuestState.QUEST_REWARD:
                return False

        # TODO: Check ReqMoney
        return True
예제 #3
0
 def reward_reputation(self):
     faction_reputation_rewards = QuestHelpers.generate_rew_faction_reputation_list(self.quest)
     faction_reputation_gain = QuestHelpers.generate_rew_faction_reputation_gain_list(self.quest)
     for index, faction in enumerate(faction_reputation_rewards):
         if faction:
             gain = faction_reputation_gain[index]
             self.owner.reputation_manager.modify_reputation(faction, gain)
예제 #4
0
 def still_needs_item(self, item_entry):
     req_items = QuestHelpers.generate_req_item_list(self.quest)
     required = item_entry in req_items
     if required:
         index = req_items.index(item_entry)
         required_items = QuestHelpers.generate_req_item_count_list(self.quest)[index]
         current_items = self._get_db_item_count(index)
         return current_items < required_items
예제 #5
0
    def get_dialog_status(self, world_object):
        dialog_status = QuestGiverStatus.QUEST_GIVER_NONE
        new_dialog_status = QuestGiverStatus.QUEST_GIVER_NONE

        if self.player_mgr.is_enemy_to(world_object):
            return dialog_status

        # Relation bounds, the quest giver; Involved relations bounds, the quest completer.
        if world_object.get_type_id() == ObjectTypeIds.ID_UNIT:
            relations_list = WorldDatabaseManager.QuestRelationHolder.creature_quest_starter_get_by_entry(world_object.entry)
            involved_relations_list = WorldDatabaseManager.QuestRelationHolder.creature_quest_finisher_get_by_entry(world_object.entry)
        else:
            return QuestGiverStatus.QUEST_GIVER_NONE

        # Quest finishers
        for involved_relation in involved_relations_list:
            if len(involved_relation) == 0:
                continue
            quest_entry = involved_relation[1]
            # Check if player is already on this quest.
            if quest_entry not in self.active_quests:
                continue
            # Grab QuestTemplate.
            quest = WorldDatabaseManager.QuestTemplateHolder.quest_get_by_entry(quest_entry)
            if not quest:
                continue
            quest_state = self.active_quests[quest_entry].get_quest_state()
            if quest_state == QuestState.QUEST_REWARD or QuestHelpers.is_instant_complete_quest(quest) and self.check_quest_requirements(quest):
                new_dialog_status = QuestGiverStatus.QUEST_GIVER_REWARD
            if new_dialog_status > dialog_status:
                dialog_status = new_dialog_status

        new_dialog_status = dialog_status
        # Quest starters
        if new_dialog_status < QuestGiverStatus.QUEST_GIVER_REWARD:
            for relation in relations_list:
                quest_entry = relation[1]
                # Check if player is already on this quest or completed the quest.
                if quest_entry in self.active_quests:
                    continue
                # Grab QuestTemplate.
                quest = WorldDatabaseManager.QuestTemplateHolder.quest_get_by_entry(quest_entry)
                if not quest:
                    continue
                # Quest is completed and not repeatable.
                if quest_entry in self.completed_quests and not QuestHelpers.is_quest_repeatable(quest):
                    continue
                # Check requirements and also update display status no matter if player does not meet requirements.
                if self.check_quest_requirements(quest):
                    new_dialog_status = self.update_dialog_display_status(quest, new_dialog_status)
                else:
                    new_dialog_status = self.update_dialog_display_status(quest, new_dialog_status, checks_failed=True)

                # Update dialog result if needed.
                if new_dialog_status > dialog_status:
                    dialog_status = new_dialog_status

        return dialog_status
예제 #6
0
 def requires_creature_or_go(self, creature_entry):
     req_creatures_or_gos = QuestHelpers.generate_req_creature_or_go_list(self.quest)
     required = creature_entry in req_creatures_or_gos
     if required:
         index = req_creatures_or_gos.index(creature_entry)
         required_kills = QuestHelpers.generate_req_creature_or_go_count_list(self.quest)[index]
         current_kills = eval(f'self.db_state.mobcount{index + 1}')
         return current_kills < required_kills
     return False
예제 #7
0
 def update_required_items_from_inventory(self):
     req_items = list(filter((0).__ne__, QuestHelpers.generate_req_item_list(self.quest)))
     req_count = list(filter((0).__ne__, QuestHelpers.generate_req_item_count_list(self.quest)))
     for index, item in enumerate(req_items):
         current_count = self.owner.inventory.get_item_count(item)
         self._update_db_item_count(index, current_count, req_count[index], override=True)
     if self.can_complete_quest():
         self.update_quest_state(QuestState.QUEST_REWARD)
     else:
         self.update_quest_state(QuestState.QUEST_ACCEPTED)
예제 #8
0
 def fill_existent_items(self):
     req_item = list(
         filter((0).__ne__,
                QuestHelpers.generate_req_item_list(self.quest)))
     req_count = list(
         filter((0).__ne__,
                QuestHelpers.generate_req_item_count_list(self.quest)))
     for index, item in enumerate(req_item):
         current_count = self.owner.inventory.get_item_count(item)
         if current_count:
             self._update_db_item_count(index, current_count,
                                        req_count[index])
예제 #9
0
 def update_item_count(self, item_entry, quantity):
     req_items = QuestHelpers.generate_req_item_list(self.quest)
     req_count = QuestHelpers.generate_req_item_count_list(self.quest)
     req_item_index = req_items.index(item_entry)
     # Persist new item count.
     self._update_db_item_count(
         req_item_index, quantity,
         req_count[req_item_index])  # Update db memento
     # Notify the current item count to the player.
     data = pack('<2I', item_entry, quantity)
     packet = PacketWriter.get_packet(OpCode.SMSG_QUESTUPDATE_ADD_ITEM,
                                      data)
     self.owner.enqueue_packet(packet)
예제 #10
0
 def update_creature_go_count(self, creature, value):
     creature_go_index = QuestHelpers.generate_req_creature_or_go_list(self.quest).index(creature.entry)
     required = QuestHelpers.generate_req_creature_or_go_count_list(self.quest)[creature_go_index]
     current = self._get_db_mob_or_go_count(creature_go_index)
     # Current < Required is already validated on requires_creature_or_go().
     self._update_db_creature_go_count(creature_go_index, 1)  # Update db memento
     # Notify the current objective count to the player.
     data = pack('<4IQ', self.db_state.quest, creature.entry, current + value, required, creature.guid)
     packet = PacketWriter.get_packet(OpCode.SMSG_QUESTUPDATE_ADD_KILL, data)
     self.owner.enqueue_packet(packet)
     # Check if this makes it complete.
     if self.can_complete_quest():
         self.update_quest_state(QuestState.QUEST_REWARD)
예제 #11
0
 def pop_item(self, item_entry, count):
     req_item = QuestHelpers.generate_req_item_list(self.quest)
     required = item_entry in req_item
     if required:
         req_item_count = QuestHelpers.generate_req_item_count_list(
             self.quest)
         index = req_item.index(item_entry)
         current_count = self.owner.inventory.get_item_count(item_entry)
         if current_count - count < req_item_count[index]:
             self._update_db_item_count(index, -count,
                                        req_item_count[index])
             self.update_quest_state(QuestState.QUEST_ACCEPTED)
             return True
     return False
예제 #12
0
    def is_quest_complete(self, quest_giver_guid):
        quest_giver = None
        high_guid = ObjectManager.extract_high_guid(quest_giver_guid)

        if high_guid == HighGuid.HIGHGUID_GAMEOBJECT:
            quest_giver = MapManager.get_surrounding_gameobject_by_guid(self.owner, quest_giver_guid)
        elif high_guid == HighGuid.HIGHGUID_UNIT:
            quest_giver = MapManager.get_surrounding_unit_by_guid(self.owner, quest_giver_guid)

        if not quest_giver:
            return False

        if QuestHelpers.is_instant_complete_quest(self.quest):
            return True

        if self.db_state.state != QuestState.QUEST_REWARD:
            return False

        if quest_giver.get_type_id() == ObjectTypeIds.ID_UNIT and quest_giver.get_type_id() != ObjectTypeIds.ID_PLAYER:
            involved_relations_list = WorldDatabaseManager.QuestRelationHolder.creature_quest_finisher_get_by_entry(quest_giver.entry)
        elif quest_giver.get_type_id() == ObjectTypeIds.ID_GAMEOBJECT:
            involved_relations_list = WorldDatabaseManager.QuestRelationHolder.gameobject_quest_finisher_get_by_entry(quest_giver.entry)
        else:
            return False

        # Return if this quest is finished by this quest giver.
        return self.quest.entry in {quest_entry[1] for quest_entry in involved_relations_list}
예제 #13
0
    def send_quest_giver_request_items(self, active_quest, quest_giver_id,
                                       close_on_cancel):
        is_complete = active_quest.is_quest_complete(quest_giver_id)
        quest_title_bytes = PacketWriter.string_to_bytes(
            active_quest.quest.Title)
        quest = active_quest.quest

        if quest.RequestItemsText:
            dialog_text_bytes = PacketWriter.string_to_bytes(
                quest.RequestItemsText)
        else:
            dialog_text_bytes = PacketWriter.string_to_bytes(quest.Objectives)

        data = pack(
            f'<QI{len(quest_title_bytes)}s{len(dialog_text_bytes)}s4I',
            quest_giver_id,
            quest.entry,
            quest_title_bytes,
            dialog_text_bytes,
            0,  # Emote delay
            0,  # Emote id
            1 if close_on_cancel else 0,  # Close Window after cancel
            quest.RewOrReqMoney
            if quest.RewOrReqMoney >= 0 else -quest.RewOrReqMoney)

        req_items = QuestHelpers.generate_req_item_list(quest)
        req_items_count_list = QuestHelpers.generate_req_item_count_list(quest)
        data += pack('<I', len(req_items))
        for index in range(0, 4):
            if req_items[index] == 0:
                continue
            data += self._gen_item_struct(req_items[index],
                                          req_items_count_list[index])

        data += pack(
            '<4I',
            0x02,
            0x03 if is_complete else
            0x00,  # Completable = flags1 && flags2 && flags3 && flags4
            0x04,  # flags2
            0x08  # flags3
        )

        packet = PacketWriter.get_packet(OpCode.SMSG_QUESTGIVER_REQUEST_ITEMS,
                                         data)
        self.player_mgr.enqueue_packet(packet)
예제 #14
0
    def send_quest_giver_offer_reward(self, quest, quest_giver_guid, enable_next=True):
        # Validate if its active to player and if its an instant complete quest.
        if quest.entry not in self.active_quests and not QuestHelpers.is_instant_complete_quest(quest):
            return

        # If this request is from an active quest (not an automatic instant complete dialog) validate again.
        if quest.entry in self.active_quests:
            # While the dialog was open displaying 'Quest Complete' the user destroyed items.
            # Validate if this quest can be completed.
            active_quest = self.active_quests[quest.entry]
            if not active_quest.can_complete_quest():
                self.send_cant_take_quest_response(QuestFailedReasons.QUEST_FAILED_MISSING_ITEMS)
                return

        quest_title_bytes = PacketWriter.string_to_bytes(quest.Title)
        display_dialog_text = quest.OfferRewardText

        dialog_text_bytes = PacketWriter.string_to_bytes(display_dialog_text)
        data = pack(
            f'<QI{len(quest_title_bytes)}s{len(dialog_text_bytes)}sI',
            quest_giver_guid,
            quest.entry,
            quest_title_bytes,
            dialog_text_bytes,
            1 if enable_next else 0  # enable_next
        )

        # Emote count, always 4.
        data += pack('<I', 4)
        for i in range(1, 5):
            offer_emote = eval(f'quest.OfferRewardEmote{i}')
            offer_emote_delay = eval(f'quest.OfferRewardEmoteDelay{i}')
            data += pack('<2I', offer_emote, offer_emote_delay)

        if QuestHelpers.has_pick_reward(quest):
            # Reward choices
            rew_choice_item_list = list(filter((0).__ne__, QuestHelpers.generate_rew_choice_item_list(quest)))
            rew_choice_count_list = list(filter((0).__ne__, QuestHelpers.generate_rew_choice_count_list(quest)))
            data += pack('<I', len(rew_choice_item_list))
            for index, item in enumerate(rew_choice_item_list):
                data += self._gen_item_struct(item, rew_choice_count_list[index])
        else:
            data += pack('<I', 0)

        #  Apart from available rewards to pick from, sometimes there are rewards you will also get, no matter what.
        if QuestHelpers.has_item_reward(quest):
            # Required items
            rew_item_list = list(filter((0).__ne__, QuestHelpers.generate_rew_item_list(quest)))
            rew_count_list = list(filter((0).__ne__, QuestHelpers.generate_rew_count_list(quest)))
            data += pack('<I', len(rew_item_list))
            for index, item in enumerate(rew_item_list):
                data += self._gen_item_struct(item, rew_count_list[index])
        else:
            data += pack('<I', 0)

        # Reward Money, 0.5.3 does not handle spells as a possible reward. CGPlayer_C::OnQuestGiverChooseReward.
        data += pack('<I', quest.RewOrReqMoney if quest.RewOrReqMoney >= 0 else -quest.RewOrReqMoney)
        self.player_mgr.enqueue_packet(PacketWriter.get_packet(OpCode.SMSG_QUESTGIVER_OFFER_REWARD, data))
예제 #15
0
    def is_instant_complete_quest(self):
        for reqSource in QuestHelpers.generate_req_source_list(self.quest):
            if reqSource != 0:
                return False

        for reqItem in QuestHelpers.generate_req_item_list(self.quest):
            if reqItem != 0:
                return False

        for reqCreatureGo in QuestHelpers.generate_req_creature_or_go_list(
                self.quest):
            if reqCreatureGo != 0:
                return False

        for reqSpellCast in QuestHelpers.generate_req_spell_cast_list(
                self.quest):
            if reqSpellCast != 0:
                return False

        return True
예제 #16
0
    def can_complete_quest(self):
        if self.is_instant_complete_quest():
            return True

        # Check for required kills / gameobjects.
        required_creature_go = QuestHelpers.generate_req_creature_or_go_count_list(
            self.quest)
        for i in range(0, 4):
            current_value = eval(f'self.db_state.mobcount{i + 1}')
            if current_value != required_creature_go[i]:
                return False

        # Check for required items.
        required_items = QuestHelpers.generate_req_item_count_list(self.quest)
        for i in range(0, 4):
            current_value = eval(f'self.db_state.itemcount{i + 1}')
            if current_value != required_items[i]:
                return False

        # TODO: Check ReqMoney
        return True
예제 #17
0
    def send_quest_giver_quest_details(self, quest_template, quest_giver_guid, activate_accept):
        # Quest information
        quest_title = PacketWriter.string_to_bytes(quest_template.Title)
        quest_details = PacketWriter.string_to_bytes(quest_template.Details)
        quest_objectives = PacketWriter.string_to_bytes(quest_template.Objectives)
        data = pack(
            f'<QI{len(quest_title)}s{len(quest_details)}s{len(quest_objectives)}sI',
            quest_giver_guid,
            quest_template.entry,
            quest_title,
            quest_details,
            quest_objectives,
            1 if activate_accept else 0
        )

        # Reward choices
        rew_choice_item_list = list(filter((0).__ne__, QuestHelpers.generate_rew_choice_item_list(quest_template)))
        rew_choice_count_list = list(filter((0).__ne__, QuestHelpers.generate_rew_choice_count_list(quest_template)))
        data += pack('<I', len(rew_choice_item_list))
        for index, item in enumerate(rew_choice_item_list):
            data += self._gen_item_struct(item, rew_choice_count_list[index])

        # Reward items
        rew_item_list = list(filter((0).__ne__, QuestHelpers.generate_rew_item_list(quest_template)))
        rew_count_list = list(filter((0).__ne__, QuestHelpers.generate_rew_count_list(quest_template)))
        data += pack('<I', len(rew_item_list))
        for index, item in enumerate(rew_item_list):
            data += self._gen_item_struct(item, rew_count_list[index])

        # Reward money
        data += pack('<I', quest_template.RewOrReqMoney)

        # Emotes
        data += pack('<I', 4)
        for index in range(1, 5):
            detail_emote = int(eval(f'quest_template.DetailsEmote{index}'))
            detail_emote_delay = eval(f'quest_template.DetailsEmoteDelay{index}')
            data += pack('<2I', detail_emote, detail_emote_delay)

        self.player_mgr.enqueue_packet(PacketWriter.get_packet(OpCode.SMSG_QUESTGIVER_QUEST_DETAILS, data))
예제 #18
0
    def send_quest_giver_request_items(self, quest, quest_giver_id, close_on_cancel):
        # We can always call to RequestItems, but this packet only goes out if there are actually
        # items.  Otherwise, we'll skip straight to the OfferReward.
        quest_title = quest.Title
        request_items_text = quest.RequestItemsText
        is_completable = quest.entry in self.active_quests and self.active_quests[quest.entry].is_quest_complete(quest_giver_id)

        if not request_items_text or (not QuestHelpers.has_item_requirements(quest) and is_completable):
            self.send_quest_giver_offer_reward(quest, quest_giver_id, enable_next=True)
            return

        quest_title_bytes = PacketWriter.string_to_bytes(quest_title)
        request_items_text_bytes = PacketWriter.string_to_bytes(request_items_text)
        data = pack(
            f'<QI{len(quest_title_bytes)}s{len(request_items_text_bytes)}s3I',
            quest_giver_id,
            quest.entry,
            quest_title_bytes,
            request_items_text_bytes,
            0,  # Emote delay
            quest.CompleteEmote if is_completable else quest.IncompleteEmote,
            close_on_cancel,  # Close Window after cancel
        )

        req_items = list(filter((0).__ne__, QuestHelpers.generate_req_item_list(quest)))
        req_items_count_list = list(filter((0).__ne__, QuestHelpers.generate_req_item_count_list(quest)))
        data += pack('<I', len(req_items))
        for index in range(len(req_items)):
            data += self._gen_item_struct(req_items[index], req_items_count_list[index])

        data += pack(
            '<3I',
            0x02,  # MaskMatch
            0x03 if is_completable else 0x00,  # Completable = Player has items?
            0x04,  # HasFaction
        )

        packet = PacketWriter.get_packet(OpCode.SMSG_QUESTGIVER_REQUEST_ITEMS, data)
        self.player_mgr.enqueue_packet(packet)
예제 #19
0
    def check_quest_requirements(self, quest_template):
        # Is the player character the required race.
        race_is_required = quest_template.RequiredRaces > 0
        if race_is_required and not (quest_template.RequiredRaces
                                     & self.player_mgr.race_mask):
            return False

        # Is the character the required class.
        class_is_required = quest_template.RequiredClasses > 0
        if class_is_required and not (quest_template.RequiredClasses
                                      & self.player_mgr.class_mask):
            return False

        # Does the character have the required source items.
        # TODO: Should we have this check?
        source_item_required = list(
            filter((0).__ne__,
                   QuestHelpers.generate_req_source_list(quest_template)))
        source_item_count_list = list(
            filter(
                (0).__ne__,
                QuestHelpers.generate_req_source_count_list(quest_template)))
        for index, item in enumerate(source_item_required):
            if self.player_mgr.inventory.get_item_count(
                    item) < source_item_count_list[index]:
                return False

        # Has the character already started the next quest in the chain.
        if quest_template.NextQuestInChain > 0 and quest_template.NextQuestInChain in self.completed_quests:
            return False

        # Does the character have the previous quest.
        if quest_template.PrevQuestId > 0 and quest_template.PrevQuestId not in self.completed_quests:
            return False

        # TODO: Does the character have the required skill

        return True
예제 #20
0
    def get_progress(self):
        total_count = 0
        req_creature_or_go = QuestHelpers.generate_req_creature_or_go_list(
            self.quest)
        req_creature_or_go_count = QuestHelpers.generate_req_creature_or_go_count_list(
            self.quest)
        for index, creature_or_go in enumerate(req_creature_or_go):
            if req_creature_or_go[index] > 0:
                current_count = eval(f'self.db_state.mobcount{index + 1}')
                required = req_creature_or_go_count[index]
                # Consider how many bits the previous creature required.
                offset = index * req_creature_or_go_count[
                    index - 1] if index > 0 else 0

                for i in range(0, required):
                    if i < current_count:  # Turn on actual kills
                        total_count += (1 & 1) << (1 * i) + offset
                    else:  # Fill remaining 0s (Missing kills)
                        total_count += 0 << (1 * i) + offset

                # Debug, enable this to take a look on whats happening at bit level.
                # Logger.debug(f'{bin(mob_kills)[2:].zfill(32)}')

        return total_count
예제 #21
0
    def need_item_from_go(self, quest_giver, go_loot_template):
        # Quest is complete.
        if self.is_quest_complete(quest_giver):
            return False

        needed_items = list(filter((0).__ne__, QuestHelpers.generate_req_item_list(self.quest)))

        # Not required items for this quest.
        if len(needed_items) == 0:
            return False

        # Check if any needed items match the provided go_loot_template.
        for entry in go_loot_template:
            if entry.item in needed_items:
                return True

        return False
예제 #22
0
    def _update_db_item_count(self, index, value, required_count=None):
        if not required_count:
            required_count = QuestHelpers.generate_req_item_count_list(
                self.quest)[index]

        # Be sure we clamp between 0 and required.
        current_db_count = self._get_db_item_count(index)
        if current_db_count + value > required_count:
            value = required_count
        if current_db_count + value < 0:
            value = 0

        if index == 0:
            self.db_state.itemcount1 += value
        elif index == 1:
            self.db_state.itemcount2 += value
        elif index == 2:
            self.db_state.itemcount3 += value
        elif index == 3:
            self.db_state.itemcount4 += value
        self.save(is_new=False)
예제 #23
0
    def handle_complete_quest(self, quest_id, quest_giver_guid):
        quest = WorldDatabaseManager.QuestTemplateHolder.quest_get_by_entry(quest_id)

        # Validate if quest exists.
        if not quest:
            return

        # If not an instant complete quest, validate it.
        if not QuestHelpers.is_instant_complete_quest(quest):
            if quest_id not in self.active_quests and quest_id not in self.completed_quests:
                self.send_quest_giver_quest_details(quest, quest_giver_guid, activate_accept=True)
                return

            active_quest = self.active_quests[quest_id]
            if not active_quest.is_quest_complete(quest_giver_guid):
                self.send_quest_giver_request_items(quest, quest_giver_guid, close_on_cancel=False)
                return

        if quest_id in self.active_quests and self.active_quests[quest_id].requires_items():
            self.send_quest_giver_request_items(quest, quest_giver_guid, close_on_cancel=False)
        else:
            self.send_quest_giver_offer_reward(quest, quest_giver_guid, True)
예제 #24
0
    def send_quest_giver_quest_details(self, quest_template, quest_giver_guid,
                                       activate_accept):
        # Quest information
        quest_title = PacketWriter.string_to_bytes(quest_template.Title)
        quest_details = PacketWriter.string_to_bytes(quest_template.Details)
        quest_objectives = PacketWriter.string_to_bytes(
            quest_template.Objectives)
        data = pack(
            f'<QI{len(quest_title)}s{len(quest_details)}s{len(quest_objectives)}sI',
            quest_giver_guid, quest_template.entry, quest_title, quest_details,
            quest_objectives, 1 if activate_accept else 0)

        # Reward choices
        rew_choice_item_list = list(
            filter((0).__ne__,
                   QuestHelpers.generate_rew_choice_item_list(quest_template)))
        rew_choice_count_list = list(
            filter(
                (0).__ne__,
                QuestHelpers.generate_rew_choice_count_list(quest_template)))
        data += pack('<I', len(rew_choice_item_list))
        for index, item in enumerate(rew_choice_item_list):
            data += self._gen_item_struct(item, rew_choice_count_list[index])

        # Reward items
        rew_item_list = list(
            filter((0).__ne__,
                   QuestHelpers.generate_rew_item_list(quest_template)))
        rew_count_list = list(
            filter((0).__ne__,
                   QuestHelpers.generate_rew_count_list(quest_template)))
        data += pack('<I', len(rew_item_list))
        for index, item in enumerate(rew_item_list):
            data += self._gen_item_struct(item, rew_count_list[index])

        # Reward money
        data += pack('<I', quest_template.RewOrReqMoney)

        # Required items
        req_item_list = list(
            filter((0).__ne__,
                   QuestHelpers.generate_req_item_list(quest_template)))
        req_count_list = list(
            filter((0).__ne__,
                   QuestHelpers.generate_req_item_count_list(quest_template)))
        data += pack('<I', len(req_item_list))
        for index, item in enumerate(req_item_list):
            data += self._gen_item_struct(item, req_count_list[index])

        # Required kill / go count
        req_creature_or_go_list = list(
            filter(
                (0).__ne__,
                QuestHelpers.generate_req_creature_or_go_list(quest_template)))
        req_creature_or_go_count_list = list(
            filter((0).__ne__,
                   QuestHelpers.generate_req_creature_or_go_count_list(
                       quest_template)))
        data += pack('<I', len(req_creature_or_go_list))
        for index, creature_or_go in enumerate(req_creature_or_go_list):
            data += pack(
                '<2I', creature_or_go if creature_or_go >= 0 else
                (creature_or_go * -1) | 0x80000000,
                req_creature_or_go_count_list[index])

        self.player_mgr.enqueue_packet(
            PacketWriter.get_packet(OpCode.SMSG_QUESTGIVER_QUEST_DETAILS,
                                    data))
예제 #25
0
 def requires_item(self, item_entry):
     req_item = QuestHelpers.generate_req_item_list(self.quest)
     req_src_item = QuestHelpers.generate_req_source_list(self.quest)
     return item_entry in req_item or item_entry in req_src_item
예제 #26
0
    def send_quest_query_response(self, quest):
        data = pack(f'<3Ii4I', quest.entry, quest.Method, quest.QuestLevel,
                    quest.ZoneOrSort, quest.Type, quest.NextQuestInChain,
                    quest.RewOrReqMoney, quest.SrcItemId)

        # Rewards given no matter what.
        rew_item_list = QuestHelpers.generate_rew_item_list(quest)
        rew_item_count_list = QuestHelpers.generate_rew_count_list(quest)
        for index, item in enumerate(rew_item_list):
            data += pack('<2I', item, rew_item_count_list[index])

        # Reward choices.
        rew_choice_item_list = QuestHelpers.generate_rew_choice_item_list(
            quest)
        rew_choice_count_list = QuestHelpers.generate_rew_choice_count_list(
            quest)
        for index, item in enumerate(rew_choice_item_list):
            data += pack('<2I', item, rew_choice_count_list[index])

        title_bytes = PacketWriter.string_to_bytes(quest.Title)
        details_bytes = PacketWriter.string_to_bytes(quest.Details)
        objectives_bytes = PacketWriter.string_to_bytes(quest.Objectives)
        end_bytes = PacketWriter.string_to_bytes(quest.EndText)
        data += pack(
            f'<I2fI{len(title_bytes)}s{len(details_bytes)}s{len(objectives_bytes)}s{len(end_bytes)}s',
            quest.PointMapId,
            quest.PointX,
            quest.PointY,
            quest.PointOpt,
            title_bytes,
            details_bytes,
            objectives_bytes,
            end_bytes,
        )

        # Required kills / Required items count.
        req_creatures_or_gos = QuestHelpers.generate_req_creature_or_go_list(
            quest)
        req_creatures_or_gos_count_list = QuestHelpers.generate_req_creature_or_go_count_list(
            quest)
        req_items = QuestHelpers.generate_req_item_list(quest)
        req_items_count_list = QuestHelpers.generate_req_item_count_list(quest)
        for index, creature_or_go in enumerate(req_creatures_or_gos):
            data += pack(
                '<4IB',
                creature_or_go if creature_or_go >= 0 else
                (creature_or_go * -1) | 0x80000000,
                req_creatures_or_gos_count_list[index],
                req_items[index],
                req_items_count_list[index],
                0x0  # Unknown, if missing, multiple objective quests will not display properly.
            )

        # Objective texts.
        req_objective_text_list = QuestHelpers.generate_objective_text_list(
            quest)
        for index, objective_text in enumerate(req_objective_text_list):
            req_objective_text_bytes = PacketWriter.string_to_bytes(
                req_objective_text_list[index])
            data += pack(f'{len(req_objective_text_bytes)}s',
                         req_objective_text_bytes)

        self.player_mgr.enqueue_packet(
            PacketWriter.get_packet(OpCode.SMSG_QUEST_QUERY_RESPONSE, data))
예제 #27
0
    def send_quest_giver_offer_reward(self,
                                      active_quest,
                                      quest_giver_guid,
                                      enable_next=True):
        # CGPlayer_C::OnQuestGiverChooseReward
        quest = active_quest.quest
        quest_title_bytes = PacketWriter.string_to_bytes(quest.Title)
        display_dialog_text = quest.OfferRewardText

        dialog_text_bytes = PacketWriter.string_to_bytes(display_dialog_text)
        data = pack(
            f'<QI{len(quest_title_bytes)}s{len(dialog_text_bytes)}sI',
            quest_giver_guid,
            active_quest.quest.entry,
            quest_title_bytes,
            dialog_text_bytes,
            1 if enable_next else 0  # enable_next
        )

        # Emote count, always 4.
        data += pack('<I', 4)
        for i in range(1, 5):
            emote = eval(f'active_quest.quest.OfferRewardEmote{i}')
            delay = eval(f'active_quest.quest.OfferRewardEmoteDelay{i}')
            data += pack('<2I', emote, delay)

        if active_quest.has_pick_reward():
            # Reward choices
            rew_choice_item_list = list(
                filter((0).__ne__,
                       QuestHelpers.generate_rew_choice_item_list(quest)))
            rew_choice_count_list = list(
                filter((0).__ne__,
                       QuestHelpers.generate_rew_choice_count_list(quest)))
            data += pack('<I', len(rew_choice_item_list))
            for index, item in enumerate(rew_choice_item_list):
                data += self._gen_item_struct(item,
                                              rew_choice_count_list[index])
        else:
            data += pack('<I', 0)

        #  Apart from available rewards to pick from, sometimes there are rewards you will also get, no matter what.
        if active_quest.has_item_reward():
            # Required items
            rew_item_list = list(
                filter((0).__ne__, QuestHelpers.generate_rew_item_list(quest)))
            rew_count_list = list(
                filter((0).__ne__,
                       QuestHelpers.generate_rew_count_list(quest)))
            data += pack('<I', len(rew_item_list))
            for index, item in enumerate(rew_item_list):
                data += self._gen_item_struct(item, rew_count_list[index])
        else:
            data += pack('<I', 0)

        # Reward
        data += pack(
            '<3I',
            quest.RewOrReqMoney
            if quest.RewOrReqMoney >= 0 else -quest.RewOrReqMoney,
            quest.RewSpell,
            quest.RewSpellCast,
        )

        self.player_mgr.enqueue_packet(
            PacketWriter.get_packet(OpCode.SMSG_QUESTGIVER_OFFER_REWARD, data))
예제 #28
0
    def handle_choose_reward(self, quest_giver_guid, quest_id, item_choice):
        if quest_id not in self.active_quests:
            return

        active_quest = self.active_quests[quest_id]
        if not active_quest.is_quest_complete(quest_giver_guid):
            return

        # Remove required items from the player inventory.
        req_item_list = QuestHelpers.generate_req_item_list(active_quest.quest)
        req_item_count = QuestHelpers.generate_req_item_count_list(
            active_quest.quest)
        for index, req_item in enumerate(req_item_list):
            if req_item != 0:
                self.player_mgr.inventory.remove_items(req_item,
                                                       req_item_count[index])

        # Add the chosen item, if any.
        rew_item_choice_list = QuestHelpers.generate_rew_choice_item_list(
            active_quest.quest)
        if item_choice < len(rew_item_choice_list
                             ) and rew_item_choice_list[item_choice] > 0:
            self.player_mgr.inventory.add_item(
                entry=rew_item_choice_list[item_choice], show_item_get=False)

        given_xp = active_quest.reward_xp()
        given_gold = active_quest.reward_gold()

        # Remove from log and mark as rewarded.
        self.remove_from_quest_log(quest_id)
        self.completed_quests.add(quest_id)

        # Update db quest status.
        active_quest.update_quest_status(rewarded=True)

        data = pack(
            '<4I',
            quest_id,
            3,  # Investigate
            given_xp,
            given_gold)

        # Give player reward items, if any. Client will announce them.
        rew_item_list = list(
            filter((0).__ne__,
                   QuestHelpers.generate_rew_item_list(active_quest.quest)))
        rew_item_count_list = list(
            filter((0).__ne__,
                   QuestHelpers.generate_rew_count_list(active_quest.quest)))
        data += pack('<I', len(rew_item_list))
        for index, rew_item in enumerate(rew_item_list):
            data += pack('<2I', rew_item_list[index],
                         rew_item_count_list[index])
            self.player_mgr.inventory.add_item(entry=rew_item_list[index],
                                               show_item_get=False)

        # TODO: Handle RewSpell and RewSpellCast upon completion.
        self.player_mgr.enqueue_packet(
            PacketWriter.get_packet(OpCode.SMSG_QUESTGIVER_QUEST_COMPLETE,
                                    data))

        # Update surrounding, NextQuestInChain was not working properly.
        self.update_surrounding_quest_status()
예제 #29
0
 def requires_items(self):
     req_items = list(filter((0).__ne__, QuestHelpers.generate_req_item_list(self.quest)))
     return any(req_items)
예제 #30
0
    def handle_choose_reward(self, quest_giver_guid, quest_id, item_choice):
        quest = WorldDatabaseManager.QuestTemplateHolder.quest_get_by_entry(quest_id)
        if not quest:
            return

        # If this is an instant complete quest, load or create its db state.
        if QuestHelpers.is_instant_complete_quest(quest) and quest_id not in self.active_quests:
            db_quest = RealmDatabaseManager.character_get_quest_by_id(self.player_mgr.guid, quest_id)
            if db_quest:
                self.active_quests[quest_id] = ActiveQuest(db_quest, self.player_mgr, quest)
            else:
                self.active_quests[quest_id] = self._create_db_quest_status(quest)
                self.active_quests[quest_id].save(is_new=True)

        active_quest = self.active_quests[quest_id]
        if not active_quest.is_quest_complete(quest_giver_guid):
            return

        # Remove required items from the player inventory.
        req_item_list = QuestHelpers.generate_req_item_list(quest)
        req_item_count = QuestHelpers.generate_req_item_count_list(quest)
        for index, req_item in enumerate(req_item_list):
            if req_item != 0:
                self.player_mgr.inventory.remove_items(req_item, req_item_count[index])

        # Add the chosen item, if any.
        rew_item_choice_list = QuestHelpers.generate_rew_choice_item_list(quest)
        if item_choice < len(rew_item_choice_list) and rew_item_choice_list[item_choice] > 0:
            self.player_mgr.inventory.add_item(entry=rew_item_choice_list[item_choice], show_item_get=False)

        given_xp = active_quest.reward_xp()
        given_gold = active_quest.reward_gold()
        active_quest.reward_reputation()

        # Update db quest status to rewarded.
        active_quest.update_quest_status(rewarded=True)

        # Repeatable quests are not persisted.
        if not QuestHelpers.is_quest_repeatable(active_quest.quest):
            # Remove from log and mark as rewarded.
            self.remove_from_quest_log(quest_id)
            self.completed_quests.add(quest_id)

        # Remove from active quests if needed.
        if quest.entry in self.active_quests:
            del self.active_quests[quest.entry]

        data = pack(
            '<4I',
            quest_id,
            3,  # Investigate
            int(given_xp * config.Server.Settings.xp_rate),
            given_gold
        )

        # Give player reward items, if any. Client will announce them.
        rew_item_list = list(filter((0).__ne__, QuestHelpers.generate_rew_item_list(active_quest.quest)))
        rew_item_count_list = list(filter((0).__ne__, QuestHelpers.generate_rew_count_list(active_quest.quest)))
        data += pack('<I', len(rew_item_list))
        for index, rew_item in enumerate(rew_item_list):
            data += pack('<2I', rew_item_list[index], rew_item_count_list[index])
            self.player_mgr.inventory.add_item(entry=rew_item_list[index], count=rew_item_count_list[index],
                                               show_item_get=False)

        self.player_mgr.enqueue_packet(PacketWriter.get_packet(OpCode.SMSG_QUESTGIVER_QUEST_COMPLETE, data))

        # Cast spell if needed.
        if active_quest.quest.RewSpellCast:
            self.cast_reward_spell(quest_giver_guid, active_quest)

        # Update surrounding, NextQuestInChain was not working properly.
        self.update_surrounding_quest_status()