from otp.ai.AIBaseGlobal import * from direct.task import Task from direct.directnotify import DirectNotifyGlobal from . import Quests from toontown.toon import NPCToons import random """ TODO: (done, tested) + + Jellybean reward + + Max Jellybean reward + + Quest combos + + Required and optional reward pool + + Quest page class + + Integrate chat next buttons + + Quest multiple choice AI + + Quest multiple choice gui + + Quest multiple choice choosing + + clean up NPC Toon dicts + + Stolen item list + + Stolen item quests + + Stolen item integration into reward panel + + Stolen item integration into battle + + Trolley quest + + Make friend quest + + sticker book reward + + non-rejectable quest tiers + + customize how many quests to choose from + + gui movies in quest movies + + Choose track access quest + + Track access partial rewards + + Toon HQ integration + + Multiple NPCs indoors + + Integrate quest page artwork + + Cog logos for posters + + Trolley reward in playground - - Dynamic timeout lengths based on movie New Track order: Choice 1: Sound or Heal Choice 2: Drop or Lure Choice 3: 1' or Trap Choice 4: 2' or 3' """ class QuestManagerAI: notify = DirectNotifyGlobal.directNotify.newCategory("QuestManagerAI") # Immediately complete all quests and all visits are to ToonHQ QuestCheat = simbase.config.GetBool("quest-cheat", 0) # table of requests for quests from specific avatars NextQuestDict = {} def __init__(self, air): self.air = air def requestInteract(self, avId, npc): self.notify.debug("requestInteract: avId: %s npcId: %s" % (avId, npc.getNpcId())) av = self.air.doId2do.get(avId) # Sanity check if av is None: self.notify.warning("some toon did a requestInteract but is not here: %s" % (avId)) return # If this NPC is busy, free the avatar if npc.isBusy(): self.notify.debug("freeing avatar %s because NPC is busy" % (avId)) npc.freeAvatar(avId) return # handle unusual cases such as NPC specific quests # interactionComplete = self.handleSpecialCases(avId, npc) # if interactionComplete: # return # First, see if any quests are completed before checking for incomplete # Since the ToonHQ could match multiple quests on the av list, we need # to prioritize what they give their attention to first. I think it # makes sense for them to clear complete quests first for questDesc in av.quests: # Sanity check for rogue quests if not Quests.questExists(questDesc[0]): av.removeAllTracesOfQuest(questDesc[0], questDesc[3]) self.rejectAvatar(av, npc) return if (self.isQuestComplete(av, npc, questDesc) == Quests.COMPLETE): self.completeQuest(av, npc, questDesc[0]) return needsQuestButNoneLeft = 0 if (self.needsQuest(av) and npc.getGivesQuests()): # bestQuests is a nested list of [questId, rewardId, toNpcId] lists quests = self.getNextQuestIds(npc, av) if quests: if (Quests.getNumChoices(av.getRewardTier()) == 0): assert(len(quests) == 1) # There should only be one if npc.getHq(): fromNpcId = Quests.ToonHQ else: fromNpcId = npc.getNpcId() self.assignQuest(avId, fromNpcId, *quests[0]) npc.assignQuest(av.getDoId(), *quests[0]) else: # if this avatar requested a quest, include it if avId in self.NextQuestDict: questId = self.NextQuestDict[avId] # if it's already in the list of quests, # we're all set ids = [] for q in quests: ids.append(q[0]) if questId not in ids: # add the quest as the first choice questDesc = Quests.QuestDict[questId] reward = questDesc[Quests.QuestDictRewardIndex] toNpcId = questDesc[Quests.QuestDictToNpcIndex] if reward is Quests.Any: reward = 604 # some jbs if toNpcId is Quests.Any: toNpcId = Quests.ToonHQ quests[0] = [questId, reward, toNpcId] npc.presentQuestChoice(av.getDoId(), quests) return else: needsQuestButNoneLeft = 1 # Now see if this npc has any incomplete quests with av questDesc = self.hasQuest(av, npc) if questDesc: completeStatus = self.isQuestComplete(av, npc, questDesc) questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc self.incompleteQuest(av, npc, questId, completeStatus, toNpcId) return else: if needsQuestButNoneLeft: # If they have quests, tell them to finish their tier if av.quests: self.rejectAvatarTierNotDone(av, npc) # If they do not have any quests, advance their tier else: if self.incrementReward(av): # quests is a nested list of [questId, rewardId, toNpcId] lists quests = self.getNextQuestIds(npc, av) if quests: if (Quests.getNumChoices(av.getRewardTier()) == 0): assert(len(quests) == 1) # There should only be one if npc.getHq(): fromNpcId = Quests.ToonHQ else: fromNpcId = npc.getNpcId() self.assignQuest(avId, fromNpcId, *quests[0]) npc.assignQuest(av.getDoId(), *quests[0]) else: npc.presentQuestChoice(av.getDoId(), quests) return else: # No more quests, sorry # TODO: put some more meaningful dialog here self.rejectAvatar(av, npc) return else: # Avatar does not need a quest, goodbye self.rejectAvatar(av, npc) return def handleSpecialCases(self, avId, npc): """ handle unusual cases such as NPC specific quests""" av = self.air.doId2do.get(avId) if npc.getNpcId() == 2018: # See if this npc has the TIP quest for questDesc in av.quests: # Do not use doId, use the NpcId because the doId is different across shards questId = questDesc[0] if (questId == 103): completeStatus = self.isQuestComplete(av, npc, questDesc) questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc self.incompleteQuest(av, npc, questId, completeStatus, toNpcId) return 1 if self.needsQuest(av): self.assignQuest(avId, npc.npcId, 103, Quests.QuestDict[103][5], Quests.QuestDict[103][4]) npc.assignQuest(avId, 103, Quests.QuestDict[103][5], Quests.QuestDict[103][4]) return 1 return 0 def rejectAvatar(self, av, npc): self.notify.debug("rejecting avatar: avId: %s" % (av.getDoId())) npc.rejectAvatar(av.getDoId()) return def rejectAvatarTierNotDone(self, av, npc): self.notify.debug("rejecting avatar because tier not done: avId: %s" % (av.getDoId())) npc.rejectAvatarTierNotDone(av.getDoId()) return def hasQuest(self, av, npc): # Check if this avId has a quest on this npcId for questDesc in av.quests: # Do not use doId, use the NpcId because the doId is different across shards questId = questDesc[0] fromNpcId = questDesc[1] toNpcId = questDesc[2] # If the fromNpc that gave you the quest is involved, or # if you have a toNpc then return this questId if (fromNpcId == npc.getNpcId()): self.notify.debug("hasQuest: found quest: %s avId: %s fromNpcId: %s" % (questId, av.getDoId(), fromNpcId)) return questDesc elif (toNpcId == npc.getNpcId()): # If the quest has this npc as the toNpc, then we are done self.notify.debug("hasQuest: found quest with toNpc: %s avId: %s toNpcId: %s" % (questId, av.getDoId(), toNpcId)) return questDesc elif (toNpcId == Quests.Any): # If the quest has "any" as the toNpc, than this guy will do self.notify.debug("hasQuest: found quest with any toNpc: %s avId: %s toNpcId: %s" % (questId, av.getDoId(), toNpcId)) return questDesc elif ((toNpcId == Quests.ToonHQ) and (npc.getHq())): # If the quest is for the HQ, and this toon has HQ powers, its a match self.notify.debug("hasQuest: found quest with HQ toNpc: %s avId: %s toNpcId: %s" % (questId, av.getDoId(), toNpcId)) return questDesc elif ((toNpcId == Quests.ToonTailor) and (npc.getTailor())): # If the quest is for a tailor, and this toon is a tailor, its a match self.notify.debug("hasQuest: found quest with Tailor toNpc: %s avId: %s toNpcId: %s" % (questId, av.getDoId(), toNpcId)) return questDesc self.notify.debug("hasQuest: did not find quest for avId: %s npcId: %s" % (av.getDoId(), npc.getNpcId())) return None def isQuestComplete(self, av, npc, questDesc): # The quest in question quest = Quests.getQuest(questDesc[0]) if quest == None: return 0 self.notify.debug("isQuestComplete: avId: %s, quest: %s" % (av.getDoId(), quest)) return quest.getCompletionStatus(av, questDesc, npc) def completeQuest(self, av, npc, questId): self.notify.info("completeQuest: avId: %s, npcId: %s, questId: %s" % (av.getDoId(), npc.getNpcId(), questId)) # If this is a track choice, we do not actually complete the quest, # We present the track choice gui. This can be cancelled which will # not complete the quest. questClass = Quests.getQuestClass(questId) if questClass == Quests.TrackChoiceQuest: self.notify.debug("completeQuest: presentTrackChoice avId: %s, npcId: %s, questId: %s" % (av.getDoId(), npc.getNpcId(), questId)) quest = Quests.getQuest(questId) tracks = quest.getChoices() npc.presentTrackChoice(av.getDoId(), questId, tracks) # Do not increment reward until avatar has chosen track # This happens in avatarChoseTrack return # If this is a deliver gag quest, we need to actually remove the # gags delivered from the player's inventory if questClass == Quests.DeliverGagQuest: self.notify.debug("completeQuest: presentTrackChoice avId: %s, npcId: %s, questId: %s" % (av.getDoId(), npc.getNpcId(), questId)) # Use the items from the inventory now quest = Quests.getQuest(questId) track, level = quest.getGagType() for i in range(0, quest.getNumGags()): av.inventory.useItem(track, level) av.d_setInventory(av.inventory.makeNetString()) # See if this quest is part of a multiquest. If it is, we assign # the next part of the multiquest. nextQuestId, nextToNpcId = Quests.getNextQuest(questId, npc, av) eventLogMessage = "%s|%s|%s|%s" % ( questId, npc.getNpcId(), questClass.__name__, nextQuestId) if nextQuestId == Quests.NA: rewardId = Quests.getAvatarRewardId(av, questId) # Update the toon with the reward reward = Quests.getReward(rewardId) # Clothing quests should have been handled by the Tailor. # Just to make sure if (reward.getType() == Quests.ClothingTicketReward): self.notify.warning("completeQuest: rogue ClothingTicketReward avId: %s, npcId: %s, questId: %s" % (av.getDoId(), npc.getNpcId(), questId)) npc.freeAvatar(av.getDoId()) return # Nope, this is the end, dish out the reward av.removeQuest(questId) # TODO: put this in the movie reward.sendRewardAI(av) # Full heal for completing a quest av.toonUp(av.maxHp) # Tell the npc to deliver the movie which will # complete the quest, display the reward, and do nothing else npc.completeQuest(av.getDoId(), questId, rewardId) # Bump the reward self.incrementReward(av) eventLogMessage += "|%s|%s" % ( reward.__class__.__name__, reward.getAmount()) else: # Full heal for completing part of a multistage quest av.toonUp(av.maxHp) # The user is not presented with a choice here av.removeQuest(questId) nextRewardId = Quests.getQuestReward(nextQuestId, av) if npc.getHq(): fromNpcId = Quests.ToonHQ else: fromNpcId = npc.getNpcId() self.assignQuest(av.getDoId(), fromNpcId, nextQuestId, nextRewardId, nextToNpcId, startingQuest = 0) npc.assignQuest(av.getDoId(), nextQuestId, nextRewardId, nextToNpcId) eventLogMessage += "|next %s" % (nextQuestId) self.air.writeServerEvent('questComplete', av.getDoId(), eventLogMessage) def incompleteQuest(self, av, npc, questId, completeStatus, toNpcId): self.notify.debug("incompleteQuest: avId: %s questId: %s" % (av.getDoId(), questId)) npc.incompleteQuest(av.getDoId(), questId, completeStatus, toNpcId) return def needsQuest(self, av): # Return 0 if this avatar does not need a new quest, 1 if he does quests = av.quests carryLimit = av.getQuestCarryLimit() if (len(quests) >= carryLimit): self.notify.debug("needsQuest: avId: %s is already full with %s/%s quest(s)" % (av.getDoId(), len(quests), carryLimit)) return 0 else: self.notify.debug("needsQuest: avId: %s only has %s/%s quest(s), needs another" % (av.getDoId(), len(quests), carryLimit)) return 1 def getNextQuestIds(self, npc, av): # Return the quest id, reward id for the next quest # Return None, None if the search fails for some reason return Quests.chooseBestQuests(av.getRewardTier(), npc, av) def incrementReward(self, av): # See if we finished a tier rewardTier = av.getRewardTier() # Make sure all the rewards have been handed out and # Make sure we have completed them all # First, make sure that the list is at least as big as the number of rewards # Then, make sure we have completed them all # Then, make sure all the rewards in the tier are in our history rewardHistory = av.getRewardHistory()[1] if ( # We cannot do this short-circuit test anymore because having # cog suit parts counts as a reward in cashbot # HQ. Unfortunately we are losing a pretty nice optimization # here. TODO: revisit and optimize. # (len(rewardHistory) >= Quests.getNumRewardsInTier(rewardTier)) and # We cannot do this because they might still be working on a few # optional quests from the old tier. # (len(av.quests) == 0) and # Make sure they have all the required rewards (Quests.avatarHasAllRequiredRewards(av, rewardTier)) and # Make sure they are not still working on required rewards (not Quests.avatarWorkingOnRequiredRewards(av)) ): if not Quests.rewardTierExists(rewardTier+1): self.notify.info("incrementReward: avId %s, at end of rewards" % (av.getDoId())) return 0 rewardTier += 1 self.notify.info("incrementReward: avId %s, new rewardTier: %s" % (av.getDoId(), rewardTier)) # If we have just moved on to the next tier, blow away the # old history, which is no longer needed. av.b_setQuestHistory([]) av.b_setRewardHistory(rewardTier, []) # The above will clear the quest history the *first* time # we cross into the next tier. There may still be some # quest id's hiding behind visit quests that belong to the # previous tier; these will find their way onto the quest # history when we eventually reveal them, but they will # still be associated with the previous tier. This does # no harm, so we won't worry about it; but it does mean # that the questHistory list is not guaranteed to only # list quests on the current tier. It is simply # guaranteed to list all the completed and in-progress # quests on the current tier, with maybe one or two others # thrown in. return 1 else: self.notify.debug("incrementReward: avId %s, not ready for new tier" % (av.getDoId())) return 0 def avatarCancelled(self, avId): # This is a message that came from the client, through the NPCToonAI. # It is in response to the avatar picking from a multiple choice menu self.notify.debug("avatarCancelled: avId: %s" % (avId)) return def avatarChoseTrack(self, avId, npc, questId, trackId): # This is a message that came from the client, through the NPCToonAI. # It is in response to the avatar picking from a multiple choice menu # of track options, along with a cancel option self.notify.info("avatarChoseTrack: avId: %s trackId: %s" % (avId, trackId)) av = self.air.doId2do.get(avId) if av: # Remove the track choice quest av.removeQuest(questId) # Update the toon with the reward rewardId = Quests.getRewardIdFromTrackId(trackId) reward = Quests.getReward(rewardId) reward.sendRewardAI(av) # Tell the npc to deliver the movie which will # complete the quest, display the reward, and do nothing else npc.completeQuest(av.getDoId(), questId, rewardId) self.incrementReward(av) else: self.notify.warning("avatarChoseTrack: av is gone.") def avatarChoseQuest(self, avId, npc, questId, rewardId, toNpcId): # This is a message that came from the client, through the NPCToonAI. # It is in response to the avatar picking from a multiple choice menu # of quest options, along with a cancel option self.notify.debug("avatarChooseQuest: avId: %s questId: %s" % (avId, questId)) av = self.air.doId2do.get(avId) if av: if npc.getHq(): fromNpcId = Quests.ToonHQ else: fromNpcId = npc.getNpcId() self.assignQuest(avId, fromNpcId, questId, rewardId, toNpcId) npc.assignQuest(avId, questId, rewardId, toNpcId) # Do not increment the reward until the quest is completed else: self.notify.warning("avatarChoseQuest: av is gone.") def assignQuest(self, avId, npcId, questId, rewardId, toNpcId, startingQuest = 1): self.notify.info("assignQuest: avId: %s npcId: %s questId: %s rewardId: %s toNpcId: %s startingQuest: %s" % (avId, npcId, questId, rewardId, toNpcId, startingQuest)) # assign quest to avatar # A quest is a list with (questId, npcId, toNpcId, rewardId, progress) av = self.air.doId2do.get(avId) if av: if startingQuest: # Since the first parts or multipart quests have NA for their # rewardIds, we need to get the final reward of this quest by searching # down the chain. If this questId is not the start of a multipart # quest, finalRewardId will come back None, and addQuest will handle it if rewardId == Quests.NA: finalRewardId = Quests.getFinalRewardId(questId) else: # Do not count the end of multipart quests, even though they # have a valid rewardId. That rewardId would have been counted # when the initial quest was given out if not Quests.isStartingQuest(questId): finalRewardId = None else: finalRewardId = rewardId # If this was not handed out as a starting quest, make sure you do not # count the reward twice else: finalRewardId = None # 0 for initial progress initialProgress = 0 # To make it easy for testing purposes. # This should never be on in production if self.QuestCheat: # Quest is already compelte initialProgress = 1000 # Clothing quests must be handled by the Tailor. if ((rewardId == Quests.NA) or (Quests.getRewardClass(rewardId) != Quests.ClothingTicketReward)): # Visit npc is the HQ toNpcId = Quests.ToonHQ if Quests.isLoopingFinalTier(av.getRewardTier()): # Do not record the history if this is the final looping tier recordHistory = 0 else: recordHistory = 1 av.addQuest((questId, npcId, toNpcId, rewardId, initialProgress), finalRewardId, recordHistory) # if this was a requested quest, clear it if self.NextQuestDict.get(avId) == questId: del self.NextQuestDict[avId] else: self.notify.warning("assignQuest: avatar not found: avId: %s" % (avId)) return def toonDefeatedFactory(self, av, location, avList): # factory is telling us that this avatar just defeated it. # see if this toon has a quest on this factory. If so, # update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 for questDesc in avQuests: quest = Quests.getQuest(questDesc[0]) num = quest.doesFactoryCount(avId, location, avList) if num > 0: questDesc[4] += num changed = 1 # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonDefeatedFactory: av made progress") av.b_setQuests(avQuests) else: self.notify.debug("toonDefeatedFactory: av made NO progress") def toonDefeatedStage(self, av, location, avList): self.notify.debug("toonDefeatedStage: av made NO progress") def toonRecoveredCogSuitPart(self, av, location, avList): avQuests = av.quests avId = av.getDoId() changed = 0 for questDesc in avQuests: quest = Quests.getQuest(questDesc[0]) num = quest.doesCogPartCount(avId, location, avList) if num > 0: questDesc[4] += num changed = 1 # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonRecoveredCogSuitPart: av made progress") av.b_setQuests(avQuests) else: self.notify.debug("toonRecoveredCogSuitPart: av made NO progress") def toonDefeatedMint(self, av, mintId, avList): # mint is telling us that this avatar just defeated it. # see if this toon has a quest on this mint. If so, # update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 for questDesc in avQuests: quest = Quests.getQuest(questDesc[0]) num = quest.doesMintCount(avId, mintId, avList) if num > 0: questDesc[4] += num changed = 1 # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonDefeatedMint: av made progress") av.b_setQuests(avQuests) else: self.notify.debug("toonDefeatedMint: av made NO progress") def toonDefeatedStage(self, av, stageId, avList): self.notify.debug("toonDefeatedStage") pass def toonKilledBuilding(self, av, track, difficulty, numFloors, zoneId, avList): # This is the battle notifying us that a toon has defeated a # building. See if this toon has a quest on this building. # If so, update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 #self.notify.debug("toonKilledBuilding: avId: %s, track: %s, diff: %s, numFloors: %s, zoneId: %s" % # (avId, track, difficulty, numFloors, zoneId)) for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) if ((questClass == Quests.BuildingQuest) or (questClass == Quests.BuildingNewbieQuest)): quest = Quests.getQuest(questDesc[0]) matchedTrack = ((quest.getBuildingTrack() == Quests.Any) or (quest.getBuildingTrack() == track)) matchedNumFloors = (quest.getNumFloors() <= numFloors) matchedLocation = quest.isLocationMatch(zoneId) if matchedTrack and matchedNumFloors and matchedLocation: num = quest.doesBuildingCount(avId, avList) if (num > 0): questDesc[4] += num changed = 1 else: # Do not care about this quest here continue # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonKilledBuilding: av made progress") av.b_setQuests(avQuests) else: self.notify.debug("toonKilledBuilding: av made NO progress") return def toonKilledCogdo(self, av, difficulty, numFloors, zoneId, avList): # This is the battle notifying us that a toon has defeated a # cogdo. See if this toon has a quest on this cogdo. # If so, update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 #self.notify.debug("toonKilledBuilding: avId: %s, track: %s, diff: %s, numFloors: %s, zoneId: %s" % # (avId, track, difficulty, numFloors, zoneId)) for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) """ TODO if ((questClass == Quests.BuildingQuest) or (questClass == Quests.BuildingNewbieQuest)): quest = Quests.getQuest(questDesc[0]) matchedTrack = ((quest.getBuildingTrack() == Quests.Any) or (quest.getBuildingTrack() == track)) matchedNumFloors = (quest.getNumFloors() <= numFloors) matchedLocation = quest.isLocationMatch(zoneId) if matchedTrack and matchedNumFloors and matchedLocation: num = quest.doesBuildingCount(avId, avList) if (num > 0): questDesc[4] += num changed = 1 else: # Do not care about this quest here continue """ # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonKilledCogdo: av made progress") av.b_setQuests(avQuests) else: self.notify.debug("toonKilledCogdo: av made NO progress") return def toonKilledCogs(self, av, cogList, zoneId, avList): # This is the battle notifying us that a toon killed some cogs # See if this toon has a quest on these cogs. If so, update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 self.notify.debug("toonKilledCogs: avId: %s, avQuests: %s, cogList: %s, zoneId: %s" % (avId, avQuests, cogList, zoneId)) for questDesc in avQuests: quest = Quests.getQuest(questDesc[0]) if quest != None: for cogDict in cogList: if cogDict['isVP']: num = quest.doesVPCount(avId, cogDict, zoneId, avList) elif cogDict['isCFO']: num = quest.doesCFOCount(avId, cogDict, zoneId, avList) else: num = quest.doesCogCount(avId, cogDict, zoneId, avList) if (num > 0): questDesc[4] += num changed = 1 # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonKilledCogs: av %s made progress" % (avId)) av.b_setQuests(avQuests) else: self.notify.debug("toonKilledCogs: av %s made NO progress" % (avId)) return def toonRodeTrolleyFirstTime(self, av): # This is notifying us that a toon has gotten on the # trolley for the first time. See if this toon has a # trolley quest. If so, update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) if (questClass == Quests.TrolleyQuest): # Set progress questDesc[4] = 1 changed = 1 else: # Do not care about this quest here pass # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonRodeTrolleyFirstTime: av %s made progress" % (avId)) av.b_setQuests(avQuests) # log this event self.air.writeServerEvent('firstTrolleyGame', avId, '') else: self.notify.debug("toonRodeTrolleyFirstTime: av %s made NO progress" % (avId)) return def toonPlayedMinigame(self, av, avList): # This is notifying us that a toon has entered a minigame. # See if this toon has a minigame quest. If so, update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) if (questClass == Quests.MinigameNewbieQuest): quest = Quests.getQuest(questDesc[0]) num = quest.doesMinigameCount(av, avList) if (num > 0): # Set progress questDesc[4] += num changed = 1 # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonPlayedMinigame: av %s made progress" % (avId)) av.b_setQuests(avQuests) else: self.notify.debug("toonPlayedMinigame: av %s made NO progress" % (avId)) return def toonOpenedMailbox(self, av): # This is notifying us that a toon has opened his mailbox # See if this toon has a mailbox quest. If so, update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) if (questClass == Quests.MailboxQuest): # Set progress questDesc[4] = 1 changed = 1 # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonOpenedMailbox: av %s made progress" % (avId)) av.b_setQuests(avQuests) else: self.notify.debug("toonOpenedMailbox: av %s made NO progress" % (avId)) return def toonUsedPhone(self, av): # This is notifying us that a toon used his phone # See if this toon has a phone quest. If so, update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) if (questClass == Quests.PhoneQuest): # Set progress questDesc[4] = 1 changed = 1 # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonUsedPhone: av %s made progress" % (avId)) av.b_setQuests(avQuests) else: self.notify.debug("toonUsedPhone: av %s made NO progress" % (avId)) return def recoverItems(self, av, cogList, zoneId): avQuests = av.quests avId = av.getDoId() itemsRecovered = [] itemsNotRecovered = [] changed = 0 for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) if (questClass == Quests.RecoverItemQuest): quest = Quests.getQuest(questDesc[0]) # See if the cog that stole the item is in the cogList questCogType = quest.getHolder() qualifier = quest.getHolderType() for cogDict in cogList: # If the cogType is Quests.Any, that means any cog # Ok, now check to see if we recovered the item based # on the percent chance of finding it stored in the quest # Only find items if we still need them self.notify.debug("recoverItems: checking against cogDict: %s" % (cogDict)) if ((questCogType == Quests.Any) or (questCogType == cogDict[qualifier]) or # If it is level based, count those higher too ((qualifier == 'level') and (questCogType <= cogDict[qualifier])) ): if avId in cogDict['activeToons']: if not quest.testDone(questDesc[4]):#if questDesc[4] < quest.getNumItems(): if quest.isLocationMatch(zoneId): #rand = random.random() * 100 #if rand <= quest.getPercentChance(): check, count = quest.testRecover(questDesc[4]) if check: # FOUND IT! Increment progress by one item #questDesc[4] += 1 # Keep track of all the items recovered itemsRecovered.append(quest.getItem()) #changed = 1 self.notify.debug("recoverItems: av %s made progress: %s" % (avId, questDesc[4])) else: self.notify.debug("recoverItems: av %s made NO progress (item not found) [%s > %s])" % (avId, check, quest.getPercentChance())) itemsNotRecovered.append(quest.getItem()) #keeping track of missed items changed = 1 questDesc[4] = count else: self.notify.debug("recoverItems: av %s made NO progress (wrong location)" % (avId)) else: self.notify.debug("recoverItems: av %s made NO progress (have enough already)" % (avId)) else: self.notify.debug("recoverItems: av %s made NO progress (av not active)" % (avId)) else: self.notify.debug("recoverItems: av %s made NO progress (wrong cog type)" % (avId)) else: # Do not care about this quest here continue # Now send the quests back to the avatar if the status changed # Note: this means that an avatar will immediately get credit # for finding an item, even if the item is found in the middle # floor of a building and the avatar later is killed on a # later floor, thus failing the building. if changed: av.b_setQuests(avQuests) return (itemsRecovered, itemsNotRecovered) def findItemInWater(self, av, zoneId): # Similar to recoverItems, but this is called from the # DistributedFishingSpot to see if there are any quest items # in the water. No cogs are involved; hence, the only valid # questCogType is Quests.AnyFish. # Only one item at a time is returned by this function; the # function either returns the item found, or None. # Note: this does not support two quests with same item avQuests = av.quests avId = av.getDoId() for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) if (questClass == Quests.RecoverItemQuest): quest = Quests.getQuest(questDesc[0]) if ((quest.getType() == Quests.RecoverItemQuest) and (quest.getHolder() == Quests.AnyFish) and ((random.random() * 100) <= quest.getPercentChance()) and (questDesc[4] < quest.getNumItems()) and quest.isLocationMatch(zoneId) ): # FOUND IT! Increment progress by one item questDesc[4] += 1 self.notify.debug("findItemInWater: av %s made progress" % (avId)) av.b_setQuests(avQuests) # Return the item recovered return quest.getItem() else: # Do not care about this quest here continue self.notify.debug("findItemInWater: av %s made NO progress" % (avId)) return None def completeAllQuestsMagically(self, av): avQuests = av.quests for quest in avQuests: # Make sure the progress is really high so the quest will seem completed quest[4] = 1000 av.b_setQuests(avQuests) return 1 def completeQuestMagically(self, av, index): avQuests = av.quests # Make sure we are in range if index < len(av.quests): # Make sure the progress is really high so the quest will seem completed avQuests[index][4] = 1000 av.b_setQuests(avQuests) return 1 else: return 0 def toonMadeFriend(self, av, otherAv): # This is notifying us that a toon has made a friend. # See if this toon has a friend quest. # If so, update the progress. avQuests = av.quests avId = av.getDoId() changed = 0 for questDesc in avQuests: questClass = Quests.getQuestClass(questDesc[0]) if ((questClass == Quests.FriendQuest) or (questClass == Quests.FriendNewbieQuest)): quest = Quests.getQuest(questDesc[0]) if (quest.doesFriendCount(av, otherAv)): # Set progress questDesc[4] += 1 changed = 1 else: # Do not care about this quest here continue # Now send the quests back to the avatar if the status changed if changed: self.notify.debug("toonMadeFriend: av %s made progress" % (avId)) av.b_setQuests(avQuests) else: self.notify.debug("toonMadeFriend: av %s made NO progress" % (avId)) def hasTailorClothingTicket(self, av, npc): for questDesc in av.quests: questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc questType = Quests.getQuestClass(questId) # See if this NPC is the one we are supposed to deliver to # You by definition have the item if (questType == Quests.DeliverItemQuest): if Quests.npcMatches(toNpcId, npc): rewardId = Quests.getAvatarRewardId(av, questId) rewardType = Quests.getRewardClass(rewardId) if (rewardType == Quests.ClothingTicketReward): return 1 elif(rewardType == Quests.TIPClothingTicketReward): return 2 else: # Reward was not a clothing ticket continue else: # NPC does not match continue else: # Not a deliver item quest continue # Did not find it, avId does not have clothing ticket on this tailor return 0 def removeClothingTicket(self, av, npc): for questDesc in av.quests: questId, fromNpcId, toNpcId, rewardId, toonProgress = questDesc questClass = Quests.getQuestClass(questId) # See if this NPC is the one we are supposed to deliver to # You by definition have the item if (questClass == Quests.DeliverItemQuest): if Quests.npcMatches(toNpcId, npc): rewardId = Quests.getAvatarRewardId(av, questId) rewardClass = Quests.getRewardClass(rewardId) if (rewardClass == Quests.ClothingTicketReward or rewardClass == Quests.TIPClothingTicketReward): # This section is much like completeQuest() av.removeQuest(questId) # Update the toon with the reward. This reward does nothing right # now but it may in the future, so it is the right thing to do reward = Quests.getReward(rewardId) reward.sendRewardAI(av) # Bump the reward self.incrementReward(av) return 1 else: # Reward was not a clothing ticket continue else: # NPC does not match continue else: # Not a deliver item quest continue # Did not find it, avId does not have clothing ticket on this tailor return 0 def setNextQuest(self, avId, questId): # for ~nextQuest: queue up a quest for this avatar self.NextQuestDict[avId] = questId def cancelNextQuest(self, avId): # cancel any pending quest for this avatar oldQuest = self.NextQuestDict.get(avId) if oldQuest: del self.NextQuestDict[avId] return oldQuest