From e22fee8fa8bececdfeabd93489b7d5dadff16303 Mon Sep 17 00:00:00 2001 From: Leo <38441309+leotheepicdev@users.noreply.github.com> Date: Sat, 31 Jan 2026 17:22:21 -0500 Subject: [PATCH] tutorial: Cleanup and fix floating Flunky in battle The following has been cleaned up: - Import statements - Whitespace - Small unnecessary comments Futhermore, I have added Anesidora documentation to the Tutorial files that did not have them. --- .../building/DistributedTutorialInterior.py | 185 +++++++++++++----- .../building/DistributedTutorialInteriorAI.py | 17 +- toontown/building/TutorialHQBuildingAI.py | 39 ++-- .../tutorial/DistributedBattleTutorial.py | 10 +- .../tutorial/DistributedBattleTutorialAI.py | 15 +- toontown/tutorial/SuitPlannerTutorialAI.py | 18 +- toontown/tutorial/TutorialBattleManagerAI.py | 6 +- toontown/tutorial/TutorialForceAcknowledge.py | 13 +- toontown/tutorial/TutorialManager.py | 52 +++-- toontown/tutorial/TutorialManagerAI.py | 80 +++----- 10 files changed, 251 insertions(+), 184 deletions(-) diff --git a/toontown/building/DistributedTutorialInterior.py b/toontown/building/DistributedTutorialInterior.py index 2d8b1d1..953aa41 100644 --- a/toontown/building/DistributedTutorialInterior.py +++ b/toontown/building/DistributedTutorialInterior.py @@ -1,26 +1,19 @@ -from toontown.toonbase.ToonBaseGlobal import * -from pandac.PandaModules import * +import random +from panda3d.core import Point3, Vec3 +from panda3d.toontown import DNADoor from direct.interval.IntervalGlobal import * -from direct.distributed.ClockDelta import * -from toontown.toonbase import ToontownGlobals -from . import ToonInterior + from direct.directnotify import DirectNotifyGlobal from direct.distributed import DistributedObject -import random from . import ToonInteriorColors from toontown.hood import ZoneUtil -from toontown.char import Char from toontown.suit import SuitDNA from toontown.suit import Suit from toontown.quest import QuestParser + class DistributedTutorialInterior(DistributedObject.DistributedObject): - - def __init__(self, cr): - DistributedObject.DistributedObject.__init__(self, cr) - - def generate(self): - DistributedObject.DistributedObject.generate(self) + notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTutorialInterior') def announceGenerate(self): DistributedObject.DistributedObject.announceGenerate(self) @@ -39,104 +32,181 @@ class DistributedTutorialInterior(DistributedObject.DistributedObject): del self.suitWalkTrack self.suit.delete() del self.suit - self.ignore('enterTutotialInterior') + self.ignore("enterTutotialInterior") DistributedObject.DistributedObject.disable(self) - def delete(self): - DistributedObject.DistributedObject.delete(self) - def randomDNAItem(self, category, findFunc): codeCount = self.dnaStore.getNumCatalogCodes(category) index = self.randomGenerator.randint(0, codeCount - 1) code = self.dnaStore.getCatalogCode(category, index) + # findFunc will probably be findNode or findTexture return findFunc(code) def replaceRandomInModel(self, model): - baseTag = 'random_' - npc = model.findAllMatches('**/' + baseTag + '???_*') + """Replace named nodes with random items. + Here are the name Here is + prefixes that are what they + affected: do: + + random_mox_ change the Model Only. + random_mcx_ change the Model and the Color. + random_mrx_ change the Model and Recurse. + random_tox_ change the Texture Only. + random_tcx_ change the Texture and the Color. + + x is simply a uniquifying integer because Multigen will not + let you have multiple nodes with the same name + + """ + baseTag = "random_" + npc = model.findAllMatches("**/" + baseTag + "???_*") for i in range(npc.getNumPaths()): np = npc.getPath(i) name = np.getName() + b = len(baseTag) category = name[b + 4:] key1 = name[b] key2 = name[b + 1] - if key1 == 'm': + + assert (key1 in ["m", "t"]) + assert (key2 in ["c", "o", "r"]) + if key1 == "m": + # ...model. model = self.randomDNAItem(category, self.dnaStore.findNode) + assert (not model.isEmpty()) newNP = model.copyTo(np) + # room has collisions already: remove collisions from models c = render.findAllMatches('**/collision') c.stash() - if key2 == 'r': + if key2 == "r": self.replaceRandomInModel(newNP) - elif key1 == 't': + elif key1 == "t": + # ...texture. texture = self.randomDNAItem(category, self.dnaStore.findTexture) + assert (texture) np.setTexture(texture, 100) newNP = np - if key2 == 'c': - if category == 'TI_wallpaper' or category == 'TI_wallpaper_border': + if key2 == "c": + if (category == "TI_wallpaper") or (category == "TI_wallpaper_border"): self.randomGenerator.seed(self.zoneId) - newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + newNP.setColorScale( + self.randomGenerator.choice(self.colors[category])) else: - newNP.setColorScale(self.randomGenerator.choice(self.colors[category])) + newNP.setColorScale( + self.randomGenerator.choice(self.colors[category])) def setup(self): self.dnaStore = base.cr.playGame.dnaStore self.randomGenerator = random.Random() + + # The math here is a little arbitrary. I'm trying to get a + # substantially different seed for each zondId, even on the + # same street. But we don't want to weigh to much on the + # block number, because we want the same block number on + # different streets to be different. + # Here we use the block number and a little of the branchId: + # seedX=self.zoneId&0x00ff + # Here we're using only the branchId: + # seedY=self.zoneId/100 + # Here we're using only the block number: + # seedZ=256-int(self.block) + self.randomGenerator.seed(self.zoneId) - self.interior = loader.loadModel('phase_3.5/models/modules/toon_interior_tutorial') - self.interior.reparentTo(render) - dnaStore = DNAStorage() - node = loader.loadDNAFile(self.cr.playGame.hood.dnaStore, 'phase_3.5/dna/tutorial_street.dna') - self.street = render.attachNewNode(node) + + self.interior = loader.loadModel("phase_3.5/models/modules/toon_interior_tutorial") + self.interior.reparentTo(base.render) + node = loader.loadDNAFile(self.cr.playGame.hood.dnaStore, "phase_3.5/dna/tutorial_street.dna") + self.street = base.render.attachNewNode(node) self.street.flattenMedium() self.street.setPosHpr(-17, 42, -0.5, 180, 0, 0) - self.street.find('**/tb2:toon_landmark_TT_A1_DNARoot').stash() - self.street.find('**/tb1:toon_landmark_hqTT_DNARoot/**/door_flat_0').stash() - self.street.findAllMatches('**/+CollisionNode').stash() - self.skyFile = 'phase_3.5/models/props/TT_sky' + # Get rid of the building we are in + self.street.find("**/tb2:toon_landmark_TT_A1_DNARoot").stash() + # Get rid of the flashing doors on the HQ building + self.street.find("**/tb1:toon_landmark_hqTT_DNARoot/**/door_flat_0").stash() + # Get rid of collisions because we do not need them and they get in the way + self.street.findAllMatches("**/+CollisionNode").stash() + self.skyFile = "phase_3.5/models/props/TT_sky" self.sky = loader.loadModel(self.skyFile) self.sky.setScale(0.8) - self.sky.reparentTo(render) + # Parent the sky to our camera, the task will counter rotate it + self.sky.reparentTo(base.render) + # Turn off depth tests on the sky because as the cloud layers interpenetrate + # we do not want to see the polys cutoff. Since there is nothing behing them + # we can get away with this. self.sky.setDepthTest(0) self.sky.setDepthWrite(0) - self.sky.setBin('background', 100) - self.sky.find('**/Sky').reparentTo(self.sky, -1) + self.sky.setBin("background", 100) + # Make sure they are drawn in the correct order in the hierarchy + # The sky should be first, then the clouds + self.sky.find("**/Sky").reparentTo(self.sky, -1) + + # Load a color dictionary for this hood: hoodId = ZoneUtil.getCanonicalHoodId(self.zoneId) self.colors = ToonInteriorColors.colors[hoodId] + # Replace all the "random_xxx_" nodes: self.replaceRandomInModel(self.interior) - doorModelName = 'door_double_round_ul' - if doorModelName[-1:] == 'r': - doorModelName = doorModelName[:-1] + 'l' + + # Door: + doorModelName = "door_double_round_ul" # hack zzzzzzz + # Switch leaning of the door: + if doorModelName[-1:] == "r": + doorModelName = doorModelName[:-1] + "l" else: - doorModelName = doorModelName[:-1] + 'r' + doorModelName = doorModelName[:-1] + "r" door = self.dnaStore.findNode(doorModelName) - door_origin = render.find('**/door_origin;+s') + # Determine where should we put the door: + door_origin = base.render.find("**/door_origin;+s") doorNP = door.copyTo(door_origin) + assert (not doorNP.isEmpty()) + assert (not door_origin.isEmpty()) + # The rooms are too small for doors: door_origin.setScale(0.8, 0.8, 0.8) + # Move the origin away from the wall so it does not shimmer + # We do this instead of decals door_origin.setPos(door_origin, 0, -0.025, 0) - color = self.randomGenerator.choice(self.colors['TI_door']) - DNADoor.setupDoor(doorNP, self.interior, door_origin, self.dnaStore, str(self.block), color) - doorFrame = doorNP.find('door_*_flat') + color = self.randomGenerator.choice(self.colors["TI_door"]) + DNADoor.setupDoor(doorNP, + self.interior, door_origin, + self.dnaStore, + str(self.block), color) + # Setting the wallpaper texture with a priority overrides + # the door texture, if it's decalled. So, we're going to + # move it out from the decal, and float it in front of + # the wall: + doorFrame = doorNP.find("door_*_flat") doorFrame.wrtReparentTo(self.interior) doorFrame.setColor(color) + del self.colors del self.dnaStore del self.randomGenerator + + # Get rid of any transitions and extra nodes self.interior.flattenMedium() - npcOrigin = self.interior.find('**/npc_origin_' + repr((self.npc.posIndex))) + + # Ok, this is a hack, but I'm tired of this freakin tutorial. + # The problem is the interior must be created first so the npc can find the origin + # of where to stand, but in this case the npc must be created first so the tutorial + # can get a handle on him. Instead, I'll let the npc be created first which means + # he will not find his origin. We'll just do that work here again. + npcOrigin = self.interior.find(f"**/npc_origin_{self.npc.posIndex}") + # Now he's no longer parented to render, but no one minds. if not npcOrigin.isEmpty(): self.npc.reparentTo(npcOrigin) self.npc.clearMat() + self.createSuit() - self.mickeyMovie = QuestParser.NPCMoviePlayer('tutorial_mickey', base.localAvatar, self.npc) + + self.mickeyMovie = QuestParser.NPCMoviePlayer("tutorial_mickey", base.localAvatar, self.npc) place = base.cr.playGame.getPlace() if place and hasattr(place, 'fsm') and place.fsm.getCurrentState().getName(): self.notify.info('Tutorial movie: Place ready.') self.playMovie() else: - self.notify.info('Tutorial movie: Waiting for place=%s, has fsm=%s' % (place, hasattr(place, 'fsm'))) + self.notify.info(f'Tutorial movie: Waiting for place={place}, has fsm={hasattr(place, "fsm")}') if hasattr(place, 'fsm'): - self.notify.info('Tutorial movie: place state=%s' % place.fsm.getCurrentState().getName()) + self.notify.info(f'Tutorial movie: place state={place.fsm.getCurrentState().getName()}') self.acceptOnce('enterTutorialInterior', self.playMovie) def playMovie(self): @@ -144,6 +214,7 @@ class DistributedTutorialInterior(DistributedObject.DistributedObject): self.mickeyMovie.play() def createSuit(self): + # Create a suit self.suit = Suit.Suit() suitDNA = SuitDNA.SuitDNA() suitDNA.newSuit('f') @@ -151,7 +222,19 @@ class DistributedTutorialInterior(DistributedObject.DistributedObject): self.suit.loop('neutral') self.suit.setPosHpr(-20, 8, 0, 0, 0, 0) self.suit.reparentTo(self.interior) - self.suitWalkTrack = Sequence(self.suit.hprInterval(0.1, Vec3(0, 0, 0)), Func(self.suit.loop, 'walk'), self.suit.posInterval(2, Point3(-20, 20, 0)), Func(self.suit.loop, 'neutral'), Wait(1.0), self.suit.hprInterval(0.1, Vec3(180, 0, 0)), Func(self.suit.loop, 'walk'), self.suit.posInterval(2, Point3(-20, 10, 0)), Func(self.suit.loop, 'neutral'), Wait(1.0)) + + self.suitWalkTrack = Sequence( + self.suit.hprInterval(0.1, Vec3(0, 0, 0)), + Func(self.suit.loop, 'walk'), + self.suit.posInterval(2, Point3(-20, 20, 0)), + Func(self.suit.loop, 'neutral'), + Wait(1.0), + self.suit.hprInterval(0.1, Vec3(180, 0, 0)), + Func(self.suit.loop, 'walk'), + self.suit.posInterval(2, Point3(-20, 10, 0)), + Func(self.suit.loop, 'neutral'), + Wait(1.0), + ) self.suitWalkTrack.loop() def setZoneIdAndBlock(self, zoneId, block): diff --git a/toontown/building/DistributedTutorialInteriorAI.py b/toontown/building/DistributedTutorialInteriorAI.py index 77bbddf..2d7136f 100644 --- a/toontown/building/DistributedTutorialInteriorAI.py +++ b/toontown/building/DistributedTutorialInteriorAI.py @@ -1,11 +1,8 @@ -from toontown.toonbase.ToontownGlobals import * -from otp.ai.AIBaseGlobal import * -from direct.distributed.ClockDelta import * - from direct.directnotify import DirectNotifyGlobal from direct.distributed import DistributedObjectAI from toontown.toon import NPCToons + class DistributedTutorialInteriorAI(DistributedObjectAI.DistributedObjectAI): if __debug__: @@ -13,14 +10,12 @@ class DistributedTutorialInteriorAI(DistributedObjectAI.DistributedObjectAI): 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.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) @@ -32,9 +27,9 @@ class DistributedTutorialInteriorAI(DistributedObjectAI.DistributedObjectAI): del self.npcs del self.building DistributedObjectAI.DistributedObjectAI.delete(self) - + def getZoneIdAndBlock(self): return [self.zoneId, self.block] - + def getTutorialNpcId(self): return self.tutorialNpcId diff --git a/toontown/building/TutorialHQBuildingAI.py b/toontown/building/TutorialHQBuildingAI.py index 0f57497..5cca631 100644 --- a/toontown/building/TutorialHQBuildingAI.py +++ b/toontown/building/TutorialHQBuildingAI.py @@ -1,5 +1,3 @@ -from pandac.PandaModules import * -from direct.directnotify import DirectNotifyGlobal from toontown.building import DistributedDoorAI from toontown.building import DistributedHQInteriorAI from toontown.building import FADoorCodes @@ -8,17 +6,19 @@ 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): @@ -34,17 +34,17 @@ class TutorialHQBuildingAI: del self.insideDoor0 self.insideDoor1.requestDelete() del self.insideDoor1 - return def setup(self, blockNumber): # The interior - self.interior=DistributedHQInteriorAI.DistributedHQInteriorAI( + 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) + 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) @@ -53,24 +53,24 @@ class TutorialHQBuildingAI: self.interior.generateWithRequired(self.interiorZone) # Outside door 0. Locked til you defeat the Flunky: - door0=DistributedDoorAI.DistributedDoorAI( + door0 = DistributedDoorAI.DistributedDoorAI( self.air, blockNumber, DoorTypes.EXT_HQ, doorIndex=0, lockValue=FADoorCodes.DEFEAT_FLUNKY_HQ) # Outside door 1. Always locked. - door1=DistributedDoorAI.DistributedDoorAI( + 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( + 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( + insideDoor1 = DistributedDoorAI.DistributedDoorAI( self.air, blockNumber, DoorTypes.INT_HQ, @@ -82,10 +82,10 @@ class TutorialHQBuildingAI: 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 + 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) @@ -96,14 +96,13 @@ class TutorialHQBuildingAI: 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 + 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) diff --git a/toontown/tutorial/DistributedBattleTutorial.py b/toontown/tutorial/DistributedBattleTutorial.py index 34a8c55..87ffe6a 100644 --- a/toontown/tutorial/DistributedBattleTutorial.py +++ b/toontown/tutorial/DistributedBattleTutorial.py @@ -1,11 +1,15 @@ -from toontown.battle import DistributedBattle from direct.directnotify import DirectNotifyGlobal +from toontown.battle import DistributedBattle + class DistributedBattleTutorial(DistributedBattle.DistributedBattle): notify = DirectNotifyGlobal.directNotify.newCategory('DistributedBattleTutorial') - def startTimer(self, ts = 0): + def startTimer(self, ts=0): + # Instead of starting the countdown, + # hide the clock! self.townBattle.timer.hide() def playReward(self, ts): - self.movie.playTutorialReward(ts, self.uniqueName('reward'), self.handleRewardDone) + self.movie.playTutorialReward(ts, self.uniqueName('reward'), + self.handleRewardDone) diff --git a/toontown/tutorial/DistributedBattleTutorialAI.py b/toontown/tutorial/DistributedBattleTutorialAI.py index 7e7523f..ecbfa9c 100644 --- a/toontown/tutorial/DistributedBattleTutorialAI.py +++ b/toontown/tutorial/DistributedBattleTutorialAI.py @@ -1,15 +1,12 @@ -from toontown.battle import DistributedBattleAI -from toontown.battle import DistributedBattleBaseAI from direct.directnotify import DirectNotifyGlobal +from toontown.battle import DistributedBattleAI + class DistributedBattleTutorialAI(DistributedBattleAI.DistributedBattleAI): 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) - """ + finishCallback=None, maxSuits=4, interactivePropTrackBonus=-1): DistributedBattleAI.DistributedBattleAI.__init__( self, air, battleMgr, pos, suit, toonId, zoneId, finishCallback, maxSuits, tutorialFlag=1) @@ -17,7 +14,3 @@ class DistributedBattleTutorialAI(DistributedBattleAI.DistributedBattleAI): # There is no timer in the tutorial... The reward movie is random length. def startRewardTimer(self): pass - - #def handleRewardDone(self): - # DistributedBattleAI.DistributedBattleAI.handleRewardDone(self) - diff --git a/toontown/tutorial/SuitPlannerTutorialAI.py b/toontown/tutorial/SuitPlannerTutorialAI.py index ecb7170..effdc54 100644 --- a/toontown/tutorial/SuitPlannerTutorialAI.py +++ b/toontown/tutorial/SuitPlannerTutorialAI.py @@ -2,12 +2,12 @@ which handles management of the suit you will fight during the tutorial.""" -from otp.ai.AIBaseGlobal import * - +from panda3d.core import Vec3 from direct.directnotify import DirectNotifyGlobal from toontown.suit import DistributedTutorialSuitAI from toontown.tutorial import TutorialBattleManagerAI + class SuitPlannerTutorialAI: """ SuitPlannerTutorialAI: manages the single suit that you fight during @@ -44,13 +44,13 @@ class SuitPlannerTutorialAI: 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 + # 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): @@ -61,16 +61,14 @@ class SuitPlannerTutorialAI: return 0 def requestBattle(self, zoneId, suit, toonId): - # 70, 20, 0 is a battle cell position that I just made up. + # 35, 20, -0.5 is a battle cell position that I just made up. self.battle = self.battleMgr.newBattle( - zoneId, zoneId, Vec3(35, 20, 0), + zoneId, zoneId, Vec3(35, 20, -0.5), suit, toonId, finishCallback=self.battleOverCallback) - return 1 + return True def removeSuit(self, suit): # Get rid of the suit. suit.requestDelete() self.suit = None - - diff --git a/toontown/tutorial/TutorialBattleManagerAI.py b/toontown/tutorial/TutorialBattleManagerAI.py index c8dc9f5..ea2a69f 100644 --- a/toontown/tutorial/TutorialBattleManagerAI.py +++ b/toontown/tutorial/TutorialBattleManagerAI.py @@ -1,13 +1,11 @@ -from toontown.battle import BattleManagerAI from direct.directnotify import DirectNotifyGlobal +from toontown.battle import BattleManagerAI from toontown.tutorial import DistributedBattleTutorialAI -class TutorialBattleManagerAI(BattleManagerAI.BattleManagerAI): +class TutorialBattleManagerAI(BattleManagerAI.BattleManagerAI): notify = DirectNotifyGlobal.directNotify.newCategory('TutorialBattleManagerAI') def __init__(self, air): BattleManagerAI.BattleManagerAI.__init__(self, air) self.battleConstructor = DistributedBattleTutorialAI.DistributedBattleTutorialAI - - diff --git a/toontown/tutorial/TutorialForceAcknowledge.py b/toontown/tutorial/TutorialForceAcknowledge.py index d967106..225e634 100644 --- a/toontown/tutorial/TutorialForceAcknowledge.py +++ b/toontown/tutorial/TutorialForceAcknowledge.py @@ -1,25 +1,26 @@ -from pandac.PandaModules import * -from toontown.toontowngui import TTDialog from toontown.toonbase import TTLocalizer +from toontown.toontowngui import TTDialog + class TutorialForceAcknowledge: def __init__(self, doneEvent): self.doneEvent = doneEvent self.dialog = None - return def enter(self): - base.localAvatar.loop('neutral') + # Make the toon stop running. + base.localAvatar.loop("neutral") self.doneStatus = {'mode': 'incomplete'} msg = TTLocalizer.TutorialForceAcknowledgeMessage - self.dialog = TTDialog.TTDialog(text=msg, command=self.handleOk, style=TTDialog.Acknowledge) + self.dialog = TTDialog.TTDialog(text=msg, + command=self.handleOk, + style=TTDialog.Acknowledge) def exit(self): if self.dialog: self.dialog.cleanup() self.dialog = None - return def handleOk(self, value): messenger.send(self.doneEvent, [self.doneStatus]) diff --git a/toontown/tutorial/TutorialManager.py b/toontown/tutorial/TutorialManager.py index fd9b928..b755a4e 100644 --- a/toontown/tutorial/TutorialManager.py +++ b/toontown/tutorial/TutorialManager.py @@ -1,45 +1,57 @@ -from pandac.PandaModules import * from direct.distributed import DistributedObject from direct.directnotify import DirectNotifyGlobal from toontown.hood import ZoneUtil -class TutorialManager(DistributedObject.DistributedObject): - notify = DirectNotifyGlobal.directNotify.newCategory('TutorialManager') - neverDisable = 1 - def __init__(self, cr): - DistributedObject.DistributedObject.__init__(self, cr) +class TutorialManager(DistributedObject.DistributedObject): + notify = DirectNotifyGlobal.directNotify.newCategory("TutorialManager") + neverDisable = 1 def generate(self): DistributedObject.DistributedObject.generate(self) - messenger.send('tmGenerate') - self.accept('requestTutorial', self.d_requestTutorial) - self.accept('requestSkipTutorial', self.d_requestSkipTutorial) - self.accept('rejectTutorial', self.d_rejectTutorial) + # Let the cr know we have arrived. + messenger.send("tmGenerate") + # Wait for a tutorial request or rejection. + self.accept("requestTutorial", self.d_requestTutorial) + self.accept("requestSkipTutorial", self.d_requestSkipTutorial) + self.accept("rejectTutorial", self.d_rejectTutorial) def disable(self): self.ignoreAll() + # In case we fell asleep in the tutorial ZoneUtil.overrideOff() DistributedObject.DistributedObject.disable(self) def d_requestTutorial(self): - self.sendUpdate('requestTutorial', []) + self.sendUpdate("requestTutorial", []) def d_rejectTutorial(self): - self.sendUpdate('rejectTutorial', []) + self.sendUpdate("rejectTutorial", []) def d_requestSkipTutorial(self): - self.sendUpdate('requestSkipTutorial', []) + self.sendUpdate("requestSkipTutorial", []) def skipTutorialResponse(self, allOk): - messenger.send('skipTutorialAnswered', [allOk]) + """Handle AI responding to our skip tutorial request.""" + messenger.send("skipTutorialAnswered", [allOk]) def enterTutorial(self, branchZone, streetZone, shopZone, hqZone): base.localAvatar.cantLeaveGame = 1 - ZoneUtil.overrideOn(branch=branchZone, exteriorList=[streetZone], interiorList=[shopZone, hqZone]) - messenger.send('startTutorial', [shopZone]) - self.acceptOnce('stopTutorial', self.__handleStopTutorial) - self.acceptOnce('toonArrivedTutorial', self.d_toonArrived) + # Override the ZoneUtil + ZoneUtil.overrideOn(branch=branchZone, + exteriorList=[streetZone], + interiorList=[shopZone, hqZone]) + # We start the tutorial in the gag shop. + messenger.send("startTutorial", [shopZone]) + # Add a hook on the tutorialDone event, which will get + # thrown when we are leaving the tutorial (by the handleEnterTunnel + # function in TutorialStreet.py + self.acceptOnce("stopTutorial", self.__handleStopTutorial) + # Add a hook that the Tutorial hood will send when the toon + # is fully in a zone. This lets the AI know it is clear to + # reset the toon properties in preparation for the tutorial + # (in case they bailed halfway through before) + self.acceptOnce("toonArrivedTutorial", self.d_toonArrived) def __handleStopTutorial(self): base.localAvatar.cantLeaveGame = 0 @@ -47,7 +59,7 @@ class TutorialManager(DistributedObject.DistributedObject): ZoneUtil.overrideOff() def d_allDone(self): - self.sendUpdate('allDone', []) + self.sendUpdate("allDone", []) def d_toonArrived(self): - self.sendUpdate('toonArrived', []) + self.sendUpdate("toonArrived", []) diff --git a/toontown/tutorial/TutorialManagerAI.py b/toontown/tutorial/TutorialManagerAI.py index e90daf6..0df303f 100644 --- a/toontown/tutorial/TutorialManagerAI.py +++ b/toontown/tutorial/TutorialManagerAI.py @@ -1,5 +1,4 @@ -from otp.ai.AIBaseGlobal import * -from pandac.PandaModules import * +from panda3d.toontown import DNAStorage from direct.distributed import DistributedObjectAI from direct.directnotify import DirectNotifyGlobal from toontown.building import TutorialBuildingAI @@ -7,10 +6,10 @@ from toontown.building import TutorialHQBuildingAI from toontown.tutorial import SuitPlannerTutorialAI from toontown.toonbase import ToontownBattleGlobals from toontown.toon import NPCToons -from toontown.toonbase import TTLocalizer from toontown.ai import BlackCatHolidayMgrAI from toontown.ai import DistributedBlackCatMgrAI + class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): notify = DirectNotifyGlobal.directNotify.newCategory("TutorialManagerAI") @@ -37,21 +36,19 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): # 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): + 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 + self.accept("avatarEntered", self.waitingToonEntered) def requestTutorial(self): # TODO: possible security breach: what if client is repeatedly @@ -100,7 +97,6 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): # No cogs defeated av.b_setCogStatus([1] * 32) av.b_setCogCount([0] * 32) - return def allDone(self): avId = self.air.getAvatarIdFromSender() @@ -118,13 +114,12 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): 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() @@ -143,7 +138,7 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): def battleOverCallback(zoneId): hqBuilding.battleOverCallback() building.battleOverCallback() - + # Create a suit planner suitPlanner = SuitPlannerTutorialAI.SuitPlannerTutorialAI( self.air, @@ -151,8 +146,7 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): battleOverCallback) # Create the NPC blocking the tunnel to the playground - blockerNPC = NPCToons.createNPC(self.air, 20001, NPCToons.NPCToonDict[20001], streetZone, - questCallback=self.__handleBlockDone) + blockerNPC = NPCToons.createNPC(self.air, 20001, NPCToons.NPCToonDict[20001], streetZone) blockerNPC.setTutorial(1) # is the black cat holiday enabled? @@ -161,23 +155,20 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): 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, - } + + 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: @@ -213,8 +204,7 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): 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.""" @@ -239,7 +229,7 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): 1000, 1 ) - + self.air.questManager.completeAllQuestsMagically(av) av.removeQuest(101) self.air.questManager.assignQuest(avId, @@ -253,7 +243,7 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): # do whatever needs to be done to make his quest state good elif av: - self.notify.debug("%s requestedSkipTutorial, but tutorialAck is 1") + self.notify.debug(f"{avId} requestedSkipTutorial, but tutorialAck is 1") else: response = 0 self.notify.warning( @@ -261,31 +251,29 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): 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.notify.warning(f"waited too long for toon {avId} responding no to skip tutorial request") self.sendUpdateToAvatarId(avId, "skipTutorialResponse", [0]) del self.avIdsRequestingSkip[avId] - self.removeTask("skipTutorialToon-%d" % avId) - + self.removeTask(f"skipTutorialToon-{avId}") - def waitForToonToEnter(self,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]) + self.doMethodLater(self.WaitTimeForSkipTutorial, self.didNotGetToon, f"skipTutorialToon-{avId}", [avId]) def didNotGetToon(self, avId): """Just say no since the AI didn't get it.""" @@ -300,23 +288,19 @@ class TutorialManagerAI(DistributedObjectAI.DistributedObjectAI): self.notify.debugStateCall(self) avId = self.air.getAvatarIdFromSender() # Make sure the avatar exists - av = self.air.doId2do.get(avId) + av = self.air.doId2do.get(avId) if av: - self.respondToSkipTutorial(avId,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 - -