247 lines
10 KiB
Python
247 lines
10 KiB
Python
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]
|