quest: tutorial: Disney's Quest and Tutorial Mgrs.
This commit is contained in:
parent
984058ce32
commit
caaaa39a92
22 changed files with 1786 additions and 51 deletions
|
@ -97,3 +97,7 @@ accept-clock-skew 1
|
||||||
text-minfilter linear_mipmap_linear
|
text-minfilter linear_mipmap_linear
|
||||||
gc-save-all 0
|
gc-save-all 0
|
||||||
server-data-folder data
|
server-data-folder data
|
||||||
|
|
||||||
|
# TEMPORARY
|
||||||
|
skip-friend-quest true
|
||||||
|
skip-phone-quest true
|
||||||
|
|
|
@ -467,7 +467,7 @@ dclass DistributedToon : DistributedPlayer {
|
||||||
setHoodsVisited(uint32[] = [2000]) required ownrecv db;
|
setHoodsVisited(uint32[] = [2000]) required ownrecv db;
|
||||||
setInterface(blob = []) required ownrecv db;
|
setInterface(blob = []) required ownrecv db;
|
||||||
setLastHood(uint32 = 0) required ownrecv db;
|
setLastHood(uint32 = 0) required ownrecv db;
|
||||||
setTutorialAck(uint8 = 1) required ownrecv db;
|
setTutorialAck(uint8 = 0) required ownrecv db;
|
||||||
setMaxClothes(uint32 = 10) required ownrecv db;
|
setMaxClothes(uint32 = 10) required ownrecv db;
|
||||||
setClothesTopsList(uint8[] = []) required ownrecv db;
|
setClothesTopsList(uint8[] = []) required ownrecv db;
|
||||||
setClothesBottomsList(uint8[] = []) required ownrecv db;
|
setClothesBottomsList(uint8[] = []) required ownrecv db;
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
from pandac.PandaModules import *
|
from panda3d.core import *
|
||||||
|
from panda3d.otp import *
|
||||||
QuietZone = 1
|
QuietZone = 1
|
||||||
UberZone = 2
|
UberZone = 2
|
||||||
WallBitmask = BitMask32(1)
|
WallBitmask = BitMask32(1)
|
||||||
|
|
18
toontown/ai/BlackCatHolidayMgrAI.py
Normal file
18
toontown/ai/BlackCatHolidayMgrAI.py
Normal file
|
@ -0,0 +1,18 @@
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from toontown.ai import HolidayBaseAI
|
||||||
|
from toontown.toonbase import ToontownGlobals
|
||||||
|
|
||||||
|
class BlackCatHolidayMgrAI(HolidayBaseAI.HolidayBaseAI):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||||
|
'BlackCatHolidayMgrAI')
|
||||||
|
|
||||||
|
PostName = 'blackCatHoliday'
|
||||||
|
|
||||||
|
def __init__(self, air, holidayId):
|
||||||
|
HolidayBaseAI.HolidayBaseAI.__init__(self, air, holidayId)
|
||||||
|
|
||||||
|
def start(self):
|
||||||
|
bboard.post(BlackCatHolidayMgrAI.PostName)
|
||||||
|
|
||||||
|
def stop(self):
|
||||||
|
bboard.remove(BlackCatHolidayMgrAI.PostName)
|
|
@ -48,6 +48,7 @@ from toontown.spellbook.ToontownMagicWordManagerAI import ToontownMagicWordManag
|
||||||
from toontown.suit.SuitInvasionManagerAI import SuitInvasionManagerAI
|
from toontown.suit.SuitInvasionManagerAI import SuitInvasionManagerAI
|
||||||
from toontown.toon import NPCToons
|
from toontown.toon import NPCToons
|
||||||
from toontown.toonbase import ToontownGlobals
|
from toontown.toonbase import ToontownGlobals
|
||||||
|
from toontown.tutorial.TutorialManagerAI import TutorialManagerAI
|
||||||
from toontown.uberdog.DistributedInGameNewsMgrAI import DistributedInGameNewsMgrAI
|
from toontown.uberdog.DistributedInGameNewsMgrAI import DistributedInGameNewsMgrAI
|
||||||
import os
|
import os
|
||||||
|
|
||||||
|
@ -220,6 +221,10 @@ class ToontownAIRepository(ToontownInternalRepository):
|
||||||
self.magicWordManager = ToontownMagicWordManagerAI(self)
|
self.magicWordManager = ToontownMagicWordManagerAI(self)
|
||||||
self.magicWordManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
self.magicWordManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||||
|
|
||||||
|
# Generate our Tutorial manager...
|
||||||
|
self.tutorialManager = TutorialManagerAI(self)
|
||||||
|
self.tutorialManager.generateWithRequired(OTP_ZONE_ID_MANAGEMENT)
|
||||||
|
|
||||||
def generateHood(self, hoodConstructor, zoneId):
|
def generateHood(self, hoodConstructor, zoneId):
|
||||||
# Bossbot HQ doesn't use DNA, so we skip over that.
|
# Bossbot HQ doesn't use DNA, so we skip over that.
|
||||||
if zoneId != ToontownGlobals.BossbotHQ:
|
if zoneId != ToontownGlobals.BossbotHQ:
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
from toontown.toonbase.ToonBaseGlobal import *
|
from toontown.toonbase.ToonBaseGlobal import *
|
||||||
from pandac.PandaModules import *
|
from panda3d.core import *
|
||||||
|
from panda3d.toontown import *
|
||||||
from direct.interval.IntervalGlobal import *
|
from direct.interval.IntervalGlobal import *
|
||||||
from direct.distributed.ClockDelta import *
|
from direct.distributed.ClockDelta import *
|
||||||
from toontown.toonbase import ToontownGlobals
|
from toontown.toonbase import ToontownGlobals
|
||||||
|
|
|
@ -1,5 +1,40 @@
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from toontown.toonbase.ToontownGlobals import *
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
from otp.ai.AIBaseGlobal import *
|
||||||
|
from direct.distributed.ClockDelta import *
|
||||||
|
|
||||||
class DistributedTutorialInteriorAI(DistributedObjectAI):
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTutorialInteriorAI')
|
from direct.distributed import DistributedObjectAI
|
||||||
|
from toontown.toon import NPCToons
|
||||||
|
|
||||||
|
class DistributedTutorialInteriorAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
|
|
||||||
|
if __debug__:
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTutorialInteriorAI')
|
||||||
|
|
||||||
|
def __init__(self, block, air, zoneId, building, npcId):
|
||||||
|
"""blockNumber: the landmark building number (from the name)"""
|
||||||
|
#self.air=air
|
||||||
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
|
self.block=block
|
||||||
|
self.zoneId=zoneId
|
||||||
|
self.building=building
|
||||||
|
self.tutorialNpcId = npcId
|
||||||
|
|
||||||
|
|
||||||
|
# Make any npcs that may be in this interior zone
|
||||||
|
# If there are none specified, this will just be an empty list
|
||||||
|
self.npcs = NPCToons.createNpcsInZone(air, zoneId)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
self.ignoreAll()
|
||||||
|
for npc in self.npcs:
|
||||||
|
npc.requestDelete()
|
||||||
|
del self.npcs
|
||||||
|
del self.building
|
||||||
|
DistributedObjectAI.DistributedObjectAI.delete(self)
|
||||||
|
|
||||||
|
def getZoneIdAndBlock(self):
|
||||||
|
return [self.zoneId, self.block]
|
||||||
|
|
||||||
|
def getTutorialNpcId(self):
|
||||||
|
return self.tutorialNpcId
|
|
@ -61,7 +61,7 @@ class ToonInterior(Place.Place):
|
||||||
State.State('HFA', self.enterHFA, self.exitHFA, ['HFAReject', 'teleportOut', 'tunnelOut']),
|
State.State('HFA', self.enterHFA, self.exitHFA, ['HFAReject', 'teleportOut', 'tunnelOut']),
|
||||||
State.State('HFAReject', self.enterHFAReject, self.exitHFAReject, ['walk']),
|
State.State('HFAReject', self.enterHFAReject, self.exitHFAReject, ['walk']),
|
||||||
State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk']),
|
State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk']),
|
||||||
State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk']),
|
State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']),
|
||||||
State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']),
|
State.State('teleportIn', self.enterTeleportIn, self.exitTeleportIn, ['walk']),
|
||||||
State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']),
|
State.State('teleportOut', self.enterTeleportOut, self.exitTeleportOut, ['teleportIn']),
|
||||||
State.State('quest', self.enterQuest, self.exitQuest, ['walk', 'doorOut']),
|
State.State('quest', self.enterQuest, self.exitQuest, ['walk', 'doorOut']),
|
||||||
|
|
91
toontown/building/TutorialBuildingAI.py
Normal file
91
toontown/building/TutorialBuildingAI.py
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
from pandac.PandaModules import *
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from . import DistributedDoorAI
|
||||||
|
from . import DistributedTutorialInteriorAI
|
||||||
|
from . import FADoorCodes
|
||||||
|
from . import DoorTypes
|
||||||
|
from toontown.toon import NPCToons
|
||||||
|
from toontown.toonbase import TTLocalizer
|
||||||
|
|
||||||
|
# This is not a distributed class... It just owns and manages some distributed
|
||||||
|
# classes.
|
||||||
|
|
||||||
|
class TutorialBuildingAI:
|
||||||
|
def __init__(self, air, exteriorZone, interiorZone, blockNumber):
|
||||||
|
# While this is not a distributed object, it needs to know about
|
||||||
|
# the repository.
|
||||||
|
self.air = air
|
||||||
|
self.exteriorZone = exteriorZone
|
||||||
|
self.interiorZone = interiorZone
|
||||||
|
|
||||||
|
# This is because we are "pretending" to be a DistributedBuilding.
|
||||||
|
# The DistributedTutorialInterior takes a peek at savedBy. It really
|
||||||
|
# should make a function call. Perhaps TutorialBuildingAI and
|
||||||
|
# DistributedBuildingAI should inherit from each other somehow,
|
||||||
|
# but I can't see an easy way to do that.
|
||||||
|
self.savedBy = None
|
||||||
|
|
||||||
|
self.setup(blockNumber)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.interior.requestDelete()
|
||||||
|
del self.interior
|
||||||
|
self.door.requestDelete()
|
||||||
|
del self.door
|
||||||
|
self.insideDoor.requestDelete()
|
||||||
|
del self.insideDoor
|
||||||
|
self.gagShopNPC.requestDelete()
|
||||||
|
del self.gagShopNPC
|
||||||
|
return
|
||||||
|
|
||||||
|
def setup(self, blockNumber):
|
||||||
|
# Put an NPC in here. Give him id# 20000. When he has assigned
|
||||||
|
# his quest, he will unlock the interior door.
|
||||||
|
self.gagShopNPC = NPCToons.createNPC(
|
||||||
|
self.air, 20000,
|
||||||
|
(self.interiorZone,
|
||||||
|
TTLocalizer.NPCToonNames[20000],
|
||||||
|
("dll" ,"ms" ,"m" ,"m" ,7 ,0 ,7 ,7 ,2 ,6 ,2 ,6 ,2 ,16), "m", 1, NPCToons.NPC_REGULAR),
|
||||||
|
self.interiorZone,
|
||||||
|
questCallback=self.unlockInteriorDoor)
|
||||||
|
# Flag him as being part of tutorial
|
||||||
|
self.gagShopNPC.setTutorial(1)
|
||||||
|
npcId = self.gagShopNPC.getDoId()
|
||||||
|
# Toon interior (with tutorial flag set to 1)
|
||||||
|
self.interior=DistributedTutorialInteriorAI.DistributedTutorialInteriorAI(
|
||||||
|
blockNumber, self.air, self.interiorZone, self, npcId)
|
||||||
|
self.interior.generateWithRequired(self.interiorZone)
|
||||||
|
# Outside door:
|
||||||
|
door=DistributedDoorAI.DistributedDoorAI(self.air, blockNumber,
|
||||||
|
DoorTypes.EXT_STANDARD,
|
||||||
|
lockValue=FADoorCodes.DEFEAT_FLUNKY_TOM)
|
||||||
|
# Inside door. Locked until you get your gags.
|
||||||
|
insideDoor=DistributedDoorAI.DistributedDoorAI(
|
||||||
|
self.air,
|
||||||
|
blockNumber,
|
||||||
|
DoorTypes.INT_STANDARD,
|
||||||
|
lockValue=FADoorCodes.TALK_TO_TOM)
|
||||||
|
# Tell them about each other:
|
||||||
|
door.setOtherDoor(insideDoor)
|
||||||
|
insideDoor.setOtherDoor(door)
|
||||||
|
door.zoneId=self.exteriorZone
|
||||||
|
insideDoor.zoneId=self.interiorZone
|
||||||
|
# Now that they both now about each other, generate them:
|
||||||
|
door.generateWithRequired(self.exteriorZone)
|
||||||
|
#door.sendUpdate("setDoorIndex", [door.getDoorIndex()])
|
||||||
|
insideDoor.generateWithRequired(self.interiorZone)
|
||||||
|
#insideDoor.sendUpdate("setDoorIndex", [door.getDoorIndex()])
|
||||||
|
# keep track of them:
|
||||||
|
self.door=door
|
||||||
|
self.insideDoor=insideDoor
|
||||||
|
return
|
||||||
|
|
||||||
|
def unlockInteriorDoor(self):
|
||||||
|
self.insideDoor.setDoorLock(FADoorCodes.UNLOCKED)
|
||||||
|
|
||||||
|
def battleOverCallback(self):
|
||||||
|
# There is an if statement here because it is possible for
|
||||||
|
# the callback to get called after cleanup has already taken
|
||||||
|
# place.
|
||||||
|
if hasattr(self, "door"):
|
||||||
|
self.door.setDoorLock(FADoorCodes.TALK_TO_HQ_TOM)
|
128
toontown/building/TutorialHQBuildingAI.py
Normal file
128
toontown/building/TutorialHQBuildingAI.py
Normal file
|
@ -0,0 +1,128 @@
|
||||||
|
from panda3d.core import *
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from . import DistributedDoorAI
|
||||||
|
from . import DistributedHQInteriorAI
|
||||||
|
from . import FADoorCodes
|
||||||
|
from . import DoorTypes
|
||||||
|
from toontown.toon import NPCToons
|
||||||
|
from toontown.quest import Quests
|
||||||
|
from toontown.toonbase import TTLocalizer
|
||||||
|
|
||||||
|
# This is not a distributed class... It just owns and manages some distributed
|
||||||
|
# classes.
|
||||||
|
|
||||||
|
class TutorialHQBuildingAI:
|
||||||
|
def __init__(self, air, exteriorZone, interiorZone, blockNumber):
|
||||||
|
# While this is not a distributed object, it needs to know about
|
||||||
|
# the repository.
|
||||||
|
self.air = air
|
||||||
|
self.exteriorZone = exteriorZone
|
||||||
|
self.interiorZone = interiorZone
|
||||||
|
|
||||||
|
self.setup(blockNumber)
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.interior.requestDelete()
|
||||||
|
del self.interior
|
||||||
|
self.npc.requestDelete()
|
||||||
|
del self.npc
|
||||||
|
self.door0.requestDelete()
|
||||||
|
del self.door0
|
||||||
|
self.door1.requestDelete()
|
||||||
|
del self.door1
|
||||||
|
self.insideDoor0.requestDelete()
|
||||||
|
del self.insideDoor0
|
||||||
|
self.insideDoor1.requestDelete()
|
||||||
|
del self.insideDoor1
|
||||||
|
return
|
||||||
|
|
||||||
|
def setup(self, blockNumber):
|
||||||
|
# The interior
|
||||||
|
self.interior=DistributedHQInteriorAI.DistributedHQInteriorAI(
|
||||||
|
blockNumber, self.air, self.interiorZone)
|
||||||
|
|
||||||
|
# We do not use a standard npc toon here becuase these npcs are created on
|
||||||
|
# the fly for as many tutorials as we need. The interior zone is not known
|
||||||
|
# until the ai allocates a zone, so we fabricate the description here.
|
||||||
|
desc = (self.interiorZone, TTLocalizer.TutorialHQOfficerName, ('dls', 'ms', 'm', 'm', 6,0,6,6,0,10,0,10,2,9), "m", 1, 0)
|
||||||
|
self.npc = NPCToons.createNPC(self.air, Quests.ToonHQ, desc,
|
||||||
|
self.interiorZone,
|
||||||
|
questCallback=self.unlockInsideDoor1)
|
||||||
|
# Flag npc as part of tutorial
|
||||||
|
self.npc.setTutorial(1)
|
||||||
|
|
||||||
|
self.interior.generateWithRequired(self.interiorZone)
|
||||||
|
# Outside door 0. Locked til you defeat the Flunky:
|
||||||
|
door0=DistributedDoorAI.DistributedDoorAI(
|
||||||
|
self.air, blockNumber, DoorTypes.EXT_HQ,
|
||||||
|
doorIndex=0,
|
||||||
|
lockValue=FADoorCodes.DEFEAT_FLUNKY_HQ)
|
||||||
|
# Outside door 1. Always locked.
|
||||||
|
door1=DistributedDoorAI.DistributedDoorAI(
|
||||||
|
self.air, blockNumber, DoorTypes.EXT_HQ,
|
||||||
|
doorIndex=1,
|
||||||
|
lockValue=FADoorCodes.GO_TO_PLAYGROUND)
|
||||||
|
# Inside door 0. Always locked, but the message will change.
|
||||||
|
insideDoor0=DistributedDoorAI.DistributedDoorAI(
|
||||||
|
self.air,
|
||||||
|
blockNumber,
|
||||||
|
DoorTypes.INT_HQ,
|
||||||
|
doorIndex=0,
|
||||||
|
lockValue=FADoorCodes.TALK_TO_HQ)
|
||||||
|
# Inside door 1. Locked til you get your HQ reward.
|
||||||
|
insideDoor1=DistributedDoorAI.DistributedDoorAI(
|
||||||
|
self.air,
|
||||||
|
blockNumber,
|
||||||
|
DoorTypes.INT_HQ,
|
||||||
|
doorIndex=1,
|
||||||
|
lockValue=FADoorCodes.TALK_TO_HQ)
|
||||||
|
# Tell them about each other:
|
||||||
|
door0.setOtherDoor(insideDoor0)
|
||||||
|
insideDoor0.setOtherDoor(door0)
|
||||||
|
door1.setOtherDoor(insideDoor1)
|
||||||
|
insideDoor1.setOtherDoor(door1)
|
||||||
|
# Put them in the right zones
|
||||||
|
door0.zoneId=self.exteriorZone
|
||||||
|
door1.zoneId=self.exteriorZone
|
||||||
|
insideDoor0.zoneId=self.interiorZone
|
||||||
|
insideDoor1.zoneId=self.interiorZone
|
||||||
|
# Now that they both now about each other, generate them:
|
||||||
|
door0.generateWithRequired(self.exteriorZone)
|
||||||
|
door1.generateWithRequired(self.exteriorZone)
|
||||||
|
door0.sendUpdate("setDoorIndex", [door0.getDoorIndex()])
|
||||||
|
door1.sendUpdate("setDoorIndex", [door1.getDoorIndex()])
|
||||||
|
insideDoor0.generateWithRequired(self.interiorZone)
|
||||||
|
insideDoor1.generateWithRequired(self.interiorZone)
|
||||||
|
insideDoor0.sendUpdate("setDoorIndex", [insideDoor0.getDoorIndex()])
|
||||||
|
insideDoor1.sendUpdate("setDoorIndex", [insideDoor1.getDoorIndex()])
|
||||||
|
# keep track of them:
|
||||||
|
self.door0=door0
|
||||||
|
self.door1=door1
|
||||||
|
self.insideDoor0=insideDoor0
|
||||||
|
self.insideDoor1=insideDoor1
|
||||||
|
# hide the periscope
|
||||||
|
self.interior.setTutorial(1)
|
||||||
|
return
|
||||||
|
|
||||||
|
def unlockDoor(self, door):
|
||||||
|
door.setDoorLock(FADoorCodes.UNLOCKED)
|
||||||
|
|
||||||
|
def battleOverCallback(self):
|
||||||
|
# There is an if statement here because it is possible for
|
||||||
|
# the callback to get called after cleanup has already taken
|
||||||
|
# place.
|
||||||
|
if hasattr(self, "door0"):
|
||||||
|
self.unlockDoor(self.door0)
|
||||||
|
|
||||||
|
# This callback type happens to give zoneId. We don't need it.
|
||||||
|
def unlockInsideDoor1(self):
|
||||||
|
# There is an if statement here because it is possible for
|
||||||
|
# the callback to get called after cleanup has already taken
|
||||||
|
# place.
|
||||||
|
if hasattr(self, "insideDoor1"):
|
||||||
|
self.unlockDoor(self.insideDoor1)
|
||||||
|
# Change the message on this locked door to tell you to go
|
||||||
|
# through the other door. Maybe this door should not be
|
||||||
|
# here at all?
|
||||||
|
if hasattr(self, "insideDoor0"):
|
||||||
|
self.insideDoor0.setDoorLock(FADoorCodes.WRONG_DOOR_HQ)
|
File diff suppressed because it is too large
Load diff
|
@ -4,7 +4,8 @@ import tokenize
|
||||||
import copy
|
import copy
|
||||||
from direct.interval.IntervalGlobal import *
|
from direct.interval.IntervalGlobal import *
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from pandac.PandaModules import *
|
from panda3d.core import *
|
||||||
|
from panda3d.otp import *
|
||||||
from direct.showbase import DirectObject
|
from direct.showbase import DirectObject
|
||||||
from . import BlinkingArrows
|
from . import BlinkingArrows
|
||||||
from toontown.toon import ToonHeadFrame
|
from toontown.toon import ToonHeadFrame
|
||||||
|
|
|
@ -1743,11 +1743,10 @@ class TrackChoiceQuest(Quest):
|
||||||
|
|
||||||
class FriendQuest(Quest):
|
class FriendQuest(Quest):
|
||||||
def filterFunc(avatar):
|
def filterFunc(avatar):
|
||||||
if len(avatar.getFriendsList()) == 0:
|
if not config.GetBool('skip-friend-quest', False) and len(avatar.getFriendsList()) == 0:
|
||||||
return 1
|
return 1
|
||||||
else:
|
else:
|
||||||
return 0
|
return 0
|
||||||
|
|
||||||
filterFunc = staticmethod(filterFunc)
|
filterFunc = staticmethod(filterFunc)
|
||||||
|
|
||||||
def __init__(self, id, quest):
|
def __init__(self, id, quest):
|
||||||
|
@ -1889,6 +1888,13 @@ class MailboxQuest(Quest):
|
||||||
|
|
||||||
|
|
||||||
class PhoneQuest(Quest):
|
class PhoneQuest(Quest):
|
||||||
|
def filterFunc(avatar):
|
||||||
|
if not config.GetBool('skip-phone-quest', False):
|
||||||
|
return 1
|
||||||
|
else:
|
||||||
|
return 0
|
||||||
|
filterFunc = staticmethod(filterFunc)
|
||||||
|
|
||||||
def __init__(self, id, quest):
|
def __init__(self, id, quest):
|
||||||
Quest.__init__(self, id, quest)
|
Quest.__init__(self, id, quest)
|
||||||
|
|
||||||
|
@ -2115,7 +2121,7 @@ QuestDict = {
|
||||||
'type'),
|
'type'),
|
||||||
ToonHQ,
|
ToonHQ,
|
||||||
ToonHQ,
|
ToonHQ,
|
||||||
NA,
|
100,
|
||||||
150,
|
150,
|
||||||
DefaultDialog),
|
DefaultDialog),
|
||||||
150: (TT_TIER,
|
150: (TT_TIER,
|
||||||
|
@ -2123,7 +2129,7 @@ QuestDict = {
|
||||||
(FriendQuest,),
|
(FriendQuest,),
|
||||||
Same,
|
Same,
|
||||||
Same,
|
Same,
|
||||||
NA,
|
100,
|
||||||
175,
|
175,
|
||||||
DefaultDialog),
|
DefaultDialog),
|
||||||
160: (TT_TIER,
|
160: (TT_TIER,
|
||||||
|
|
|
@ -315,6 +315,30 @@ class RequestMinigame(MagicWord):
|
||||||
retStr += f" with difficulty {mgDiff}"
|
retStr += f" with difficulty {mgDiff}"
|
||||||
return retStr + "."
|
return retStr + "."
|
||||||
|
|
||||||
|
class Quests(MagicWord):
|
||||||
|
aliases = ["quest", "tasks", "task", "toontasks"]
|
||||||
|
desc = "Quest manupliation"
|
||||||
|
execLocation = MagicWordConfig.EXEC_LOC_SERVER
|
||||||
|
arguments = [("command", str, True), ("index", int, False, -1)]
|
||||||
|
|
||||||
|
def handleWord(self, invoker, avId, toon, *args):
|
||||||
|
command = args[0]
|
||||||
|
index = args[1]
|
||||||
|
"""
|
||||||
|
Commands:
|
||||||
|
- "finish": Finish a task (sets the progress to 1000), finishes all by default
|
||||||
|
"""
|
||||||
|
if command == "finish":
|
||||||
|
if index == -1:
|
||||||
|
self.air.questManager.completeAllQuestsMagically(toon)
|
||||||
|
return "Finished all quests."
|
||||||
|
else:
|
||||||
|
if self.air.questManager.completeQuestMagically(toon, index):
|
||||||
|
return f"Finished quest {index}."
|
||||||
|
return f"Quest {index} not found. (Hint: Quest indexes start at 0)"
|
||||||
|
else:
|
||||||
|
return "Valid commands: \"finish\""
|
||||||
|
|
||||||
# Instantiate all classes defined here to register them.
|
# Instantiate all classes defined here to register them.
|
||||||
# A bit hacky, but better than the old system
|
# A bit hacky, but better than the old system
|
||||||
for item in list(globals().values()):
|
for item in list(globals().values()):
|
||||||
|
|
|
@ -77,11 +77,12 @@ class ToontownMagicWordManagerAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
self.notify.warning('requestExecuteMagicWord: Magic Word use requested but invoker avatar is non-existent!')
|
self.notify.warning('requestExecuteMagicWord: Magic Word use requested but invoker avatar is non-existent!')
|
||||||
return
|
return
|
||||||
|
|
||||||
|
# FIXME:
|
||||||
# Same thing with the Toontorial. Magic Words are strictly forbidden here
|
# Same thing with the Toontorial. Magic Words are strictly forbidden here
|
||||||
# Tell the user they can't use it because they're in the Toontorial
|
# Tell the user they can't use it because they're in the Toontorial
|
||||||
if hasattr(self.air, 'tutorialManager') and avId in list(self.air.tutorialManager.avId2fsm.keys()):
|
# if hasattr(self.air, 'tutorialManager') and avId in list(self.air.tutorialManager.avId2fsm.keys()):
|
||||||
self.generateResponse(avId=avId, responseType="Tutorial")
|
# self.generateResponse(avId=avId, responseType="Tutorial")
|
||||||
return
|
# return
|
||||||
|
|
||||||
# Our Magic Word affectRange is either SELF (the invoker) or BOTH (invoker and a target)
|
# Our Magic Word affectRange is either SELF (the invoker) or BOTH (invoker and a target)
|
||||||
# Because of this, we should add the invoker to the target list
|
# Because of this, we should add the invoker to the target list
|
||||||
|
|
|
@ -1,5 +1,55 @@
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from otp.ai.AIBaseGlobal import *
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
|
||||||
|
|
||||||
class DistributedTutorialSuitAI(DistributedObjectAI):
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTutorialSuitAI')
|
from toontown.battle import SuitBattleGlobals
|
||||||
|
from . import DistributedSuitBaseAI
|
||||||
|
|
||||||
|
|
||||||
|
class DistributedTutorialSuitAI(DistributedSuitBaseAI.DistributedSuitBaseAI):
|
||||||
|
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||||
|
'DistributedTutorialSuitAI')
|
||||||
|
|
||||||
|
def __init__(self, air, suitPlanner):
|
||||||
|
"""__init__(air, suitPlanner)"""
|
||||||
|
DistributedSuitBaseAI.DistributedSuitBaseAI.__init__(self, air,
|
||||||
|
suitPlanner)
|
||||||
|
|
||||||
|
def delete(self):
|
||||||
|
DistributedSuitBaseAI.DistributedSuitBaseAI.delete(self)
|
||||||
|
self.ignoreAll()
|
||||||
|
|
||||||
|
def requestBattle(self, x, y, z, h, p, r):
|
||||||
|
"""requestBattle(x, y, z, h, p, r)
|
||||||
|
"""
|
||||||
|
toonId = self.air.getAvatarIdFromSender()
|
||||||
|
|
||||||
|
if self.notify.getDebug():
|
||||||
|
self.notify.debug( str( self.getDoId() ) + \
|
||||||
|
str( self.zoneId ) + \
|
||||||
|
': request battle with toon: %d' % toonId )
|
||||||
|
|
||||||
|
# Store the suit's actual pos and hpr on the client
|
||||||
|
self.confrontPos = Point3(x, y, z)
|
||||||
|
self.confrontHpr = Vec3(h, p, r)
|
||||||
|
|
||||||
|
# Request a battle from the suit planner
|
||||||
|
if (self.sp.requestBattle(self.zoneId, self, toonId)):
|
||||||
|
self.acceptOnce(self.getDeathEvent(), self._logDeath, [toonId])
|
||||||
|
if self.notify.getDebug():
|
||||||
|
self.notify.debug( "Suit %d requesting battle in zone %d" %
|
||||||
|
(self.getDoId(), self.zoneId) )
|
||||||
|
else:
|
||||||
|
# Suit tells toon to get lost
|
||||||
|
if self.notify.getDebug():
|
||||||
|
self.notify.debug('requestBattle from suit %d - denied by battle manager' % (self.getDoId()))
|
||||||
|
self.b_setBrushOff(SuitDialog.getBrushOffIndex(self.getStyleName()))
|
||||||
|
self.d_denyBattle( toonId )
|
||||||
|
|
||||||
|
def getConfrontPosHpr(self):
|
||||||
|
""" getConfrontPosHpr()
|
||||||
|
"""
|
||||||
|
return (self.confrontPos, self.confrontHpr)
|
||||||
|
|
||||||
|
def _logDeath(self, toonId):
|
||||||
|
self.air.writeServerEvent('beatFirstCog', toonId, '')
|
|
@ -3781,7 +3781,7 @@ class DistributedToonAI(DistributedPlayerAI.DistributedPlayerAI, DistributedSmoo
|
||||||
paidStatus = simbase.config.GetString('force-paid-status', 'none')
|
paidStatus = simbase.config.GetString('force-paid-status', 'none')
|
||||||
if paidStatus == 'unpaid':
|
if paidStatus == 'unpaid':
|
||||||
access = 1
|
access = 1
|
||||||
print('Setting Access %s' % access)
|
self.notify.debug('Setting Access %s' % access)
|
||||||
if access == OTPGlobals.AccessInvalid:
|
if access == OTPGlobals.AccessInvalid:
|
||||||
if not __dev__:
|
if not __dev__:
|
||||||
self.air.writeServerEvent('Setting Access', self.doId, 'setAccess not being sent by the OTP Server, changing access to unpaid')
|
self.air.writeServerEvent('Setting Access', self.doId, 'setAccess not being sent by the OTP Server, changing access to unpaid')
|
||||||
|
|
|
@ -64,7 +64,7 @@ class Street(BattlePlace.BattlePlace):
|
||||||
State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk']),
|
State.State('WaitForBattle', self.enterWaitForBattle, self.exitWaitForBattle, ['battle', 'walk']),
|
||||||
State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']),
|
State.State('battle', self.enterBattle, self.exitBattle, ['walk', 'teleportOut', 'died']),
|
||||||
State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk']),
|
State.State('doorIn', self.enterDoorIn, self.exitDoorIn, ['walk']),
|
||||||
State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk']),
|
State.State('doorOut', self.enterDoorOut, self.exitDoorOut, ['walk', 'stopped']),
|
||||||
State.State('elevatorIn', self.enterElevatorIn, self.exitElevatorIn, ['walk']),
|
State.State('elevatorIn', self.enterElevatorIn, self.exitElevatorIn, ['walk']),
|
||||||
State.State('elevator', self.enterElevator, self.exitElevator, ['walk']),
|
State.State('elevator', self.enterElevator, self.exitElevator, ['walk']),
|
||||||
State.State('trialerFA', self.enterTrialerFA, self.exitTrialerFA, ['trialerFAReject', 'DFA']),
|
State.State('trialerFA', self.enterTrialerFA, self.exitTrialerFA, ['trialerFAReject', 'DFA']),
|
||||||
|
|
|
@ -1,5 +1,21 @@
|
||||||
|
from toontown.battle import DistributedBattleAI
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
|
||||||
|
|
||||||
class DistributedBattleTutorialAI(DistributedObjectAI):
|
class DistributedBattleTutorialAI(DistributedBattleAI.DistributedBattleAI):
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleTutorialAI')
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleTutorialAI')
|
||||||
|
|
||||||
|
def __init__(self, air, battleMgr, pos, suit, toonId, zoneId,
|
||||||
|
finishCallback=None, maxSuits=4, interactivePropTrackBonus = -1):
|
||||||
|
"""__init__(air, battleMgr, pos, suit, toonId, zoneId,
|
||||||
|
finishCallback, maxSuits)
|
||||||
|
"""
|
||||||
|
DistributedBattleAI.DistributedBattleAI.__init__(
|
||||||
|
self, air, battleMgr, pos, suit, toonId, zoneId,
|
||||||
|
finishCallback, maxSuits, tutorialFlag=1)
|
||||||
|
|
||||||
|
# There is no timer in the tutorial... The reward movie is random length.
|
||||||
|
def startRewardTimer(self):
|
||||||
|
pass
|
||||||
|
|
||||||
|
#def handleRewardDone(self):
|
||||||
|
# DistributedBattleAI.DistributedBattleAI.handleRewardDone(self)
|
||||||
|
|
74
toontown/tutorial/SuitPlannerTutorialAI.py
Normal file
74
toontown/tutorial/SuitPlannerTutorialAI.py
Normal file
|
@ -0,0 +1,74 @@
|
||||||
|
""" SuitPlannerTutorial module: contains the SuitPlannerTutorial class
|
||||||
|
which handles management of the suit you will fight during the
|
||||||
|
tutorial."""
|
||||||
|
|
||||||
|
from otp.ai.AIBaseGlobal import *
|
||||||
|
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from toontown.suit import DistributedTutorialSuitAI
|
||||||
|
from . import TutorialBattleManagerAI
|
||||||
|
|
||||||
|
class SuitPlannerTutorialAI:
|
||||||
|
"""
|
||||||
|
SuitPlannerTutorialAI: manages the single suit that you fight during
|
||||||
|
the tutorial.
|
||||||
|
"""
|
||||||
|
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory(
|
||||||
|
'SuitPlannerTutorialAI')
|
||||||
|
|
||||||
|
def __init__(self, air, zoneId, battleOverCallback):
|
||||||
|
# Store these things
|
||||||
|
self.zoneId = zoneId
|
||||||
|
self.air = air
|
||||||
|
self.battle = None
|
||||||
|
# This callback will be used to open the HQ doors when the
|
||||||
|
# battle is over.
|
||||||
|
self.battleOverCallback = battleOverCallback
|
||||||
|
|
||||||
|
# Create a battle manager
|
||||||
|
self.battleMgr = TutorialBattleManagerAI.TutorialBattleManagerAI(
|
||||||
|
self.air)
|
||||||
|
|
||||||
|
# Create a flunky
|
||||||
|
newSuit = DistributedTutorialSuitAI.DistributedTutorialSuitAI(self.air, self)
|
||||||
|
newSuit.setupSuitDNA(1, 1, "c")
|
||||||
|
# This is a special tutorial path state
|
||||||
|
newSuit.generateWithRequired(self.zoneId)
|
||||||
|
self.suit = newSuit
|
||||||
|
|
||||||
|
def cleanup(self):
|
||||||
|
self.zoneId = None
|
||||||
|
self.air = None
|
||||||
|
if self.suit:
|
||||||
|
self.suit.requestDelete()
|
||||||
|
self.suit = None
|
||||||
|
if self.battle:
|
||||||
|
#self.battle.requestDelete()
|
||||||
|
#RAU made to kill the mem leak when you close the window in the middle of the battle tutorial
|
||||||
|
cellId = self.battle.battleCellId
|
||||||
|
battleMgr = self.battle.battleMgr
|
||||||
|
if cellId in battleMgr.cellId2battle:
|
||||||
|
battleMgr.destroy(self.battle)
|
||||||
|
|
||||||
|
self.battle = None
|
||||||
|
|
||||||
|
def getDoId(self):
|
||||||
|
# This is here because the suit expects the suit planner to be
|
||||||
|
# a distributed object, if it has a suit planner. We want it to
|
||||||
|
# have a suit planner, but not a distributed one, so we return
|
||||||
|
# 0 when asked what our DoId is. Kind of hackful, I guess.
|
||||||
|
return 0
|
||||||
|
|
||||||
|
def requestBattle(self, zoneId, suit, toonId):
|
||||||
|
# 70, 20, 0 is a battle cell position that I just made up.
|
||||||
|
self.battle = self.battleMgr.newBattle(
|
||||||
|
zoneId, zoneId, Vec3(35, 20, 0),
|
||||||
|
suit, toonId,
|
||||||
|
finishCallback=self.battleOverCallback)
|
||||||
|
return 1
|
||||||
|
|
||||||
|
def removeSuit(self, suit):
|
||||||
|
# Get rid of the suit.
|
||||||
|
suit.requestDelete()
|
||||||
|
self.suit = None
|
11
toontown/tutorial/TutorialBattleManagerAI.py
Normal file
11
toontown/tutorial/TutorialBattleManagerAI.py
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
from toontown.battle import BattleManagerAI
|
||||||
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
|
from . import DistributedBattleTutorialAI
|
||||||
|
|
||||||
|
class TutorialBattleManagerAI(BattleManagerAI.BattleManagerAI):
|
||||||
|
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory('TutorialBattleManagerAI')
|
||||||
|
|
||||||
|
def __init__(self, air):
|
||||||
|
BattleManagerAI.BattleManagerAI.__init__(self, air)
|
||||||
|
self.battleConstructor = DistributedBattleTutorialAI.DistributedBattleTutorialAI
|
|
@ -1,5 +1,322 @@
|
||||||
|
from otp.ai.AIBaseGlobal import *
|
||||||
|
from panda3d.core import *
|
||||||
|
from panda3d.toontown import *
|
||||||
|
from direct.distributed import DistributedObjectAI
|
||||||
from direct.directnotify import DirectNotifyGlobal
|
from direct.directnotify import DirectNotifyGlobal
|
||||||
from direct.distributed.DistributedObjectAI import DistributedObjectAI
|
from toontown.building import TutorialBuildingAI
|
||||||
|
from toontown.building import TutorialHQBuildingAI
|
||||||
|
from . import SuitPlannerTutorialAI
|
||||||
|
from toontown.toonbase import ToontownBattleGlobals
|
||||||
|
from toontown.toon import NPCToons
|
||||||
|
from toontown.ai import BlackCatHolidayMgrAI
|
||||||
|
from toontown.ai import DistributedBlackCatMgrAI
|
||||||
|
|
||||||
|
class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI):
|
||||||
|
notify = DirectNotifyGlobal.directNotify.newCategory("TutorialManagerAI")
|
||||||
|
|
||||||
|
# how many seconds do we wait for the toon to appear on AI before we
|
||||||
|
# nuke his skip tutorial request
|
||||||
|
WaitTimeForSkipTutorial = 5.0
|
||||||
|
|
||||||
|
def __init__(self, air):
|
||||||
|
DistributedObjectAI.DistributedObjectAI.__init__(self, air)
|
||||||
|
# This is a dictionary of all the players who are currently in
|
||||||
|
# tutorials. We need to create things when someone requests
|
||||||
|
# a tutorial, and destroy them when they leave.
|
||||||
|
self.playerDict = {}
|
||||||
|
|
||||||
|
# There are only two blocks in the tutorial. One for the gag shop
|
||||||
|
# building, and one for the Toon HQ. If there aren't, something
|
||||||
|
# is wrong.
|
||||||
|
self.dnaStore = DNAStorage()
|
||||||
|
|
||||||
|
dnaFile = simbase.air.lookupDNAFileName("tutorial_street.dna")
|
||||||
|
self.air.loadDNAFileAI(self.dnaStore, dnaFile)
|
||||||
|
numBlocks = self.dnaStore.getNumBlockNumbers()
|
||||||
|
assert numBlocks == 2
|
||||||
|
# Assumption: the only block that isn't an HQ is the gag shop block.
|
||||||
|
self.hqBlock = None
|
||||||
|
self.gagBlock = None
|
||||||
|
for blockIndex in range (0, numBlocks):
|
||||||
|
blockNumber = self.dnaStore.getBlockNumberAt(blockIndex)
|
||||||
|
buildingType = self.dnaStore.getBlockBuildingType(blockNumber)
|
||||||
|
if (buildingType == 'hq'):
|
||||||
|
self.hqBlock = blockNumber
|
||||||
|
else:
|
||||||
|
self.gagBlock = blockNumber
|
||||||
|
|
||||||
|
assert self.hqBlock and self.gagBlock
|
||||||
|
|
||||||
|
# key is avId, value is real time when the request was made
|
||||||
|
self.avIdsRequestingSkip = {}
|
||||||
|
self.accept("avatarEntered", self.waitingToonEntered )
|
||||||
|
|
||||||
|
return None
|
||||||
|
|
||||||
|
def requestTutorial(self):
|
||||||
|
# TODO: possible security breach: what if client is repeatedly
|
||||||
|
# requesting tutorial? can client request tutorial from playground?
|
||||||
|
# can client request tutorial if hp is at least 16? How do we
|
||||||
|
# handle these cases?
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
# Handle unexpected exits
|
||||||
|
self.acceptOnce(self.air.getAvatarExitEvent(avId),
|
||||||
|
self.__handleUnexpectedExit,
|
||||||
|
extraArgs=[avId])
|
||||||
|
# allocate tutorial objects and zones
|
||||||
|
zoneDict = self.__createTutorial(avId)
|
||||||
|
# Tell the player to enter the zone
|
||||||
|
self.d_enterTutorial(avId,
|
||||||
|
zoneDict["branchZone"],
|
||||||
|
zoneDict["streetZone"],
|
||||||
|
zoneDict["shopZone"],
|
||||||
|
zoneDict["hqZone"]
|
||||||
|
)
|
||||||
|
self.air.writeServerEvent('startedTutorial', avId, '')
|
||||||
|
|
||||||
|
def toonArrived(self):
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
# Make sure the avatar exists
|
||||||
|
av = self.air.doId2do.get(avId)
|
||||||
|
# Clear out the avatar's quests, hp, inventory, and everything else in case
|
||||||
|
# he made it half way through the tutorial last time.
|
||||||
|
if av:
|
||||||
|
# No quests
|
||||||
|
av.b_setQuests([])
|
||||||
|
av.b_setQuestHistory([])
|
||||||
|
av.b_setRewardHistory(0, [])
|
||||||
|
av.b_setQuestCarryLimit(1)
|
||||||
|
# Starting HP
|
||||||
|
av.b_setMaxHp(15)
|
||||||
|
av.b_setHp(15)
|
||||||
|
# No exp
|
||||||
|
av.experience.zeroOutExp()
|
||||||
|
av.d_setExperience(av.experience.makeNetString())
|
||||||
|
# One cupcake and one squirting flower
|
||||||
|
av.inventory.zeroInv()
|
||||||
|
av.inventory.addItem(ToontownBattleGlobals.THROW_TRACK, 0)
|
||||||
|
av.inventory.addItem(ToontownBattleGlobals.SQUIRT_TRACK, 0)
|
||||||
|
av.d_setInventory(av.inventory.makeNetString())
|
||||||
|
# No cogs defeated
|
||||||
|
av.b_setCogStatus([1] * 32)
|
||||||
|
av.b_setCogCount([0] * 32)
|
||||||
|
return
|
||||||
|
|
||||||
|
def allDone(self):
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
# No need to worry further about unexpected exits
|
||||||
|
self.ignore(self.air.getAvatarExitEvent(avId))
|
||||||
|
# Make sure the avatar exists
|
||||||
|
av = self.air.doId2do.get(avId)
|
||||||
|
if av:
|
||||||
|
self.air.writeServerEvent('finishedTutorial', avId, '')
|
||||||
|
av.b_setTutorialAck(1)
|
||||||
|
self.__destroyTutorial(avId)
|
||||||
|
else:
|
||||||
|
self.notify.warning(
|
||||||
|
"Toon " +
|
||||||
|
str(avId) +
|
||||||
|
" isn't here, but just finished a tutorial. " +
|
||||||
|
"I will ignore this."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
def __createTutorial(self, avId):
|
||||||
|
if self.playerDict.get(avId):
|
||||||
|
self.notify.warning(str(avId) + " is already in the playerDict!")
|
||||||
|
|
||||||
|
branchZone = self.air.allocateZone()
|
||||||
|
streetZone = self.air.allocateZone()
|
||||||
|
shopZone = self.air.allocateZone()
|
||||||
|
hqZone = self.air.allocateZone()
|
||||||
|
# Create a building object
|
||||||
|
building = TutorialBuildingAI.TutorialBuildingAI(self.air,
|
||||||
|
streetZone,
|
||||||
|
shopZone,
|
||||||
|
self.gagBlock)
|
||||||
|
# Create an HQ object
|
||||||
|
hqBuilding = TutorialHQBuildingAI.TutorialHQBuildingAI(self.air,
|
||||||
|
streetZone,
|
||||||
|
hqZone,
|
||||||
|
self.hqBlock)
|
||||||
|
|
||||||
|
def battleOverCallback(zoneId):
|
||||||
|
hqBuilding.battleOverCallback()
|
||||||
|
building.battleOverCallback()
|
||||||
|
|
||||||
|
# Create a suit planner
|
||||||
|
suitPlanner = SuitPlannerTutorialAI.SuitPlannerTutorialAI(
|
||||||
|
self.air,
|
||||||
|
streetZone,
|
||||||
|
battleOverCallback)
|
||||||
|
|
||||||
|
# Create the NPC blocking the tunnel to the playground
|
||||||
|
blockerNPC = NPCToons.createNPC(self.air, 20001, NPCToons.NPCToonDict[20001], streetZone,
|
||||||
|
questCallback=self.__handleBlockDone)
|
||||||
|
blockerNPC.setTutorial(1)
|
||||||
|
|
||||||
|
# is the black cat holiday enabled?
|
||||||
|
blackCatMgr = None
|
||||||
|
if bboard.has(BlackCatHolidayMgrAI.BlackCatHolidayMgrAI.PostName):
|
||||||
|
blackCatMgr = DistributedBlackCatMgrAI.DistributedBlackCatMgrAI(
|
||||||
|
self.air, avId)
|
||||||
|
blackCatMgr.generateWithRequired(streetZone)
|
||||||
|
|
||||||
|
zoneDict={"branchZone" : branchZone,
|
||||||
|
"streetZone" : streetZone,
|
||||||
|
"shopZone" : shopZone,
|
||||||
|
"hqZone" : hqZone,
|
||||||
|
"building" : building,
|
||||||
|
"hqBuilding" : hqBuilding,
|
||||||
|
"suitPlanner" : suitPlanner,
|
||||||
|
"blockerNPC" : blockerNPC,
|
||||||
|
"blackCatMgr" : blackCatMgr,
|
||||||
|
}
|
||||||
|
self.playerDict[avId] = zoneDict
|
||||||
|
return zoneDict
|
||||||
|
|
||||||
|
def __handleBlockDone(self):
|
||||||
|
return None
|
||||||
|
|
||||||
|
def __destroyTutorial(self, avId):
|
||||||
|
zoneDict = self.playerDict.get(avId)
|
||||||
|
if zoneDict:
|
||||||
|
zoneDict["building"].cleanup()
|
||||||
|
zoneDict["hqBuilding"].cleanup()
|
||||||
|
zoneDict["blockerNPC"].requestDelete()
|
||||||
|
if zoneDict["blackCatMgr"]:
|
||||||
|
zoneDict["blackCatMgr"].requestDelete()
|
||||||
|
self.air.deallocateZone(zoneDict["branchZone"])
|
||||||
|
self.air.deallocateZone(zoneDict["streetZone"])
|
||||||
|
self.air.deallocateZone(zoneDict["shopZone"])
|
||||||
|
self.air.deallocateZone(zoneDict["hqZone"])
|
||||||
|
zoneDict["suitPlanner"].cleanup()
|
||||||
|
del self.playerDict[avId]
|
||||||
|
else:
|
||||||
|
self.notify.warning("Tried to deallocate zones for " +
|
||||||
|
str(avId) +
|
||||||
|
" but none were present in playerDict.")
|
||||||
|
|
||||||
|
def rejectTutorial(self):
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
# Make sure the avatar exists
|
||||||
|
av = self.air.doId2do.get(avId)
|
||||||
|
if av:
|
||||||
|
# Acknowlege that the player has seen a tutorial
|
||||||
|
self.air.writeServerEvent('finishedTutorial', avId, '')
|
||||||
|
av.b_setTutorialAck(1)
|
||||||
|
|
||||||
|
self.sendUpdateToAvatarId(avId, "skipTutorialResponse", [1])
|
||||||
|
else:
|
||||||
|
self.notify.warning(
|
||||||
|
"Toon " +
|
||||||
|
str(avId) +
|
||||||
|
" isn't here, but just rejected a tutorial. " +
|
||||||
|
"I will ignore this."
|
||||||
|
)
|
||||||
|
return
|
||||||
|
|
||||||
|
def respondToSkipTutorial(self, avId, av):
|
||||||
|
"""Reply to the client if we let him skip the tutorial."""
|
||||||
|
self.notify.debugStateCall(self)
|
||||||
|
assert avId
|
||||||
|
assert av
|
||||||
|
response = 1
|
||||||
|
if av:
|
||||||
|
if av.tutorialAck:
|
||||||
|
self.air.writeServerEvent('suspicious', avId, 'requesting skip tutorial, but tutorialAck is 1')
|
||||||
|
response = 0
|
||||||
|
|
||||||
|
if av and response:
|
||||||
|
# Acknowlege that the player has seen a tutorial
|
||||||
|
self.air.writeServerEvent('skippedTutorial', avId, '')
|
||||||
|
av.b_setTutorialAck(1)
|
||||||
|
# these values were taken by running a real tutorial
|
||||||
|
self.air.questManager.assignQuest(avId,
|
||||||
|
20000,
|
||||||
|
101,
|
||||||
|
100,
|
||||||
|
1000,
|
||||||
|
1
|
||||||
|
)
|
||||||
|
|
||||||
|
self.air.questManager.completeAllQuestsMagically(av)
|
||||||
|
av.removeQuest(101)
|
||||||
|
self.air.questManager.assignQuest(avId,
|
||||||
|
1000,
|
||||||
|
110,
|
||||||
|
2,
|
||||||
|
1000,
|
||||||
|
0
|
||||||
|
)
|
||||||
|
self.air.questManager.completeAllQuestsMagically(av)
|
||||||
|
|
||||||
|
# do whatever needs to be done to make his quest state good
|
||||||
|
elif av:
|
||||||
|
self.notify.debug("%s requestedSkipTutorial, but tutorialAck is 1")
|
||||||
|
else:
|
||||||
|
response = 0
|
||||||
|
self.notify.warning(
|
||||||
|
"Toon " +
|
||||||
|
str(avId) +
|
||||||
|
" isn't here, but requested to skip tutorial. " +
|
||||||
|
"I will ignore this."
|
||||||
|
)
|
||||||
|
self.sendUpdateToAvatarId(avId, "skipTutorialResponse", [response])
|
||||||
|
return
|
||||||
|
|
||||||
|
def waitingToonEntered(self, av):
|
||||||
|
"""Check if the avatar is someone who's requested to skip, then proceed accordingly."""
|
||||||
|
avId = av.doId
|
||||||
|
if avId in self.avIdsRequestingSkip:
|
||||||
|
requestTime = self.avIdsRequestingSkip[avId]
|
||||||
|
|
||||||
|
curTime = globalClock.getFrameTime()
|
||||||
|
if (curTime - requestTime) <= self.WaitTimeForSkipTutorial:
|
||||||
|
self.respondToSkipTutorial(avId, av)
|
||||||
|
else:
|
||||||
|
self.notify.warning("waited too long for toon %d responding no to skip tutorial request" % avId)
|
||||||
|
self.sendUpdateToAvatarId(avId, "skipTutorialResponse", [0])
|
||||||
|
del self.avIdsRequestingSkip[avId]
|
||||||
|
self.removeTask("skipTutorialToon-%d" % avId)
|
||||||
|
|
||||||
|
|
||||||
|
def waitForToonToEnter(self,avId):
|
||||||
|
"""Mark our toon as requesting to skip, and start a task to timeout for it."""
|
||||||
|
self.notify.debugStateCall(self)
|
||||||
|
self.avIdsRequestingSkip[avId] = globalClock.getFrameTime()
|
||||||
|
self.doMethodLater(self.WaitTimeForSkipTutorial, self.didNotGetToon, "skipTutorialToon-%d" % avId, [avId])
|
||||||
|
|
||||||
|
def didNotGetToon(self, avId):
|
||||||
|
"""Just say no since the AI didn't get it."""
|
||||||
|
self.notify.debugStateCall(self)
|
||||||
|
if avId in self.avIdsRequestingSkip:
|
||||||
|
del self.avIdsRequestingSkip[avId]
|
||||||
|
self.sendUpdateToAvatarId(avId, "skipTutorialResponse", [0])
|
||||||
|
return Task.done
|
||||||
|
|
||||||
|
def requestSkipTutorial(self):
|
||||||
|
"""We are requesting to skip tutorial, add other quest history to be consistent."""
|
||||||
|
self.notify.debugStateCall(self)
|
||||||
|
avId = self.air.getAvatarIdFromSender()
|
||||||
|
# Make sure the avatar exists
|
||||||
|
av = self.air.doId2do.get(avId)
|
||||||
|
if av:
|
||||||
|
self.respondToSkipTutorial(avId,av)
|
||||||
|
else:
|
||||||
|
self.waitForToonToEnter(avId)
|
||||||
|
|
||||||
|
def d_enterTutorial(self, avId, branchZone, streetZone, shopZone, hqZone):
|
||||||
|
self.sendUpdateToAvatarId(avId, "enterTutorial", [branchZone,
|
||||||
|
streetZone,
|
||||||
|
shopZone,
|
||||||
|
hqZone])
|
||||||
|
return
|
||||||
|
|
||||||
|
def __handleUnexpectedExit(self, avId):
|
||||||
|
self.notify.warning("Avatar: " + str(avId) +
|
||||||
|
" has exited unexpectedly")
|
||||||
|
self.__destroyTutorial(avId)
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
class TutorialManagerAI(DistributedObjectAI):
|
|
||||||
notify = DirectNotifyGlobal.directNotify.newCategory('TutorialManagerAI')
|
|
||||||
|
|
Loading…
Reference in a new issue