oldschool-toontown/toontown/quest/QuestManagerAI.py

994 lines
44 KiB
Python
Raw Normal View History

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))
2020-01-08 22:38:58 -06:00
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
2020-01-08 22:38:58 -06:00
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
2020-01-09 21:18:33 -06:00
def setNextQuest(self, avId, questId):
# for ~nextQuest: queue up a quest for this avatar
self.NextQuestDict[avId] = questId
2020-01-10 16:20:26 -06:00
def cancelNextQuest(self, avId):
# cancel any pending quest for this avatar
oldQuest = self.NextQuestDict.get(avId)
if oldQuest:
del self.NextQuestDict[avId]
return oldQuest