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 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 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 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 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 _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 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_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 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 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_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 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()