from toontown.toon.DistributedNPCSpecialQuestGiverAI import DistributedNPCSpecialQuestGiverAI from toontown.building import FADoorCodes from otp.ai.MagicWordGlobal import * from toontown.hood import ZoneUtil from toontown.quest import Quests from toontown.uberdog import TopToonsGlobals from toontown.toonbase import ToontownGlobals QuestIdIndex = 0 QuestFromNpcIdIndex = 1 QuestToNpcIdIndex = 2 QuestRewardIdIndex = 3 QuestProgressIndex = 4 class QuestManagerAI: notify = directNotify.newCategory('QuestManagerAI') def __init__(self, air): self.air = air def requestInteract(self, avId, npc): # Get the avatar. av = self.air.doId2do.get(avId) if not av: return avQuestPocketSize = av.getQuestCarryLimit() avQuests = av.getQuests() needTrackTask = False fakeTier = 0 avTrackProgress = av.getTrackProgress() if avTrackProgress[0] == -1: avQuestTier = av.getRewardTier() if avQuestTier < Quests.DG_TIER and avQuestTier > Quests.DD_TIER: fakeTier = Quests.DD_TIER needTrackTask = True elif avQuestTier < Quests.BR_TIER and avQuestTier > Quests.MM_TIER: fakeTier = Quests.MM_TIER needTrackTask = True elif avQuestTier < Quests.DL_TIER and avQuestTier > Quests.BR_TIER: fakeTier = Quests.BR_TIER needTrackTask = True # Iterate through their quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i:i + 5] questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc questClass = Quests.getQuest(questId) if questClass: completeStatus = questClass.getCompletionStatus(av, questDesc, npc) else: continue # If the quest is a DeliverGagQuest, add the gags. if isinstance(questClass, Quests.DeliverGagQuest): # Check if it's the required NPC. if npc.npcId == toNpcId: # Add progress. questList = [] progress = questClass.removeGags(av) for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i:i + 5] if questDesc[QuestIdIndex] == questId: questDesc[QuestProgressIndex] += progress if questDesc[QuestProgressIndex] >= questClass.getNumGags(): completeStatus = Quests.COMPLETE questList.append(questDesc) av.b_setQuests(questList) if completeStatus != Quests.COMPLETE: continue # If they've completed a quest. if completeStatus == Quests.COMPLETE: # ToonUp the toon to max health. messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_TASKS, 1]) av.toonUp(av.maxHp) # If it's a TrackChoiceQuest then present their track choices. if isinstance(questClass, Quests.TrackChoiceQuest): npc.presentTrackChoice(avId, questId, questClass.getChoices()) break # If there is another part to this quest then give them that. if Quests.getNextQuest(questId, npc, av)[0] != Quests.NA: self.nextQuest(av, npc, questId) if avId in self.air.tutorialManager.avId2fsm: self.air.tutorialManager.avId2fsm[avId].demand('Tunnel') break else: # The toon has completed this quest. Give them a reward! npc.completeQuest(avId, questId, rewardId) self.completeQuest(av, questId) av.addStat(ToontownGlobals.STAT_TASKS) break else: # They haven't completed any quests so we have to give them choices. # If they've got a full pouch then reject them. if (len(avQuests) == avQuestPocketSize*5): npc.rejectAvatar(avId) return elif isinstance(npc, DistributedNPCSpecialQuestGiverAI): # Don't display choices. Force a choice. self.tutorialQuestChoice(avId, npc) return else: #Present quest choices. if needTrackTask: choices = self.npcGiveTrackChoice(av, fakeTier) else: choices = self.avatarQuestChoice(av, npc) if choices != []: npc.presentQuestChoice(avId, choices) else: npc.rejectAvatar(avId) def npcGiveTrackChoice(self, av, tier): trackQuest = Quests.chooseTrackChoiceQuest(tier, av) return [(trackQuest, 400, Quests.ToonHQ)] def avatarQuestChoice(self, av, npc): # Get the best quests for an avatar/npc. return Quests.chooseBestQuests(av.getRewardTier(), npc, av) def avatarChoseQuest(self, avId, npc, questId, rewardId, toNpcId): # Get the avatar. av = self.air.doId2do.get(avId) if not av: return # Get the npcIds fromNpcId = npc.npcId if npc else 0 if toNpcId == 0: toNpcId = Quests.getQuestToNpcId(questId) # Add the quest to the avatars list. transformedRewardId = Quests.transformReward(rewardId, av) av.addQuest([questId, fromNpcId, toNpcId, rewardId, 0], transformedRewardId) if not npc: return # Remove the tasks for timeout. taskMgr.remove(npc.uniqueName('clearMovie')) # Assign the quest. npc.assignQuest(avId, questId, rewardId, toNpcId) def avatarChoseTrack(self, avId, npc, pendingTrackQuest, trackId): # Get the avatar. av = self.air.doId2do.get(avId) if not av: return # Remove the tasks for timeout. taskMgr.remove(npc.uniqueName('clearMovie')) # Show the completion movie and remove the task. npc.completeQuest(avId, pendingTrackQuest, Quests.getRewardIdFromTrackId(trackId)) self.completeQuest(av, pendingTrackQuest) # Set their track their working on. av.b_setTrackProgress(trackId, 0) def avatarCancelled(self, npcId): # Get the NPC. npc = self.air.doId2do.get(npcId) if not npc: return # Remove the task for timeout. taskMgr.remove(npc.uniqueName('clearMovie')) def nextQuest(self, av, npc, questId): # Get the next QuestId and toNpcId. nextQuestId, toNpcId = Quests.getNextQuest(questId, npc, av) # Get the avatars current quests. avQuests = av.getQuests() questList = [] # Iterate through their current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i:i + 5] if questDesc[QuestIdIndex] == questId: questDesc[QuestIdIndex] = nextQuestId questDesc[QuestToNpcIdIndex] = toNpcId questDesc[QuestProgressIndex] = 0 questList.append(questDesc) # Show the quest movie and set their quests. npc.incompleteQuest(av.doId, nextQuestId, Quests.QUEST, toNpcId) av.b_setQuests(questList) def completeQuest(self, av, completeQuestId): #Get the avatars current quests. avQuests = av.getQuests() # Iterate through their current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i:i + 5] questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc questClass = Quests.getQuest(questId) if questId == completeQuestId: av.removeQuest(questId) self.giveReward(av, questId, rewardId) self.avatarConsiderProgressTier(av) break def giveReward(self, av, questId, rewardId): # Give the reward. rewardClass = Quests.getReward(rewardId) if rewardClass is None: self.notify.warning('rewardClass was None for rewardId: %s.' % rewardId) else: rewardClass.sendRewardAI(av) # Add the rewardId to the avatars rewardHistory. rewardTier, rewardHistory = av.getRewardHistory() transformedRewardId = Quests.transformReward(rewardId, av) if transformedRewardId != rewardId: rewardHistory.append(rewardId) av.b_setRewardHistory(rewardTier, rewardHistory) def avatarConsiderProgressTier(self, av): # Get the avatars current tier. currentTier = av.getRewardTier() # Check if they have all required rewards. if Quests.avatarHasAllRequiredRewards(av, currentTier): if currentTier != Quests.ELDER_TIER: currentTier += 1 av.b_setRewardHistory(currentTier, []) def tutorialQuestChoice(self, avId, npc): # Get the avatar. av = self.air.doId2do.get(avId) if not av: return # Get the possible quest choices and force the player to choose it. choices = self.avatarQuestChoice(av, npc) quest = choices[0] self.avatarChoseQuest(avId, npc, quest[0], quest[1], 0) # Are we in the tutorial speaking to Tutorial Tom? if avId in self.air.tutorialManager.avId2fsm: if av.getRewardHistory()[0] == 0: self.air.tutorialManager.avId2fsm[avId].demand('Battle') def toonRodeTrolleyFirstTime(self, av): # Toon played a minigame. self.toonPlayedMinigame(av, []) def toonPlayedMinigame(self, av, toons): # Get the avatars current quests. avQuests = av.getQuests() questList = [] # Iterate through their current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.TrolleyQuest): questDesc[QuestProgressIndex] = 1 questList.append(questDesc) av.b_setQuests(questList) def toonMadeFriend(self, avId): # Get the avatar. av = self.air.doId2do.get(avId) if not av: return # Get the avatars current quests. avQuests = av.getQuests() questList = [] # Iterate through their current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.FriendQuest) and questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE: questDesc[QuestProgressIndex] += 1 questList.append(questDesc) av.b_setQuests(questList) def toonCalledClarabelle(self, av): avQuests = av.getQuests() questList = [] for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.PhoneQuest) and questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE: questDesc[QuestProgressIndex] += 1 questList.append(questDesc) av.b_setQuests(questList) def toonMadeNPCFriend(self, av, count, method): avQuests = av.getQuests() questList = [] for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.RescueQuest) and questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE and questClass.isMethodMatch(method): questDesc[QuestProgressIndex] += count questList.append(questDesc) av.b_setQuests(questList) def toonUsedPhone(self, avId): # Get the avatar. av = self.air.doId2do.get(avId) if not av: return # Get the avatars current quests. avQuests = av.getQuests() questList = [] # Iterate through their current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.PhoneQuest): questDesc[QuestProgressIndex] += 1 questList.append(questDesc) av.b_setQuests(questList) def toonCaughtFishingItem(self, av): # Get the avatars current quests. avQuests = av.getQuests() fishingItem = -1 questList = [] # Iterate through their current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if fishingItem != -1: questList.append(questDesc) continue if isinstance(questClass, Quests.RecoverItemQuest): if not hasattr(questClass, 'getItem'): questList.append(questDesc) continue if questClass.getHolder() == Quests.AnyFish: if not questClass.getCompletionStatus(av, questDesc) == Quests.COMPLETE: baseChance = questClass.getPercentChance() amountRemaining = questClass.getNumItems() - questDesc[QuestProgressIndex] chance = Quests.calcRecoverChance(amountRemaining, baseChance) if chance >= baseChance: questDesc[QuestProgressIndex] += 1 fishingItem = questClass.getItem() questList.append(questDesc) av.b_setQuests(questList) return fishingItem def hasTailorClothingTicket(self, av, npc): # Get the avatars current quests. avQuests = av.getQuests() # Iterate through their current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.DeliverItemQuest): if questClass.getCompletionStatus(av, questDesc, npc) == Quests.COMPLETE: # They have a clothing ticket. return 1 return 0 def removeClothingTicket(self, av, npc): # Get the avatars current quests. avQuests = av.getQuests() # Iterate through their current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.DeliverItemQuest): if questClass.getCompletionStatus(av, questDesc, npc) == Quests.COMPLETE: av.removeQuest(questDesc[QuestIdIndex]) break def recoverItems(self, av, suitsKilled, taskZoneId): # Get the avatars current quests. avQuests = av.getQuests() questList = [] recoveredItems = [] unrecoveredItems = [] taskZoneId = ZoneUtil.getBranchZone(taskZoneId) # Iterate through the avatars current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) # Check if the Quest isnt already complete if questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE: # Check if we are dealing with a RecoverItemQuest if isinstance(questClass, Quests.RecoverItemQuest): # Iterate through all the Cogs that were killed in the battle for suit in suitsKilled: # Because the RecoveItemQuest class doesn't have a # function to see if a Cog counts. We need to manually # check if the Cog is valid for the Quest if (questClass.getHolder() == Quests.Any) or \ (questClass.getHolderType() == 'type' and \ questClass.getHolder() == suit['type']) or \ (questClass.getHolderType() == 'track' and \ questClass.getHolder() == suit['track']) or \ (questClass.getHolderType() == 'level' and \ questClass.getHolder() <= suit['level']): # It looks like the Cog was valid. Lets see if they # found what they were looking for. baseChance = questClass.getPercentChance() amountRemaining = questClass.getNumItems() - questDesc[QuestProgressIndex] chance = Quests.calcRecoverChance(amountRemaining, baseChance) # They found it! Give them their reward! if chance >= baseChance: questDesc[QuestProgressIndex] += 1 recoveredItems.append(questClass.getItem()) # Better luck next time :( else: unrecoveredItems.append(questClass.getItem()) questList.append(questDesc) av.b_setQuests(questList) return (recoveredItems, unrecoveredItems) def toonKilledBuilding(self, av, type, difficulty, floors, zoneId, cogdo): # Get the avatars current quests. messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_BLDG, 1]) avQuests = av.getQuests() questList = [] zoneId = ZoneUtil.getBranchZone(zoneId) # Iterate through the avatars current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.BuildingQuest) and questClass.getCompletionStatus(av, questDesc) == Quests.INCOMPLETE: if questClass.isLocationMatch(zoneId) and questClass.doesBuildingTypeCount(type): if questClass.isCogdo() == cogdo: if floors >= questClass.getNumFloors(): questDesc[QuestProgressIndex] += 1 questList.append(questDesc) av.b_setQuests(questList) def toonDefeatedFactory(self, av, factoryId): # Get the avatars current quests. avQuests = av.getQuests() questList = [] # Iterate through the avatars current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.FactoryQuest): if questClass.doesFactoryCount(av, factoryId): questDesc[QuestProgressIndex] += 1 questList.append(questDesc) av.b_setQuests(questList) def toonDefeatedMint(self, av, mintId): # Get the avatars current quests. avQuests = av.getQuests() questList = [] # Iterate through the avatars current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) if isinstance(questClass, Quests.MintQuest): if questClass.doesMintCount(av, mintId): questDesc[QuestProgressIndex] += 1 questList.append(questDesc) av.b_setQuests(questList) def toonDefeatedStage(self, av, stageId): pass def toonKilledCogs(self, av, suitsKilled, zoneId): # Get the avatar's current quests. messenger.send('topToonsManager-event', [av.doId, TopToonsGlobals.CAT_COGS, len(suitsKilled)]) avQuests = av.getQuests() questList = [] # Iterate through the avatar's current quests. for i in xrange(0, len(avQuests), 5): questDesc = avQuests[i : i + 5] questClass = Quests.getQuest(questDesc[QuestIdIndex]) # Check if they are doing a cog quest if isinstance(questClass, Quests.CogQuest): # Check if the cog counts... for suit in suitsKilled: if questClass.doesCogCount(av.doId, suit, zoneId): # Looks like the cog counts! if questClass.getCompletionStatus(av, questDesc) != Quests.COMPLETE: questDesc[QuestProgressIndex] += 1 # Add the quest to the questList questList.append(questDesc) # Update the avatar's quests av.b_setQuests(questList) @magicWord(category=CATEGORY_PROGRAMMER, types=[str, int, int]) def quests(command, arg0=0, arg1=0): invoker = spellbook.getInvoker() currQuests = invoker.getQuests() currentQuestIds = [] for i in xrange(0, len(currQuests), 5): currentQuestIds.append(currQuests[i]) pocketSize = invoker.getQuestCarryLimit() carrying = len(currQuests) / 5 canCarry = False if (carrying < pocketSize): canCarry = True if command == 'clear': invoker.b_setQuests([]) return 'Cleared quests' elif command == 'clearHistory': invoker.d_setQuestHistory([]) return 'Cleared quests history' elif command == 'add': if arg0: if canCarry: if arg0 in Quests.QuestDict.keys(): quest = Quests.QuestDict[arg0] simbase.air.questManager.avatarChoseQuest(invoker.doId, None, arg0, quest[5], quest[4]) return 'Added QuestID %s'%(arg0) else: return 'Invalid QuestID %s'%(arg0) else: return 'Cannot take anymore quests' else: return 'add needs 1 argument.' elif command == 'remove': if arg0: if arg0 in currentQuestIds: invoker.removeQuest(arg0) return 'Removed QuestID %s'%(arg0) elif arg0 < pocketSize and arg0 > 0: if len(currentQuestIds) <= arg0: questIdToRemove = currentQuestIds[arg0 - 1] invoker.removeQuest(questIdToRemove) return 'Removed quest from slot %s'%(arg0) else: return 'Invalid quest slot' else: return 'Cannot remove quest %s'%(arg0) else: return 'remove needs 1 argument.' elif command == 'list': if arg0: if arg0 > 0 and arg0 <= pocketSize: start = (arg0 -1) * 5 questDesc = currQuests[start : start + 5] return 'QuestDesc in slot %s: %s.'%(arg0, questDesc) else: return 'Invalid quest slot %s.'%(arg0) else: return 'CurrentQuests: %s'%(currentQuestIds) elif command == 'bagSize': if arg0 > 0 and arg0 < 5: invoker.b_setQuestCarryLimit(arg0) return 'Set carry limit to %s'%(arg0) else: return 'Argument 0 must be between 1 and 4.' elif command == 'progress': if arg0 and arg1: if arg0 > 0 and arg0 <= pocketSize: questList = [] wantedQuestId = currentQuestIds[arg0 - 1] for i in xrange(0, len(currQuests), 5): questDesc = currQuests[i : i + 5] if questDesc[0] == wantedQuestId: questDesc[4] = arg1 questList.append(questDesc) invoker.b_setQuests(questList) return 'Set quest slot %s progress to %s'%(arg0, arg1) elif arg0 in Quests.QuestDict.keys(): if arg0 in currentQuestIds: questList = [] for i in xrange(0, len(currQuests), 5): questDesc = currQuests[i : i + 5] if questDesc[0] == arg0: questDesc[4] = arg1 questList.append(questDesc) invoker.b_setQuests(questList) return 'Set QuestID %s progress to %s'%(arg0, arg1) else: return 'Cannot progress QuestID: %s.'%(arg0) else: return 'Invalid quest or slot id' else: return 'progress needs 2 arguments.' elif command == 'tier': if arg0: invoker.b_setRewardHistory(arg0, invoker.getRewardHistory()[1]) return 'Set tier to %s'%(arg0) else: return 'tier needs 1 argument.' else: return 'Invalid first argument.'