mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-26 21:22:27 -06:00
535 lines
21 KiB
Python
Executable file
535 lines
21 KiB
Python
Executable file
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')
|
|
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.titleSeq = None
|
|
self.zonesEnteredList = []
|
|
self.fColorZones = 0
|
|
self.scenarioIndex = 0
|
|
|
|
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 setAvIds(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
|
|
self.privGotAllRequired()
|
|
|
|
def setStartTimestamp(self, timestamp):
|
|
DistributedLevel.notify.debug('setStartTimestamp: %s' % timestamp)
|
|
self.startTime = globalClockDelta.networkToLocalTime(timestamp, bits=32)
|
|
self.privGotAllRequired()
|
|
|
|
def privGotAllRequired(self):
|
|
if hasattr(self, 'zoneIds') and hasattr(self, 'startTime'):
|
|
self.levelAnnounceGenerate()
|
|
|
|
def levelAnnounceGenerate(self):
|
|
pass
|
|
|
|
def initializeLevel(self, levelSpec):
|
|
self.privGotSpec(levelSpec)
|
|
|
|
def privGotSpec(self, levelSpec):
|
|
Level.Level.initializeLevel(self, self.doId, levelSpec, self.scenarioIndex)
|
|
modelZoneNums = self.zoneNums
|
|
specZoneNums = self.zoneNum2zoneId.keys()
|
|
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 parentId not in self.parent2pendingChildren:
|
|
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)
|
|
taskMgr.add(self.visChangeTask, self.uniqueName(DistributedLevel.VisChangeTaskName), priority=49)
|
|
|
|
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 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
|
|
|
|
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 = Sequence(Func(self.hideSmallTitleText), Func(self.showTitleText), Wait(6.1), LerpColorInterval(self.titleText, 0.5, Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], self.titleColor[3]), startColor=Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], 0.0)))
|
|
smallTitleSeq = Sequence(Func(self.hideTitleText), Func(self.showSmallTitle))
|
|
if titleSeq:
|
|
self.titleSeq = Sequence(titleSeq, smallTitleSeq)
|
|
else:
|
|
self.titleSeq = smallTitleSeq
|
|
self.titleSeq.start()
|
|
|
|
def showInfoText(self, text = 'hello world'):
|
|
description = text
|
|
if description and description != '':
|
|
if self.titleSeq is not None:
|
|
self.titleSeq.finish()
|
|
self.titleSeq = None
|
|
self.smallTitleText.setText(description)
|
|
self.titleText.setText(description)
|
|
self.titleText.setColor(Vec4(*self.titleColor))
|
|
self.titleText.setFg(self.titleColor)
|
|
titleSeq = None
|
|
titleSeq = Sequence(Func(self.hideSmallTitleText), Func(self.showTitleText), Wait(3.1), LerpColorInterval(self.titleText, 0.5, Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], self.titleColor[3]), startColor=Vec4(self.titleColor[0], self.titleColor[1], self.titleColor[2], 0.0)))
|
|
if titleSeq:
|
|
self.titleSeq = Sequence(titleSeq)
|
|
self.titleSeq.start()
|
|
|
|
def showTitleText(self):
|
|
self.titleText.show()
|
|
|
|
def hideTitleText(self):
|
|
if self.titleText:
|
|
self.titleText.hide()
|
|
|
|
def showSmallTitle(self):
|
|
if self.titleText:
|
|
self.titleText.hide()
|
|
if self.smallTitleText:
|
|
self.smallTitleText.show()
|
|
|
|
def hideSmallTitleText(self):
|
|
if self.smallTitleText:
|
|
self.smallTitleText.hide()
|
|
|
|
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
|