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
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
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)
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
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
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
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)
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])
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)
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)
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
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}
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)
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))
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
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
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))
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)
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
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
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
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)
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)
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))
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
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))
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))
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()
def requires_items(self): req_items = list(filter((0).__ne__, QuestHelpers.generate_req_item_list(self.quest))) return any(req_items)
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()