oldschool-toontown/otp/level/DistributedLevel.py

587 lines
23 KiB
Python
Raw Normal View History

2019-11-02 17:27:54 -05:00
from direct.distributed.ClockDelta import *
from pandac.PandaModules import *
from direct.showbase.PythonUtil import Functor, sameElements, list2dict, uniqueElements
from direct.interval.IntervalGlobal import *
from toontown.distributed.ToontownMsgTypes import *
from toontown.toonbase import ToontownGlobals
from otp.otpbase import OTPGlobals
from direct.distributed import DistributedObject
import Level
import LevelConstants
from direct.directnotify import DirectNotifyGlobal
import EntityCreator
from direct.gui import OnscreenText
from direct.task import Task
import LevelUtil
import random
class DistributedLevel(DistributedObject.DistributedObject, Level.Level):
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedLevel')
WantVisibility = config.GetBool('level-visibility', 1)
ColorZonesAllDOs = 0
FloorCollPrefix = 'zoneFloor'
OuchTaskName = 'ouchTask'
VisChangeTaskName = 'visChange'
EmulateEntrancePoint = True
def __init__(self, cr):
DistributedObject.DistributedObject.__init__(self, cr)
Level.Level.__init__(self)
self.lastToonZone = None
self.lastCamZone = 0
self.titleColor = (1, 1, 1, 1)
self.titleText = OnscreenText.OnscreenText('', fg=self.titleColor, shadow=(0, 0, 0, 1), font=ToontownGlobals.getSuitFont(), pos=(0, -0.5), scale=0.16, drawOrder=0, mayChange=1)
self.smallTitleText = OnscreenText.OnscreenText('', fg=self.titleColor, font=ToontownGlobals.getSuitFont(), pos=(0.65, 0.9), scale=0.08, drawOrder=0, mayChange=1, bg=(0.5, 0.5, 0.5, 0.5), align=TextNode.ARight)
self.zonesEnteredList = []
self.fColorZones = 0
self.scenarioIndex = 0
return
def generate(self):
DistributedLevel.notify.debug('generate')
DistributedObject.DistributedObject.generate(self)
self.parent2pendingChildren = {}
self.curSpec = None
if base.cr.timeManager is not None:
base.cr.timeManager.synchronize('DistributedLevel.generate')
else:
self.notify.warning('generate(): no TimeManager!')
return
def setLevelZoneId(self, zoneId):
self.levelZone = zoneId
def setPlayerIds(self, avIdList):
self.avIdList = avIdList
def setEntranceId(self, entranceId):
self.entranceId = entranceId
def getEntranceId(self):
return self.entranceId
def setZoneIds(self, zoneIds):
DistributedLevel.notify.debug('setZoneIds: %s' % zoneIds)
self.zoneIds = zoneIds
def setStartTimestamp(self, timestamp):
DistributedLevel.notify.debug('setStartTimestamp: %s' % timestamp)
self.startTime = globalClockDelta.networkToLocalTime(timestamp, bits=32)
self.privGotAllRequired()
def privGotAllRequired(self):
self.levelAnnounceGenerate()
def levelAnnounceGenerate(self):
pass
def initializeLevel(self, levelSpec):
if __dev__:
self.candidateSpec = levelSpec
self.sendUpdate('requestCurrentLevelSpec', [levelSpec.stringHash(), levelSpec.entTypeReg.getHashStr()])
else:
self.privGotSpec(levelSpec)
if __dev__:
def reportModelSpecSyncError(self, msg):
DistributedLevel.notify.error('%s\n\nyour spec does not match the level model\nuse SpecUtil.updateSpec, then restart your AI and client' % msg)
def setSpecDeny(self, reason):
DistributedLevel.notify.error(reason)
def setSpecSenderDoId(self, doId):
DistributedLevel.notify.debug('setSpecSenderDoId: %s' % doId)
blobSender = base.cr.doId2do[doId]
def setSpecBlob(specBlob, blobSender = blobSender, self = self):
blobSender.sendAck()
from LevelSpec import LevelSpec
spec = eval(specBlob)
if spec is None:
spec = self.candidateSpec
del self.candidateSpec
self.privGotSpec(spec)
return
if blobSender.isComplete():
setSpecBlob(blobSender.getBlob())
else:
evtName = self.uniqueName('specDone')
blobSender.setDoneEvent(evtName)
self.acceptOnce(evtName, setSpecBlob)
def privGotSpec(self, levelSpec):
Level.Level.initializeLevel(self, self.doId, levelSpec, self.scenarioIndex)
modelZoneNums = self.zoneNums
specZoneNums = self.zoneNum2zoneId.keys()
if not sameElements(modelZoneNums, specZoneNums):
self.reportModelSpecSyncError('model zone nums (%s) do not match spec zone nums (%s)' % (modelZoneNums, specZoneNums))
self.initVisibility()
self.placeLocalToon()
def announceLeaving(self):
DistributedLevel.notify.debug('announceLeaving')
self.doneBarrier()
def placeLocalToon(self, moveLocalAvatar = True):
initialZoneEnt = None
if self.entranceId in self.entranceId2entity:
epEnt = self.entranceId2entity[self.entranceId]
if moveLocalAvatar:
epEnt.placeToon(base.localAvatar, self.avIdList.index(base.localAvatar.doId), len(self.avIdList))
initialZoneEnt = self.getEntity(epEnt.getZoneEntId())
elif self.EmulateEntrancePoint:
self.notify.debug('unknown entranceId %s' % self.entranceId)
if moveLocalAvatar:
base.localAvatar.reparentTo(render)
base.localAvatar.setPosHpr(0, 0, 0, 0, 0, 0)
self.notify.debug('showing all zones')
self.setColorZones(1)
zoneEntIds = list(self.entType2ids['zone'])
zoneEntIds.remove(LevelConstants.UberZoneEntId)
if len(zoneEntIds):
zoneEntId = random.choice(zoneEntIds)
initialZoneEnt = self.getEntity(zoneEntId)
if moveLocalAvatar:
base.localAvatar.setPos(render, initialZoneEnt.getZoneNode().getPos(render))
else:
initialZoneEnt = self.getEntity(LevelConstants.UberZoneEntId)
if moveLocalAvatar:
base.localAvatar.setPos(render, 0, 0, 0)
if initialZoneEnt is not None:
self.enterZone(initialZoneEnt.entId)
return
def createEntityCreator(self):
return EntityCreator.EntityCreator(level=self)
def onEntityTypePostCreate(self, entType):
Level.Level.onEntityTypePostCreate(self, entType)
if entType == 'levelMgr':
self.__handleLevelMgrCreated()
def __handleLevelMgrCreated(self):
levelMgr = self.getEntity(LevelConstants.LevelMgrEntId)
self.geom = levelMgr.geom
self.zoneNum2node = LevelUtil.getZoneNum2Node(self.geom)
self.zoneNums = self.zoneNum2node.keys()
self.zoneNums.sort()
self.zoneNumDict = list2dict(self.zoneNums)
DistributedLevel.notify.debug('zones from model: %s' % self.zoneNums)
self.fixupLevelModel()
def fixupLevelModel(self):
for zoneNum, zoneNode in self.zoneNum2node.items():
if zoneNum == LevelConstants.UberZoneEntId:
continue
allColls = zoneNode.findAllMatches('**/+CollisionNode')
floorColls = []
for coll in allColls:
bitmask = coll.node().getIntoCollideMask()
if not (bitmask & ToontownGlobals.FloorBitmask).isZero():
floorColls.append(coll)
if len(floorColls) > 0:
floorCollName = '%s%s' % (DistributedLevel.FloorCollPrefix, zoneNum)
others = zoneNode.findAllMatches('**/%s' % floorCollName)
for other in others:
other.setName('%s_renamed' % floorCollName)
for floorColl in floorColls:
floorColl.setName(floorCollName)
def handleZoneEnter(collisionEntry, self = self, zoneNum = zoneNum):
self.toonEnterZone(zoneNum)
floorNode = collisionEntry.getIntoNode()
if floorNode.hasTag('ouch'):
ouchLevel = int(self.getFloorOuchLevel())
self.startOuch(ouchLevel)
self.accept('enter%s' % floorCollName, handleZoneEnter)
def handleZoneExit(collisionEntry, self = self, zoneNum = zoneNum):
floorNode = collisionEntry.getIntoNode()
if floorNode.hasTag('ouch'):
self.stopOuch()
self.accept('exit%s' % floorCollName, handleZoneExit)
def getFloorOuchLevel(self):
return 1
def announceGenerate(self):
DistributedLevel.notify.debug('announceGenerate')
DistributedObject.DistributedObject.announceGenerate(self)
def disable(self):
DistributedLevel.notify.debug('disable')
if hasattr(self, 'geom'):
del self.geom
self.shutdownVisibility()
self.destroyLevel()
self.ignoreAll()
taskMgr.remove(self.uniqueName('titleText'))
if self.smallTitleText:
self.smallTitleText.cleanup()
self.smallTitleText = None
if self.titleText:
self.titleText.cleanup()
self.titleText = None
self.zonesEnteredList = []
DistributedObject.DistributedObject.disable(self)
return
def delete(self):
DistributedLevel.notify.debug('delete')
DistributedObject.DistributedObject.delete(self)
self.stopOuch()
def requestReparent(self, entity, parentId, wrt = False):
parent = self.getEntity(parentId)
if parent is not None:
if wrt:
entity.wrtReparentTo(parent.getNodePath())
else:
entity.reparentTo(parent.getNodePath())
else:
DistributedLevel.notify.debug('entity %s requesting reparent to %s, not yet created' % (entity, parentId))
entity.reparentTo(hidden)
if not self.parent2pendingChildren.has_key(parentId):
self.parent2pendingChildren[parentId] = []
def doReparent(parentId = parentId, self = self, wrt = wrt):
parent = self.getEntity(parentId)
for child in self.parent2pendingChildren[parentId]:
DistributedLevel.notify.debug('performing pending reparent of %s to %s' % (child, parent))
if wrt:
child.wrtReparentTo(parent.getNodePath())
else:
child.reparentTo(parent.getNodePath())
del self.parent2pendingChildren[parentId]
self.ignore(self.getEntityCreateEvent(parentId))
self.accept(self.getEntityCreateEvent(parentId), doReparent)
self.parent2pendingChildren[parentId].append(entity)
return
def getZoneNode(self, zoneEntId):
return self.zoneNum2node.get(zoneEntId)
def warpToZone(self, zoneNum):
zoneNode = self.getZoneNode(zoneNum)
if zoneNode is None:
return
base.localAvatar.setPos(zoneNode, 0, 0, 0)
base.localAvatar.setHpr(zoneNode, 0, 0, 0)
self.enterZone(zoneNum)
return
def showZone(self, zoneNum):
zone = self.getZoneNode(zoneNum)
zone.unstash()
zone.clearColor()
def setColorZones(self, fColorZones):
self.fColorZones = fColorZones
self.resetVisibility()
def getColorZones(self):
return self.fColorZones
def hideZone(self, zoneNum):
zone = self.getZoneNode(zoneNum)
if self.fColorZones:
zone.unstash()
zone.setColor(1, 0, 0)
else:
zone.stash()
def setTransparency(self, alpha, zone = None):
self.geom.setTransparency(1)
if zone is None:
node = self.geom
else:
node = self.getZoneNode(zoneNum)
node.setAlphaScale(alpha)
return
def initVisibility(self):
self.curVisibleZoneNums = list2dict(self.zoneNums)
del self.curVisibleZoneNums[LevelConstants.UberZoneEntId]
self.curZoneNum = None
self.visChangedThisFrame = 0
self.fForceSetZoneThisFrame = 0
def handleCameraRayFloorCollision(collEntry, self = self):
name = collEntry.getIntoNode().getName()
self.notify.debug('camera floor ray collided with: %s' % name)
prefixLen = len(DistributedLevel.FloorCollPrefix)
if name[:prefixLen] == DistributedLevel.FloorCollPrefix:
try:
zoneNum = int(name[prefixLen:])
except:
DistributedLevel.notify.warning('Invalid zone floor collision node: %s' % name)
else:
self.camEnterZone(zoneNum)
self.accept('on-floor', handleCameraRayFloorCollision)
if not DistributedLevel.WantVisibility:
zoneNums = list(self.zoneNums)
zoneNums.remove(LevelConstants.UberZoneEntId)
self.forceSetZoneThisFrame()
self.setVisibility(zoneNums)
taskMgr.add(self.visChangeTask, self.uniqueName(DistributedLevel.VisChangeTaskName), priority=49)
return
def shutdownVisibility(self):
taskMgr.remove(self.uniqueName(DistributedLevel.VisChangeTaskName))
def toonEnterZone(self, zoneNum, ouchLevel = None):
DistributedLevel.notify.debug('toonEnterZone%s' % zoneNum)
if zoneNum != self.lastToonZone:
self.lastToonZone = zoneNum
self.notify.debug('toon is standing in zone %s' % zoneNum)
messenger.send('factoryZoneChanged', [zoneNum])
def camEnterZone(self, zoneNum):
DistributedLevel.notify.debug('camEnterZone%s' % zoneNum)
self.enterZone(zoneNum)
if zoneNum != self.lastCamZone:
self.lastCamZone = zoneNum
self.smallTitleText.hide()
self.spawnTitleText()
def lockVisibility(self, zoneNum = None, zoneId = None):
if zoneId is not None:
zoneNum = self.getZoneNumFromId(zoneId)
self.notify.debug('lockVisibility to zoneNum %s' % zoneNum)
self.lockVizZone = zoneNum
self.enterZone(self.lockVizZone)
return
def unlockVisibility(self):
self.notify.debug('unlockVisibility')
if not hasattr(self, 'lockVizZone'):
self.notify.warning('visibility already unlocked')
else:
del self.lockVizZone
self.updateVisibility()
def enterZone(self, zoneNum):
DistributedLevel.notify.debug('entering zone %s' % zoneNum)
if not DistributedLevel.WantVisibility:
return
if zoneNum == self.curZoneNum:
return
if zoneNum not in self.zoneNumDict:
DistributedLevel.notify.error('no ZoneEntity for this zone (%s)!!' % zoneNum)
self.updateVisibility(zoneNum)
def updateVisibility(self, zoneNum = None):
if zoneNum is None:
zoneNum = self.curZoneNum
if zoneNum is None:
return
if hasattr(self, 'lockVizZone'):
zoneNum = self.lockVizZone
zoneEnt = self.getEntity(zoneNum)
visibleZoneNums = list2dict([zoneNum])
visibleZoneNums.update(list2dict(zoneEnt.getVisibleZoneNums()))
if not __debug__:
if self.lastToonZone not in visibleZoneNums:
if self.lastToonZone is not None:
self.notify.warning('adding zoneNum %s to visibility list because toon is standing in that zone!' % self.lastToonZone)
visibleZoneNums.update(list2dict([self.lastToonZone]))
zoneEntIds = list(self.entType2ids['zone'])
zoneEntIds.remove(LevelConstants.UberZoneEntId)
if len(zoneEntIds):
pass
vizZonesChanged = 1
addedZoneNums = []
removedZoneNums = []
allVZ = dict(visibleZoneNums)
allVZ.update(self.curVisibleZoneNums)
for vz, dummy in allVZ.items():
new = vz in visibleZoneNums
old = vz in self.curVisibleZoneNums
if new and old:
continue
if new:
addedZoneNums.append(vz)
else:
removedZoneNums.append(vz)
if not addedZoneNums and not removedZoneNums:
DistributedLevel.notify.debug('visible zone list has not changed')
vizZonesChanged = 0
else:
DistributedLevel.notify.debug('showing zones %s' % addedZoneNums)
for az in addedZoneNums:
self.showZone(az)
DistributedLevel.notify.debug('hiding zones %s' % removedZoneNums)
for rz in removedZoneNums:
self.hideZone(rz)
if vizZonesChanged or self.fForceSetZoneThisFrame:
self.setVisibility(visibleZoneNums.keys())
self.fForceSetZoneThisFrame = 0
self.curZoneNum = zoneNum
self.curVisibleZoneNums = visibleZoneNums
return
def setVisibility(self, vizList):
if self.fColorZones and DistributedLevel.ColorZonesAllDOs:
vizList = list(self.zoneNums)
vizList.remove(LevelConstants.UberZoneEntId)
uberZone = self.getZoneId(LevelConstants.UberZoneEntId)
visibleZoneIds = [OTPGlobals.UberZone, self.levelZone, uberZone]
for vz in vizList:
if vz is not LevelConstants.UberZoneEntId:
visibleZoneIds.append(self.getZoneId(vz))
DistributedLevel.notify.debug('new viz list: %s' % visibleZoneIds)
base.cr.sendSetZoneMsg(self.levelZone, visibleZoneIds)
def resetVisibility(self):
self.curVisibleZoneNums = list2dict(self.zoneNums)
del self.curVisibleZoneNums[LevelConstants.UberZoneEntId]
for vz, dummy in self.curVisibleZoneNums.items():
self.showZone(vz)
self.updateVisibility()
def handleVisChange(self):
Level.Level.handleVisChange(self)
self.visChangedThisFrame = 1
def forceSetZoneThisFrame(self):
self.fForceSetZoneThisFrame = 1
def visChangeTask(self, task):
if self.visChangedThisFrame or self.fForceSetZoneThisFrame:
self.updateVisibility()
self.visChangedThisFrame = 0
return Task.cont
if __dev__:
def setAttribChange(self, entId, attribName, valueStr, username):
value = eval(valueStr)
self.levelSpec.setAttribChange(entId, attribName, value, username)
def spawnTitleText(self):
def getDescription(zoneNum, self = self):
ent = self.entities.get(zoneNum)
if ent and hasattr(ent, 'description'):
return ent.description
return None
description = getDescription(self.lastCamZone)
if description and description != '':
taskMgr.remove(self.uniqueName('titleText'))
self.smallTitleText.setText(description)
self.titleText.setText(description)
self.titleText.setColor(Vec4(*self.titleColor))
self.titleText.setFg(self.titleColor)
titleSeq = None
if self.lastCamZone not in self.zonesEnteredList:
self.zonesEnteredList.append(self.lastCamZone)
titleSeq = Task.sequence(Task.Task(self.hideSmallTitleTextTask), Task.Task(self.showTitleTextTask), Task.pause(0.1), Task.pause(6.0), self.titleText.lerpColor(Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], self.titleColor[3]), Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], 0.0), 0.5))
smallTitleSeq = Task.sequence(Task.Task(self.hideTitleTextTask), Task.Task(self.showSmallTitleTask))
if titleSeq:
seq = Task.sequence(titleSeq, smallTitleSeq)
else:
seq = smallTitleSeq
taskMgr.add(seq, self.uniqueName('titleText'))
return
def showInfoText(self, text = 'hello world'):
description = text
if description and description != '':
taskMgr.remove(self.uniqueName('titleText'))
self.smallTitleText.setText(description)
self.titleText.setText(description)
self.titleText.setColor(Vec4(*self.titleColor))
self.titleText.setFg(self.titleColor)
titleSeq = None
titleSeq = Task.sequence(Task.Task(self.hideSmallTitleTextTask), Task.Task(self.showTitleTextTask), Task.pause(0.1), Task.pause(3.0), self.titleText.lerpColor(Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], self.titleColor[3]), Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], 0.0), 0.5))
if titleSeq:
seq = Task.sequence(titleSeq)
taskMgr.add(seq, self.uniqueName('titleText'))
return
def showTitleTextTask(self, task):
self.titleText.show()
return Task.done
def hideTitleTextTask(self, task):
if self.titleText:
self.titleText.hide()
return Task.done
def showSmallTitleTask(self, task):
if self.titleText:
self.titleText.hide()
self.smallTitleText.show()
return Task.done
def hideSmallTitleTextTask(self, task):
if self.smallTitleText:
self.smallTitleText.hide()
return Task.done
def startOuch(self, ouchLevel, period = 2):
self.notify.debug('startOuch %s' % ouchLevel)
if not hasattr(self, 'doingOuch'):
def doOuch(task, self = self, ouchLevel = ouchLevel, period = period):
self.b_setOuch(ouchLevel)
self.lastOuchTime = globalClock.getFrameTime()
taskMgr.doMethodLater(period, doOuch, DistributedLevel.OuchTaskName)
delay = 0
if hasattr(self, 'lastOuchTime'):
curFrameTime = globalClock.getFrameTime()
timeSinceLastOuch = curFrameTime - self.lastOuchTime
if timeSinceLastOuch < period:
delay = period - timeSinceLastOuch
if delay > 0:
taskMgr.doMethodLater(period, doOuch, DistributedLevel.OuchTaskName)
else:
doOuch(None)
self.doingOuch = 1
return
def stopOuch(self):
if hasattr(self, 'doingOuch'):
taskMgr.remove(DistributedLevel.OuchTaskName)
del self.doingOuch
def b_setOuch(self, penalty, anim = None):
self.notify.debug('b_setOuch %s' % penalty)
av = base.localAvatar
if not av.isStunned:
self.d_setOuch(penalty)
self.setOuch(penalty, anim)
def d_setOuch(self, penalty):
self.sendUpdate('setOuch', [penalty])
def setOuch(self, penalty, anim = None):
if anim == 'Squish':
if base.cr.playGame.getPlace():
base.cr.playGame.getPlace().fsm.request('squished')
elif anim == 'Fall':
if base.cr.playGame.getPlace():
base.cr.playGame.getPlace().fsm.request('fallDown')
av = base.localAvatar
av.stunToon()
av.playDialogueForString('!')
def complexVis(self):
return 1