import random from panda3d.core import Point3, Vec3 from panda3d.toontown import DNADoor from direct.interval.IntervalGlobal import * from direct.directnotify import DirectNotifyGlobal from direct.distributed import DistributedObject from . import ToonInteriorColors from toontown.hood import ZoneUtil from toontown.suit import SuitDNA from toontown.suit import Suit from toontown.quest import QuestParser class DistributedTutorialInterior(DistributedObject.DistributedObject): notify = DirectNotifyGlobal.directNotify.newCategory('DistributedTutorialInterior') def announceGenerate(self): DistributedObject.DistributedObject.announceGenerate(self) self.setup() def disable(self): self.interior.removeNode() del self.interior self.street.removeNode() del self.street self.sky.removeNode() del self.sky self.mickeyMovie.cleanup() del self.mickeyMovie self.suitWalkTrack.finish() del self.suitWalkTrack self.suit.delete() del self.suit self.ignore("enterTutotialInterior") DistributedObject.DistributedObject.disable(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): """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] 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": self.replaceRandomInModel(newNP) 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"): self.randomGenerator.seed(self.zoneId) newNP.setColorScale( self.randomGenerator.choice(self.colors[category])) else: 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(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) # 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) # 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) # 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) # 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" door = self.dnaStore.findNode(doorModelName) # 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) # 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() # 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) 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(f'Tutorial movie: Waiting for place={place}, has fsm={hasattr(place, "fsm")}') if hasattr(place, 'fsm'): self.notify.info(f'Tutorial movie: place state={place.fsm.getCurrentState().getName()}') self.acceptOnce('enterTutorialInterior', self.playMovie) def playMovie(self): self.notify.info('Tutorial movie: Play.') self.mickeyMovie.play() def createSuit(self): # Create a suit self.suit = Suit.Suit() suitDNA = SuitDNA.SuitDNA() suitDNA.newSuit('f') self.suit.setDNA(suitDNA) 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.loop() def setZoneIdAndBlock(self, zoneId, block): self.zoneId = zoneId self.block = block def setTutorialNpcId(self, npcId): self.npcId = npcId self.npc = self.cr.doId2do[npcId]