예제 #1
0
class DynamicHuman(HumanBase.HumanBase, Biped.Biped):
    notify = DirectNotifyGlobal.directNotify.newCategory('Human')
    
    def __init__(self, other = None):
        Biped.Biped.__init__(self, other, HumanAnimationMixer)
        self.model = None
        self.zombie = False
        self.crazyColorSkin = False
        self.crazyColorSkinIndex = 0
        self.flattenPending = None
        if __dev__:
            self.optimizeLOD = base.config.GetBool('optimize-avatar-lod', 1)
        else:
            self.optimizeLOD = 0
        self.master = 0
        self.loaded = 0
        self.playingRate = None
        self.shadowFileName = 'models/misc/drop_shadow'
        self.setFont(PiratesGlobals.getInterfaceFont())
        self._DynamicHuman__blinkName = 'blink-' + str(self.this)
        self.eyeLids = None
        self.eyeBalls = None
        self.eyeIris = None
        self.reducedAnimList = None
        self.headNode = None
        self.extraNode = None
        self.scaleNode = None
        self.rootNode = None
        self.floorOffsetZ = 0.0
        self.headFudgeHpr = Vec3(0, 0, 0)
        self.randGen = random.Random()
        self.randGen.seed(random.random())
        self.eyeFSM = ClassicFSM('eyeFSM', [
            State('off', self.enterEyeFSMOff, self.exitEyeFSMOff, [
                'open',
                'closed']),
            State('open', self.enterEyeFSMOpen, self.exitEyeFSMOpen, [
                'closed',
                'off']),
            State('closed', self.enterEyeFSMClosed, self.exitEyeFSMClosed, [
                'open',
                'off'])], 'off', 'off')
        self.eyeFSM.enterInitialState()
        self.isPaid = True
        if other != None:
            self.copyHuman(other)
        

    
    def removeCopiedNodes(self):
        self.dropShadow = self.find('**/drop_shadow*')
        if not self.dropShadow.isEmpty():
            self.deleteDropShadow()
        

    
    def flattenHuman(self):
        self.deleteNametag3d()
        self.getWeaponJoints()

    
    def _DynamicHuman__doneFlattenHuman(self, models):
        self.flattenPending = None
        self.getWeaponJoints()

    
    def copyHuman(self, other):
        self.gender = other.gender
        self.loaded = other.loaded
        self.type = other.type
        self.loadAnimatedHead = other.loadAnimatedHead
        self.flattenHuman()
        self.model = None

    
    def delete(self):
        
        try:
            pass
        except:
            self.Human_deleted = 1
            taskMgr.remove(self._DynamicHuman__blinkName)
            if self.dropShadow and not self.dropShadow.isEmpty():
                self.deleteDropShadow()
            
            del self.eyeFSM
            self.controlShapes = None
            self.sliderNames = None
            if self.model:
                self.model.delete()
                del self.model
            
            Biped.Biped.delete(self)


    
    def isDeleted(self):
        
        try:
            if self.Human_deleted == 1:
                return True
        except:
            return False


    
    def setupExtraNodes(self):
        idx = 0
        if self.gender == 'f':
            idx = 1
        
        jointName = 'def_head01'
        jointNameExtra = 'def_extra_jt'
        jointNameScale = 'def_scale_jt'
        lods = self.getLODNames()
        self.headNode = self.controlJoint(None, 'legs', jointName, lods[0])
        self.extraNode = self.controlJoint(None, 'legs', jointNameExtra, lods[0])
        self.scaleNode = self.controlJoint(None, 'legs', jointNameScale, lods[0])
        self.rootNode = self.getLOD('2000').find('**/dx_root')
        self.floorOffsetZ = self.rootNode.getZ()
        for lod in lods[1:]:
            self.controlJoint(self.headNode, 'legs', jointName, lod)
            self.controlJoint(self.extraNode, 'legs', jointNameExtra, lod)
            self.controlJoint(self.scaleNode, 'legs', jointNameScale, lod)
            exposedHeadJoint = self.getLOD(lod).find('**/def_head01')
            if not exposedHeadJoint.isEmpty():
                exposedHeadJoint.removeNode()
                continue
        
        self.headNode.setScale(HeadScales[idx][self.style.getBodyShape()])
        self.setGlobalScale(self.calcBodyScale())

    
    def undoExtraNodes(self):
        jointNameExtra = 'def_extra_jt'
        jointNameScale = 'def_scale_jt'
        joints = self.findAllMatches('**/*' + jointNameExtra)
        if not joints.isEmpty():
            joints.detach()
            joints.clear()
        
        if self.headNode:
            self.headNode.removeNode()
            self.headNode = None
            self.extraNode.removeNode()
            self.extraNode = None
        
        joints = self.findAllMatches('**/*' + jointNameScale)
        if not joints.isEmpty():
            joints.detach()
            joints.clear()
        
        if self.scaleNode:
            self.scaleNode.removeNode()
            self.scaleNode = None
            self.rootNode = None
        

    
    def fixEyes(self):
        self.eyeLids = { }
        self.eyeBalls = { }
        self.eyeIris = { }
        for lodName in self.getLODNames():
            geom = self.getPart('head', lodName)
            self.eyeLids[lodName] = geom.findAllMatches('**/*eyelid*')
            self.eyeBalls[lodName] = geom.findAllMatches('**/eye_ball*')
            self.eyeIris[lodName] = geom.findAllMatches('**/eye_iris*')
            self.eyeLids[lodName].stash()
            self.eyeBalls[lodName].unstash()
            self.eyeIris[lodName].unstash()
        

    
    def generateFaceTexture(self):
        faceTextureIdx = self.style.head.texture
        if self.gender == 'f':
            face_textures = PirateFemale.face_textures
        else:
            face_textures = PirateMale.face_textures
        tex_name = self.getTrySafe(face_textures, faceTextureIdx)
        if tex_name != None:
            tex = self.model.faceTextures.findTexture(tex_name)
            if tex == None:
                return None
            
        else:
            return None
        for lodName in self.getLODNames():
            self.findAllMatches('**/body_master_face').setTexture(tex, 1)
        

    
    def generateSkinColor(self):
        skinColor = self.style.getSkinColor()
        self.model.faces[0].setColorScale(skinColor)
        if self.model.newAvatars:
            self.model.currentBody.setColorScale(skinColor)
        else:
            numPaths = self.model.body.getNumPaths()
            if self.zombie:
                self.model.body.setColorScale(Vec4(1, 1, 1, 1))
            else:
                self.model.body.setColorScale(skinColor)

    
    def generateSkinTexture(self):
        bodyTextureIdx = self.style.body.skin
        if self.zombie:
            if self.gender == 'f':
                bodyTextureIdx = PirateFemale.ZOMB_BODY_TEXTURE
            else:
                bodyTextureIdx = PirateMale.ZOMB_BODY_TEXTURE
        
        if self.gender == 'f':
            body_textures = PirateFemale.body_textures[self.style.body.shape]
        else:
            body_textures = PirateMale.body_textures[self.style.body.shape]
        tex_name = self.getTrySafe(body_textures, bodyTextureIdx)
        if tex_name != None:
            tex = self.model.bodyTextures.findTexture(tex_name)
        else:
            return None
        for parts in self.model.bodys:
            parts.setTexture(tex, 1)
        

    
    def generateHairColor(self, colorName = None, colorModel = None):
        self.model.setHairBaseColor()

    
    def getTrySafe(self, list, idx):
        
        try:
            if type(idx) == str:
                lookup = idx.split('_cut')[0]
            else:
                lookup = idx
            return list[lookup]
        except:
            return None


    
    def generateEyesTexture(self):
        eyesTextureIdx = self.style.head.eyes.color
        if self.gender == 'f':
            eye_iris_textures = PirateFemale.eye_iris_textures
        else:
            eye_iris_textures = PirateMale.eye_iris_textures
        tex_name = self.getTrySafe(eye_iris_textures, eyesTextureIdx)
        if tex_name != None:
            tex = self.eyeIrisTextures.findTexture(tex_name)
        else:
            return None
        self.model.irises.setTexture(tex, 1)

    
    def generateHatColor(self):
        style = self.style
        if self.zombie:
            style = self.model.dnaZomb
        
        hatColor = style.lookupHatColor()
        geom = self.getGeomNode()
        geom.findAllMatches('**/hat_band*').setColorScale(hatColor)

    
    def generateClothesColor(self):
        style = self.style
        if self.zombie:
            style = self.model.dnaZomb
        
        clothesTopColor = style.lookupClothesTopColor()
        clothesBotColor = style.lookupClothesBotColor()
        geom = self.getGeomNode()
        geom.findAllMatches('**/clothing_layer1_shirt*').setColorScale(clothesTopColor[0])
        geom.findAllMatches('**/clothing_layer2_vest*').setColorScale(clothesTopColor[1])
        geom.findAllMatches('**/clothing_layer3_coat*').setColorScale(clothesTopColor[2])
        geom.findAllMatches('**/clothing_layer1_pant*').setColorScale(clothesBotColor[0])
        geom.findAllMatches('**/clothing_layer2_belt*').setColorScale(clothesBotColor[1])
        geom.findAllMatches('**/clothing_layer1_shoe*').setColorScale(clothesBotColor[2])

    
    def generateColor(self):
        self.generateSkinColor()
        self.generateHairColor()
        self.generateHatColor()

    
    def makeAnimDict(self, gender, animNames):
        self.animDict = []
        for currAnim in animNames:
            anim = animNames.get(currAnim)
            for currAnimName in anim:
                self.animTable.append([
                    currAnimName,
                    currAnimName])
            
        
        self.reducedAnimList = self.animDict

    
    def forceLoadAnimDict(self):
        for anim in self.animDict:
            self.getAnimControls(anim[0])
        

    
    def createAnimDict(self, customList = None):
        if self.gender == 'f':
            filePrefix = 'models/char/f'
            genderPrefix = 'f'
        else:
            filePrefix = 'models/char/m'
            genderPrefix = 'm'
        filePrefix += 'p'
        animList = self.reducedAnimList
        if animList is None:
            animList = AnimListDict[self.type]
        
        AnimDict.clear()
        for anim in animList:
            animSuffix = ''
            for customAnim in CustomAnimDict[genderPrefix + self.type]:
                if anim[0] == customAnim:
                    animSuffix = '_' + genderPrefix + NewModelDict.get(self.type)
                    break
                    continue
            
            AnimDict[anim[0]] = filePrefix + '_' + anim[1] + animSuffix
        
        if self.reducedAnimList is None:
            AnimDict.pop('intro')
        
        return filePrefix

    
    def generateBody(self, copy = 1):
        filePrefix = self.createAnimDict()
        lodString = '2000'
        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '2000', copy)
        self.loadAnims(AnimDict, 'modelRoot', 'all')
        if loader.loadModel(filePrefix + '_' + '1000', allowInstance = True) != None:
            lodString = '1000'
        
        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '1000', copy)
        if loader.loadModel(filePrefix + '_' + '500', allowInstance = True) != None:
            lodString = '500'
        
        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '500', copy)
        self.makeSubpart('head', [
            'zz_head01'], [])
        self.makeSubpart('torso', [
            'zz_spine01'], [
            'zz_head01'])
        self.makeSubpart('legs', [
            'dx_root'], [
            'zz_spine01'])
        self.setSubpartsComplete(True)
        self.getWeaponJoints()
        self.eyeIrisTextures = loader.loadModel('models/misc/eye_iris.bam')

    
    def refreshBody(self):
        if self.style.getGender() == 'f':
            gender = 1
            cjs = FemaleBodyShapeControlJoints
            matrix = FemaleBodyShapeControlJointMatrix
        else:
            gender = 0
            cjs = MaleBodyShapeControlJoints
            matrix = MaleBodyShapeControlJointMatrix
        type = self.style.getBodyShape()
        filePrefix = self.createAnimDict()
        self.loadAnims(AnimDict, 'modelRoot', 'all')
        for jointName in cjs:
            joint = self.find('**/*' + jointName)
            vector = matrix[jointName][type]
            if jointName.find('def') != -1:
                joint.setScale(vector)
                continue
            joint.setPos(vector)
        
        self.headNode.setScale(HeadScales[gender][self.style.getBodyShape()])
        self.setGlobalScale(self.calcBodyScale())
        self.createAnimDict()
        self.stop(self.getCurrentAnim())
        self.loop(self.getCurrentAnim())

    
    def setLODs(self):
        self.setLODNode()
        avatarDetail = base.config.GetString('avatar-detail', 'high')
        if avatarDetail == 'high':
            dist = [
                0,
                20,
                80,
                280]
        elif avatarDetail == 'med':
            dist = [
                0,
                10,
                40,
                280]
        elif avatarDetail == 'low':
            dist = [
                0,
                5,
                20,
                280]
        else:
            raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
        self.addLOD(2000, dist[1], dist[0])
        self.addLOD(1000, dist[2], dist[1])
        self.addLOD(500, dist[3], dist[2])
        if self.optimizeLOD:
            lowLOD = self.getLOD('500')
            lowLOD.setTextureOff(1000)
            lowLOD.setTransparency(0, 1000)
        
        self.getLODNode().setCenter(Point3(0, 0, 5))

    
    def showLOD(self, lodName):
        if not self.model.loaded:
            self.model.setupHead(lodName)
            self.model.setupBody(lodName)
            self.model.setupClothing(lodName)
            if self.master:
                self.model.setupSelectionChoices('NPC')
            
            self.model.loaded = 1
        
        self.model.setFromDNA()
        self.generateEyesTexture()
        if self.optimizeLOD:
            self.optimizeLowLOD()
        
        self.generateColor()

    
    def loadHuman(self, gender = 'm', other = None):
        if other:
            pirate = other
            pirate.style = self.style
        else:
            pirate = self
        pirate.gender = gender
        if self.loaded:
            return None
        
        if pirate.gender == 'f':
            pirate.type = BodyDefs.femaleFrames[pirate.style.getBodyShape()]
            controlShapes = PirateFemale.ControlShapes
            sliderNames = PirateFemale.SliderNames
        else:
            pirate.type = BodyDefs.maleFrames[pirate.style.getBodyShape()]
            controlShapes = PirateMale.ControlShapes
            sliderNames = PirateMale.SliderNames
        if not pirate.loaded:
            pirate.setLODs()
            pirate.loadAnimatedHead = True
            pirate.generateBody()
            if pirate.gender == 'f':
                pirate.model = PirateFemale.PirateFemale(pirate, pirate.style)
                self.pirateFemale = PirateFemale.PirateFemale(pirate, pirate.style)
            else:
                pirate.model = PirateMale.PirateMale(pirate, pirate.style)
                self.pirateMale = PirateMale.PirateMale(pirate, pirate.style)
            if base.config.GetBool('debug-dynamic-human', 0):
                pirate.model.newAvatars = True
            else:
                pirate.model.newAvatars = False
            pirate.faceAwayFromViewer()
            pirate.fixEyes()
        else:
            pirate.model.dna = pirate.style
            pirate.reducedAnimList = self.reducedAnimList
            pirate.createAnimDict()
            pirate.loadAnims(AnimDict, 'modelRoot', 'all')
        self.lods = pirate.getLODNames()
        if pirate.gender == 'f':
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 1
        else:
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 0
        pirate.zombie = self.zombie
        pirate.showLOD(2000)
        pirate.loaded = 1
        self.model = pirate.model
        if pirate.zombie:
            pirate.showZombie()
        else:
            pirate.showNormal()
        if hasattr(self, 'motionFSM'):
            self.motionFSM.setAvatar(self)
        
        self.controlShapes = controlShapes
        self.sliderNames = sliderNames
        if other:
            self.copyActor(other)
            self.fixEyes()
            self.copyHuman(other)
            self.undoExtraNodes()
        
        self.setupExtraNodes()
        self.applyBodyShaper()
        self.applyHeadShaper()
        if other:
            pirate.zombie = 0
            pirate.showNormal()
            pirate.unloadAnims(AnimDict, None, None)
            pirate.removeAnimControlDict()
            pirate.reducedAnimList = None
        
        self.initializeMiscNodes()
        self.startBlink()

    
    def initializeMiscNodes(self):
        self.initializeNametag3d()
        self.initializeDropShadow()
        if self.getLOD('2000') == None:
            return None
        
        exposedHeadJoint = self.getLOD('2000').find('**/def_head01')
        if not exposedHeadJoint.isEmpty():
            idx = 0
            if self.gender == 'f':
                idx = 1
            
            exposedHeadJoint.setScale(1)
            self.headNode.reparentTo(exposedHeadJoint)
            self.headNode.setScale(HeadScales[idx][self.style.getBodyShape()])
        

    
    def undoControlJoints(self):
        self.getGeomNode().getParent().findAllMatches('def_*').detach()
        self.getGeomNode().getParent().findAllMatches('trs_*').detach()
        self.findAllMatches('def_*').detach()
        self.findAllMatches('trs_*').detach()

    
    def cleanupHuman(self, gender = 'm'):
        self.eyeFSM.request('off')
        self.undoExtraNodes()
        self.undoControlJoints()
        self.eyeLids = { }
        self.eyeBalls = { }
        self.eyeIris = { }
        self.flush()
        self.loaded = 0
        self.master = 0

    
    def generateHuman(self, gender = 'm', others = None):
        other = None
        if others:
            if gender == 'f':
                other = others[1]
            else:
                other = others[0]
        
        if other and not (other.master) and other.loaded:
            other.cleanupHuman()
        elif self.loaded:
            self.cleanupHuman()
        
        self.loadHuman(self.style.gender, other)
        if self.isLocal():
            self.renderReflection = True
        
        self.setRenderReflection()
        self.disableMixing()
        self.enableMixing()

    generateHuman = quickProfile('loadHuman')(generateHuman)
    
    def getShadowJoint(self):
        return self

    
    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('legs', lodName)
            joint = bundle.findChild('name_tag')
            if joint:
                joints.append(joint)
                continue
        
        return joints

    
    def _DynamicHuman__blinkOpenEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() == 'closed':
            self.eyeFSM.request('open')
        
        r = self.randGen.random()
        if r < 0.10000000000000001:
            t = 0.20000000000000001
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self._DynamicHuman__blinkCloseEyes, self._DynamicHuman__blinkName)
        return Task.done

    
    def _DynamicHuman__blinkCloseEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() != 'open':
            taskMgr.doMethodLater(4.0, self._DynamicHuman__blinkCloseEyes, self._DynamicHuman__blinkName)
        else:
            self.eyeFSM.request('closed')
            taskMgr.doMethodLater(0.125, self._DynamicHuman__blinkOpenEyes, self._DynamicHuman__blinkName)
        return Task.done

    
    def startBlink(self):
        taskMgr.remove(self._DynamicHuman__blinkName)
        if self.eyeLids:
            self.openEyes()
        
        taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1, self._DynamicHuman__blinkCloseEyes, self._DynamicHuman__blinkName)

    
    def stopBlink(self):
        taskMgr.remove(self._DynamicHuman__blinkName)
        if self.eyeLids:
            self.eyeFSM.request('open')
        

    
    def closeEyes(self):
        self.eyeFSM.request('closed')

    
    def openEyes(self):
        self.eyeFSM.request('open')

    
    def enterEyeFSMOff(self):
        pass

    
    def exitEyeFSMOff(self):
        pass

    
    def enterEyeFSMOpen(self):
        for lodName in self.getLODNames():
            if not self.eyeLids[lodName].isEmpty():
                self.eyeLids[lodName].hide()
                self.eyeBalls[lodName].show()
                self.eyeIris[lodName].show()
                continue
        

    
    def exitEyeFSMOpen(self):
        pass

    
    def enterEyeFSMClosed(self):
        return None
        for lodName in self.getLODNames():
            if not self.eyeLids[lodName].isEmpty():
                self.eyeLids[lodName].show()
                self.eyeBalls[lodName].hide()
                self.eyeIris[lodName].hide()
                continue
        

    
    def exitEyeFSMClosed(self):
        pass

    
    def setControlValue(self, r, name):
        if self.style.getGender() == 'f':
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
        else:
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
        shapes = self.controlShapes
        ctl = shapes[name]
        slider = ctl[0]
        if r < 0.0:
            if len(ctl) > 1:
                slider = ctl[1]
            
        
        for i in range(0, len(slider)):
            jointName = slider[i][0]
            jointCtls = self.findAllMatches(jointName)
            posI = matrixI[jointName][0]
            hprI = matrixI[jointName][1]
            sclI = matrixI[jointName][2]
            posF = VBase3(posI[0], posI[1], posI[2])
            hprF = VBase3(hprI[0], hprI[1], hprI[2])
            sclF = VBase3(sclI[0], sclI[1], sclI[2])
            self.notify.debug('scv: %s initial %s' % (jointName, posI))
            dr = slider[i][4] * r
            ctl[0][i][5] = dr
            posDelta = VBase3(0, 0, 0)
            hprDelta = VBase3(0, 0, 0)
            sclDelta = VBase3(0, 0, 0)
            for sliderIdx in xrange(0, len(matrixF[jointName])):
                sliderName = matrixF[jointName][sliderIdx]
                jointSet = shapes[sliderName][0]
                for jointIdx in xrange(0, len(jointSet)):
                    if jointSet[jointIdx][0] == jointName:
                        if jointSet[jointIdx][1] == TX:
                            posDelta.setX(posDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == TY:
                            posDelta.setY(posDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == TZ:
                            posDelta.setZ(posDelta.getZ() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RX:
                            hprDelta.setX(hprDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RY:
                            hprDelta.setY(hprDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RZ:
                            hprDelta.setZ(hprDelta.getZ() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SX:
                            if r < 0.0:
                                sclDelta.setX(sclDelta.getX() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setX(sclDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SY:
                            if r < 0.0:
                                sclDelta.setY(sclDelta.getY() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setY(sclDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SZ:
                            if r < 0.0:
                                sclDelta.setZ(sclDelta.getZ() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setZ(sclDelta.getZ() + jointSet[jointIdx][5])
                        else:
                            self.notify.warning('scv:wrong element = %s' % jointSet[jointIdx][1])
                    jointSet[jointIdx][1] == TX
                
            
            self.notify.debug('scv: %s composite posDelta = %s' % (jointName, posDelta))
            posF.setX(posI[0] + posDelta[0])
            posF.setY(posI[1] + posDelta[1])
            posF.setZ(posI[2] + posDelta[2])
            self.notify.debug('scv: %s final posDelta%s' % (jointName, posF))
            self.notify.debug('scv: %s composite hprDelta = %s' % (jointName, hprDelta))
            hprF.setX(hprI[0] + hprDelta[0])
            hprF.setY(hprI[1] + hprDelta[1])
            hprF.setZ(hprI[2] + hprDelta[2])
            self.notify.debug('scv: %s final hprDelta%s' % (jointName, hprF))
            self.notify.debug('scv: %s composite sclDelta = %s' % (jointName, sclDelta))
            sclF.setX(sclI[0] + sclDelta[0])
            sclF.setY(sclI[1] + sclDelta[1])
            sclF.setZ(sclI[2] + sclDelta[2])
            self.notify.debug('scv: %s final sclDelta%s' % (jointName, sclF))
            for j in range(0, jointCtls.getNumPaths()):
                jointCtl = jointCtls[j]
                jointCtl.setPosHprScale(posF, hprF, sclF)
            
        

    
    def applyBodyShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleBodyShapeControlJoints
            matrix = FemaleBodyShapeControlJointMatrix
        else:
            cjs = MaleBodyShapeControlJoints
            matrix = MaleBodyShapeControlJointMatrix
        type = self.style.getBodyShape()
        for jointName in cjs:
            for lodName in self.getLODNames():
                if lodName == '2000':
                    joint = self.controlJoint(None, 'legs', jointName, lodName)
                    continue
                joint = self.controlJoint(joint, 'legs', jointName, lodName)
            
            joint = self.find('**/*' + jointName)
            vector = matrix[jointName][type]
            if jointName.find('def') != -1:
                joint.setScale(vector)
                continue
            joint.setPos(vector)
        

    
    def undoBodyShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleBodyShapeControlJoints
        else:
            cjs = MaleBodyShapeControlJoints

    
    def applyHeadShaper(self):
        self.createControlJoints()
        self.initHeadControlShapes()
        self.setHeadControlShapeValues()

    
    def undoHeadShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
        else:
            cjs = MaleHeadShapeControlJoints

    
    def createControlJoints(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
        else:
            cjs = MaleHeadShapeControlJoints
        for jointName in cjs:
            for lodName in self.getLODNames():
                if lodName == '2000':
                    joint = self.controlJoint(None, 'legs', jointName, lodName)
                    continue
                if lodName == '1000':
                    continue
                    continue
                if lodName == '500':
                    continue
                    continue
            
        

    
    def initHeadControlShapes(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
        else:
            cjs = MaleHeadShapeControlJoints
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
        if len(matrixF['initialized']) > 0:
            return None
        
        initializedMatrixI = len(matrixI['initialized'])
        initializedMatrixF = len(matrixF['initialized'])
        for jointName in cjs:
            transform = TransformState.makeMat(self.getJointTransform('legs', jointName, '2000'))
            pos = Vec3(transform.getPos())
            hpr = Vec3(transform.getHpr())
            scale = Vec3(transform.getScale())
            matrixI[jointName].append(pos)
            matrixI[jointName].append(hpr)
            matrixI[jointName].append(scale)
        
        matrixI['initialized'].append('initialized')
        shapes = self.controlShapes
        names = self.sliderNames
        for i in xrange(0, len(shapes)):
            slider = shapes[names[i]]
            for k in xrange(0, len(slider[0])):
                slider[0][k][4] = slider[0][k][2]
                if len(slider) > 1:
                    slider[1][k][4] = slider[1][k][2]
                    continue
            
        
        for i in xrange(0, len(shapes)):
            slider = shapes[names[i]]
            for k in xrange(0, len(slider[0])):
                jointCtl = slider[0][k]
                jointName = jointCtl[0]
                matrixF[jointName].append(names[i])
                pos = matrixI[jointName][0]
                hpr = matrixI[jointName][1]
                scl = matrixI[jointName][2]
                if jointCtl[1] < 3:
                    posDelta = jointCtl[4] - pos[jointCtl[1]]
                    jointCtl[4] = posDelta
                    if len(slider) > 1:
                        jointCtl = slider[1][k]
                        jointCtl[4] = posDelta
                    
                len(slider) > 1
                if jointCtl[1] > 2 and jointCtl[1] < 6:
                    hprDelta = jointCtl[4] - hpr[jointCtl[1] - 3]
                    jointCtl[4] = hprDelta
                    if len(slider) > 1:
                        jointCtl = slider[1][k]
                        jointCtl[4] = hprDelta
                    
                len(slider) > 1
                sclDelta = jointCtl[4] - scl[jointCtl[1] - 6]
                jointCtl[4] = sclDelta
                if len(slider) > 1:
                    jointCtl = slider[1][k]
                    jointCtl[4] = sclDelta
                    continue
            
        
        matrixF['initialized'].append('initialized')

    
    def setHeadControlShapeValues_old(self):
        value = self.style.getHeadSize()
        mappedValue = 0.90000000000000002 + (1 + value) * 0.10000000000000001
        self.extraNode.setScale(2 - mappedValue, mappedValue, 1)
        self.setControlValue(self.style.getHeadWidth(), 'headWidth')
        self.setControlValue(self.style.getHeadHeight(), 'headHeight')
        self.setControlValue(self.style.getHeadRoundness(), 'headRoundness')
        self.setControlValue(self.style.getJawWidth(), 'jawWidth')
        self.setControlValue(self.style.getJawAngle(), 'jawChinAngle')
        self.setControlValue(self.style.getJawChinSize(), 'jawChinSize')
        self.setControlValue(self.style.getJawLength(), 'jawLength')
        self.setControlValue(self.style.getMouthWidth(), 'mouthWidth')
        self.setControlValue(self.style.getMouthLipThickness(), 'mouthLipThickness')
        self.setControlValue(self.style.getCheekFat(), 'cheekFat')
        self.setControlValue(self.style.getBrowProtruding(), 'browProtruding')
        self.setControlValue(self.style.getEyeCorner(), 'eyeCorner')
        self.setControlValue(self.style.getEyeOpeningSize(), 'eyeOpeningSize')
        self.setControlValue(self.style.getEyeBulge(), 'eyeSpacing')
        self.setControlValue(self.style.getNoseBridgeWidth(), 'noseBridgeWidth')
        self.setControlValue(self.style.getNoseNostrilWidth(), 'noseNostrilWidth')
        self.setControlValue(self.style.getNoseLength(), 'noseLength')
        self.setControlValue(self.style.getNoseBump(), 'noseBump')
        self.setControlValue(self.style.getNoseNostrilHeight(), 'noseNostrilHeight')
        self.setControlValue(self.style.getNoseNostrilAngle(), 'noseNostrilAngle')
        self.setControlValue(self.style.getNoseBridgeBroke(), 'noseBridgeBroke')
        self.setControlValue(self.style.getNoseNostrilBroke(), 'noseNostrilBroke')
        self.setControlValue(self.style.getEarScale(), 'earScale')
        self.setControlValue(self.style.getEarFlapAngle(), 'earFlap')
        self.setControlValue(self.style.getEarPosition(), 'earPosition')

    
    def getGlobalScale(self):
        return self.scaleNode.getScale()

    
    def setGlobalScale(self, scale):
        self.scaleNode.setScale(scale)
        self.scaleNode.setZ(-(self.floorOffsetZ * (1 - scale)))

    
    def calcBodyScale(self):
        idx = 0
        if self.gender == 'f':
            idx = 1
        
        mappedValue = (0.80000000000000004 + (1 + self.style.getBodyHeight()) * 0.20000000000000001) * BodyScales[idx][self.style.getBodyShape()]
        return mappedValue

    
    def showZombie(self):
        self.model.irises.stash()
        self.model.faces[0].stash()
        self.model.faceZomb.unstash()
        self.generateSkinTexture()

    
    def showNormal(self):
        self.model.irises.unstash()
        self.model.faces[0].unstash()
        self.model.faceZomb.stash()
        self.generateSkinTexture()

    
    def takeAwayTexture(self, geoms, omitFace = False):
        emptyRenderState = RenderState.makeEmpty()
        eyeIrisColor = VBase4(0, 0, 0, 1)
        for i in range(0, geoms.getNumPaths()):
            element = geoms[i]
            if 'eye_iris' in element.getName():
                element.setColorScale(eyeIrisColor)
            elif omitFace and 'master_face' in element.getName():
                continue
            
            element.setTextureOff()
            geom = element.node()
            for j in range(0, geom.getNumGeoms()):
                geom.setGeomState(j, emptyRenderState)
            
        

    
    def optimizeMedLOD(self):
        medLOD = self.getLOD('1000')
        geoms = medLOD.findAllMatches('**/teeth*')
        geoms.stash()
        self.medSkinGone = False
        geoms = medLOD.find('**/body_forearm*')
        if geoms.isEmpty():
            self.medSkinGone = True
            geoms = medLOD.findAllMatches('**/body_*')
            self.takeAwayTexture(geoms, True)
        
        geoms = medLOD.findAllMatches('**/hair_*')
        self.takeAwayTexture(geoms)
        if self.gender != 'f':
            geoms = medLOD.findAllMatches('**/beard_*')
            self.takeAwayTexture(geoms)
            geoms = medLOD.findAllMatches('**/mustache_*')
            self.takeAwayTexture(geoms)
        
        geoms = medLOD.findAllMatches('**/eye_*')
        self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/clothing_layer2_belt_*')
        self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/clothing_layer1_shoe_*')
        self.takeAwayTexture(geoms)

    
    def optimizeLowLOD(self):
        lowLOD = self.getLOD('500')
        geoms = lowLOD.findAllMatches('**/teeth*')
        geoms.stash()
        geoms = lowLOD.findAllMatches('**/+GeomNode')
        self.takeAwayTexture(geoms)

    
    def setHeadControlShapeValues(self):
        value = self.style.getHeadSize()
        mappedValue = 0.90000000000000002 + (1 + value) * 0.10000000000000001
        self.extraNode.setScale(2 - mappedValue, mappedValue, 1)
        self.setControlValue_new(self.style.getHeadWidth(), 'headWidth')
        self.setControlValue_new(self.style.getHeadHeight(), 'headHeight')
        self.setControlValue_new(self.style.getHeadRoundness(), 'headRoundness')
        self.setControlValue_new(self.style.getJawWidth(), 'jawWidth')
        self.setControlValue_new(self.style.getJawAngle(), 'jawChinAngle')
        self.setControlValue_new(self.style.getJawChinSize(), 'jawChinSize')
        self.setControlValue_new(self.style.getJawLength(), 'jawLength')
        self.setControlValue_new(self.style.getMouthWidth(), 'mouthWidth')
        self.setControlValue_new(self.style.getMouthLipThickness(), 'mouthLipThickness')
        self.setControlValue_new(self.style.getCheekFat(), 'cheekFat')
        self.setControlValue_new(self.style.getBrowProtruding(), 'browProtruding')
        self.setControlValue_new(self.style.getEyeCorner(), 'eyeCorner')
        self.setControlValue_new(self.style.getEyeOpeningSize(), 'eyeOpeningSize')
        self.setControlValue_new(self.style.getEyeBulge(), 'eyeSpacing')
        self.setControlValue_new(self.style.getNoseBridgeWidth(), 'noseBridgeWidth')
        self.setControlValue_new(self.style.getNoseNostrilWidth(), 'noseNostrilWidth')
        self.setControlValue_new(self.style.getNoseLength(), 'noseLength')
        self.setControlValue_new(self.style.getNoseBump(), 'noseBump')
        self.setControlValue_new(self.style.getNoseNostrilHeight(), 'noseNostrilHeight')
        self.setControlValue_new(self.style.getNoseNostrilAngle(), 'noseNostrilAngle')
        self.setControlValue_new(self.style.getNoseBridgeBroke(), 'noseBridgeBroke')
        self.setControlValue_new(self.style.getNoseNostrilBroke(), 'noseNostrilBroke')
        self.setControlValue_new(self.style.getEarScale(), 'earScale')
        self.setControlValue_new(self.style.getEarFlapAngle(), 'earFlap')
        self.setControlValue_new(self.style.getEarPosition(), 'earPosition')
        self.postProcess_setHeadControlShapeValues()

    
    def setControlValue_new(self, r, name):
        ctl = self.controlShapes[name]
        zeroindex = ctl[0]
        sliders = zeroindex
        if r < 0.0:
            if len(ctl) > 1:
                sliders = ctl[1]
            
        
        for i in range(0, len(sliders)):
            zeroindex[i][5] = sliders[i][4] * r
        

    
    def postProcess_setHeadControlShapeValues(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
        else:
            cjs = MaleHeadShapeControlJoints
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
        posDelta = VBase3()
        hprDelta = VBase3()
        sclDelta = VBase3()
        fdict2 = {
            0: posDelta.addX,
            1: posDelta.addY,
            2: posDelta.addZ,
            3: hprDelta.addX,
            4: hprDelta.addY,
            5: hprDelta.addZ,
            6: sclDelta.addX,
            7: sclDelta.addY,
            8: sclDelta.addZ }
        for jointName in cjs:
            posDelta.assign(matrixI[jointName][0])
            hprDelta.assign(matrixI[jointName][1])
            sclDelta.assign(matrixI[jointName][2])
            for sliderIdx in xrange(0, len(matrixF[jointName])):
                sliderName = matrixF[jointName][sliderIdx]
                jointSet = self.controlShapes[sliderName][0]
                for sliderJoint in jointSet:
                    if sliderJoint[0] == jointName:
                        fdict2[sliderJoint[1]](sliderJoint[5])
                        continue
                
            
            self.find(jointName).setPosHprScale(posDelta, hprDelta, sclDelta)
class MasterHuman(HumanBase.HumanBase, Biped.Biped):
    notify = DirectNotifyGlobal.directNotify.newCategory('Human')
    prebuiltAnimData = { }

    def __init__(self, other = None):
        Biped.Biped.__init__(self, other, HumanAnimationMixer)
        self.model = None
        self.useFaceTex = True
        self.joints = { }
        self.jointTrans = { }
        self.jointTrans2 = { }
        self.zombie = False
        self.crazyColorSkin = False
        self.crazyColorSkinIndex = 0
        self.flattenPending = None
        self.optimizeLOD = base.config.GetBool('optimize-avatar-lod', 1)
        self.master = 0
        self.loaded = 0
        self.playingRate = None
        self.shadowFileName = 'models/misc/drop_shadow'
        self.setFont(PiratesGlobals.getInterfaceFont())
        self._MasterHuman__blinkName = 'blink-' + str(self.this)
        self.eyeLids = None
        self.eyeBalls = None
        self.eyeIris = None
        self.reducedAnimList = None
        self.rootScale = 1.0
        self.headNode = None
        self.extraNode = None
        self.scaleNode = None
        self.rootNode = None
        self.floorOffsetZ = 0.0
        self.headFudgeHpr = Vec3(0, 0, 0)
        self.frozeSomething = True
        self.randGen = random.Random()
        self.randGen.seed(random.random())
        self.eyeFSM = ClassicFSM('eyeFSM', [
            State('off', self.enterEyeFSMOff, self.exitEyeFSMOff, [
                'open',
                'closed']),
            State('open', self.enterEyeFSMOpen, self.exitEyeFSMOpen, [
                'closed',
                'off']),
            State('closed', self.enterEyeFSMClosed, self.exitEyeFSMClosed, [
                'open',
                'off'])], 'off', 'off')
        self.eyeFSM.enterInitialState()
        if other != None:
            self.copyHuman(other)

        self.isPaid = False


    def removeCopiedNodes(self):
        self.dropShadow = self.find('**/drop_shadow*')
        if not self.dropShadow.isEmpty():
            self.deleteDropShadow()
        else:
            self.dropShadow = None


    def flattenHuman(self):
        self.deleteNametag3d()
        self.getWeaponJoints()


    def _MasterHuman__doneFlattenHuman(self, models):
        self.flattenPending = None
        self.getWeaponJoints()


    def copyHuman(self, other):
        self.gender = other.gender
        self.loaded = other.loaded
        self.type = other.type
        self.loadAnimatedHead = other.loadAnimatedHead
        self.model = None


    def delete(self):

        try:
            pass
        except:
            self.Human_deleted = 1
            taskMgr.remove(self._MasterHuman__blinkName)
            if self.dropShadow and not self.dropShadow.isEmpty():
                self.deleteDropShadow()

            del self.eyeFSM
            self.controlShapes = None
            self.sliderNames = None
            if self.model:
                self.model.delete()
                del self.model

            Biped.Biped.delete(self)



    def isDeleted(self):

        try:
            if self.Human_deleted == 1:
                return True
        except:
            return False



    def fixEyes(self):
        self.eyeLids = { }
        self.eyeBalls = { }
        self.eyeIris = { }
        for lodName in self.getLODNames():
            geom = self.getPart('head', lodName)
            self.eyeLids[lodName] = geom.findAllMatches('**/*eyelid*')
            self.eyeBalls[lodName] = geom.findAllMatches('**/eye_ball*')
            self.eyeIris[lodName] = geom.findAllMatches('**/eye_iris*')
            self.eyeLids[lodName].stash()
            self.eyeBalls[lodName].unstash()
            self.eyeIris[lodName].unstash()



    def getCrazyColorSkinIndex(self):
        return self.crazyColorSkinIndex


    def setCrazyColorSkinIndex(self, index):
        if len(HumanDNA.crazySkinColors) > index:
            self.crazyColorSkinIndex = index
        else:
            self.notify.warning('(MasterHuman)index: %d is out of bounds for crazyColorSkin: %d' % (index, len(HumanDNA.crazySkinColors)))


    def generateSkinColor(self):
        skinColor = self.style.getSkinColor()
        lowColor = self.model.lowLODSkinColor
        color = VBase4(lowColor[0] * skinColor[0], lowColor[1] * skinColor[1], lowColor[2] * skinColor[2], 1.0)
        self.model.faces[0].setColorScale(skinColor)
        if self.model.newAvatars:
            self.model.currentBody.setColorScale(skinColor)
            if self.optimizeLOD:
                self.model.currentBody[2].setColorScale(color)
                self.model.faces[0][2].setColorScale(color)

        else:
            numPaths = self.model.body.getNumPaths()
            medIdx = numPaths / 3
            lowIdx = (numPaths / 3) * 2
            if self.zombie:
                self.model.body.setColorScale(Vec4(1, 1, 1, 1))
                if self.optimizeLOD:
                    color = VBase4(121 / 255.0, 124 / 255.0, 103 / 255.0, 1.0)
                    for i in xrange(lowIdx, numPaths):
                        self.model.body[i].setColorScale(color)

                    self.model.faceZomb[2].setColorScale(color)

            else:
                self.model.body.setColorScale(skinColor)
                lowColor = self.model.lowLODSkinColor
                if self.optimizeLOD:
                    color = VBase4(lowColor[0] * skinColor[0], lowColor[1] * skinColor[1], lowColor[2] * skinColor[2], 1.0)
                    for i in xrange(lowIdx, numPaths):
                        self.model.body[i].setColorScale(color)




    def generateSkinTexture(self):
        bodyTextureIdx = self.style.body.skin
        if self.zombie:
            if self.gender == 'f':
                bodyTextureIdx = PirateFemale.ZOMB_BODY_TEXTURE
            else:
                bodyTextureIdx = PirateMale.ZOMB_BODY_TEXTURE

        if self.gender == 'f':
            body_textures = PirateFemale.body_textures[self.style.body.shape]
        else:
            body_textures = PirateMale.body_textures[0] #self.style.body.shape]
        tex_name = self.getTrySafe(body_textures, bodyTextureIdx)
        if tex_name != None:
            tex = self.model.bodyTextures.findTexture(tex_name)
        else:
            return None
        if tex:
            for parts in self.model.bodys:
                numPaths = parts.getNumPaths()
                for i in xrange(numPaths):
                    parts[i].setTexture(tex, 1)





    def generateFaceTexture(self, default):
        if default:
            faceTextureIdx = 0
        else:
            faceTextureIdx = self.style.head.texture
        if faceTextureIdx >= len(self.model.faceTexturesSet):
            faceTextureIdx = 0

        self.model.faces[0].setTexture(self.model.faceTexturesSet[faceTextureIdx])


    def generateHairColor(self, colorName = None, colorModel = None):
        self.model.setHairBaseColor()


    def getTrySafe(self, list, idx):

        try:
            if type(idx) == str:
                lookup = idx.split('_cut')[0]
            else:
                lookup = idx
            return list[lookup]
        except:
            return None



    def generateEyesTexture(self):
        eyesTextureIdx = self.style.head.eyes.color
        if self.gender == 'f':
            eye_iris_textures = PirateFemale.eye_iris_textures
        else:
            eye_iris_textures = PirateMale.eye_iris_textures
        tex_name = self.getTrySafe(eye_iris_textures, eyesTextureIdx)
        if tex_name != None:
            tex = self.eyeIrisTextures.findTexture(tex_name)
        else:
            return None
        if tex:
            self.model.irises.setTexture(tex, 1)



    def generateHatColor(self):
        style = self.model.dna
        if self.zombie:
            style = self.model.dnaZomb

        hatColor = style.lookupHatColor()
        geom = self.getGeomNode()
        parts = geom.findAllMatches('**/hat_band*')
        parts.setColorScale(hatColor)


    def generateClothesColor(self):
        style = self.model.dna
        if self.zombie:
            style = self.model.dnaZomb

        clothesTopColor = style.lookupClothesTopColor()
        clothesBotColor = style.lookupClothesBotColor()
        geom = self.getGeomNode()
        if self.optimizeLOD:

            def tempColorParts(parts, ct):
                numPaths = parts.getNumPaths()
                lowIdx = (numPaths / 3) * 2
                for j in xrange(lowIdx):
                    parts[j].setColorScale(ct)

                for j in xrange(lowIdx, numPaths):
                    cl = parts[j].getColorScale()
                    compoundColor = VBase4(cl[0] * ct[0], cl[1] * ct[1], cl[2] * ct[2], 1.0)
                    parts[j].setColorScale(compoundColor)


        else:

            def tempColorParts(parts, ct):
                parts.setColorScale(ct)

        colorParts = tempColorParts
        parts = geom.findAllMatches('**/clothing_layer1_shirt*')
        colorParts(parts, clothesTopColor[0])
        parts = geom.findAllMatches('**/clothing_layer2_vest*')
        colorParts(parts, clothesTopColor[1])
        parts = geom.findAllMatches('**/clothing_layer3_coat*')
        colorParts(parts, clothesTopColor[2])
        parts = geom.findAllMatches('**/clothing_layer1_pant*')
        colorParts(parts, clothesBotColor[0])
        del colorParts


    def generateTexture(self):
        self.generateFaceTexture(not (self.useFaceTex))
        self.generateEyesTexture()


    def generateColor(self):
        self.generateSkinColor()
        self.generateHairColor()
        self.generateHatColor()


    def makeAnimDict(self, gender, animNames):
        self.animTable = []
        for currAnim in animNames:
            anim = animNames.get(currAnim)
            for currAnimName in anim:
                self.animTable.append([
                    currAnimName,
                    currAnimName])


        self.reducedAnimList = self.animTable


    def forceLoadAnimDict(self):
        for anim in self.animTable:
            self.getAnimControls(anim[0])



    def createAnimDict(self, customList = None):
        if self.reducedAnimList is None:
            self.animDict = self.prebuiltAnimData[self.gender + self.type]
            return None

        if self.gender == 'f':
            filePrefix = 'models/char/f'
            genderPrefix = 'f'
        else:
            filePrefix = 'models/char/m'
            genderPrefix = 'm'
        filePrefix += 'p'
        animList = self.reducedAnimList
        if animList is None:
            animList = AnimListDict[self.type]

        self.animDict = { }
        for anim in animList:
            animSuffix = ''
            for i in range(0, len(CustomAnimDict[genderPrefix + self.type])):
                if anim[0] == CustomAnimDict[genderPrefix + self.type][i]:
                    animSuffix = '_' + genderPrefix + NewModelDict.get(self.type)
                    break
                    continue

            self.animDict[anim[0]] = filePrefix + '_' + anim[1] + animSuffix

        if self.reducedAnimList is None:
            self.animDict.pop('intro')

        return filePrefix


    def generateBody(self, copy = 1):
        if self.gender == 'm':
            filePrefix = 'models/char/mp'
        else:
            filePrefix = 'models/char/fp'
        messenger.send('tick')
        lodString = '2000'
        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '2000', copy)
        messenger.send('tick')
        if loader.loadModel(filePrefix + '_' + '1000', allowInstance = True) != None:
            lodString = '1000'

        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '1000', copy)
        messenger.send('tick')
        if loader.loadModel(filePrefix + '_' + '500', allowInstance = True) != None:
            lodString = '500'

        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '500', copy)
        messenger.send('tick')
        self.makeSubpart('head', [
            'zz_head01'], [])
        self.makeSubpart('torso', [
            'zz_spine01'], [
            'zz_head01'])
        self.makeSubpart('legs', [
            'dx_root'], [
            'zz_spine01'])
        self.setSubpartsComplete(True)
        self.eyeIrisTextures = loader.loadModel('models/misc/eye_iris.bam')


    def setLODs(self):
        self.setLODNode()
        avatarDetail = base.config.GetString('avatar-detail', 'high')
        if avatarDetail == 'high':
            dist = [
                0,
                20,
                80,
                1000000000]
        elif avatarDetail == 'med':
            dist = [
                0,
                10,
                20,
                1000000000]
        elif avatarDetail == 'low':
            dist = [
                0,
                0,
                10,
                1000000000]
        else:
            raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
        self.addLOD(2000, dist[1], dist[0])
        self.addLOD(1000, dist[2], dist[1])
        self.addLOD(500, dist[3], dist[2])
        if self.optimizeLOD:
            lowLOD = self.getLOD('500')
            lowLOD.setTransparency(0, 1000)

        self.getLODNode().setCenter(Point3(0, 0, 5))


    def showLOD(self, lodName):
        self.generateTexture()
        self.model.setFromDNA()
        tex = self.model.faces[0][2].findTexture('*face*')


    def loadHuman(self, gender = 'm', other = None):
        self.gender = gender
        if self.gender == 'f':
            controlShapes = PirateFemale.ControlShapes
            sliderNames = PirateFemale.SliderNames
        else:
            controlShapes = PirateMale.ControlShapes
            sliderNames = PirateMale.SliderNames
        self.setLODs()
        self.loadAnimatedHead = True
        self.generateBody()
        if self.gender == 'f':
            self.type = BodyDefs.femaleFrames[self.style.getBodyShape()]
            self.model = PirateFemale.PirateFemale(self, self.style)
        else:
            self.type = BodyDefs.maleFrames[self.style.getBodyShape()]
            self.model = PirateMale.PirateMale(self, self.style)
        self.faceAwayFromViewer()
        self.lods = self.getLODNames()
        if self.gender == 'f':
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 1
        else:
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 0
        messenger.send('tick')
        self.model.setupHead(2000)
        messenger.send('tick')
        self.model.setupBody(2000)
        messenger.send('tick')
        self.model.setupClothing(2000)
        messenger.send('tick')
        if self.master:
            self.model.setupSelectionChoices('NPC')

        self.showNormal()
        self.createAnimDict()
        messenger.send('tick')
        self.initAnimsOnAllLODs([
            'head',
            'legs',
            'torso',
            'modelRoot'])
        messenger.send('tick')
        self.controlShapes = controlShapes
        self.sliderNames = sliderNames
        self.initHeadControlShapes()
        self.storeJoints()
        self.find('**/nametag3d').detachNode()
        self.findAllMatches('**/name_tag').detach()
        self.rootNode = self.getLOD('2000').find('**/dx_root')
        self.floorOffsetZ = self.rootNode.getZ()
        messenger.send('tick')
        root = self.getLOD('500')
        gr = SceneGraphReducer()
        gr.applyAttribs(root.node(), gr.TTTransform | gr.TTTexMatrix | gr.TTOther)
        gr.makeCompatibleFormat(root.node(), 0)
        gr.premunge(root.node(), RenderState.makeEmpty())
        gr.decompose(root.node())
        stashedSet = root.findAllMatches('**/@@*')
        stashedSet.unstash()
        gr.makeCompatibleFormat(root.node(), 0)
        stashedSet.stash()
        messenger.send('tick')
        for face in self.model.faces[0]:
            node = face.node()
            for i in range(node.getNumGeoms()):
                face.node().setGeomState(i, RenderState.makeEmpty())




    def initializeMiscNodes(self):
        self.initializeNametag3d()
        self.initializeDropShadow()
        if self.getLOD('2000') == None:
            return None

        exposedHeadJoint = self.getLOD('2000').find('**/def_head01')
        if not exposedHeadJoint.isEmpty():
            idx = 0
            if self.gender == 'f':
                idx = 1

            exposedHeadJoint.setScale(1)
            self.headNode.reparentTo(exposedHeadJoint)
            self.headNode.setScale(HeadScales[idx][self.style.getBodyShape()])



    def undoControlJoints(self):
        self.getGeomNode().getParent().findAllMatches('def_*').detach()
        self.getGeomNode().getParent().findAllMatches('trs_*').detach()
        self.findAllMatches('def_*').detach()
        self.findAllMatches('trs_*').detach()


    def cleanupHuman(self, gender = 'm'):
        self.eyeFSM.request('off')
        self.undoControlJoints()
        self.eyeLids = { }
        self.eyeBalls = { }
        self.eyeIris = { }
        self.flush()
        self.loaded = 0
        self.master = 0


    def generateHuman(self, gender = 'm'):
        self.loadHuman(self.style.gender)


    def getShadowJoint(self):
        return self


    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('legs', lodName)
            joint = bundle.findChild('name_tag')
            if joint:
                joints.append(joint)
                continue

        return joints


    def _MasterHuman__blinkOpenEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() == 'closed':
            self.eyeFSM.request('open')

        r = self.randGen.random()
        if r < 0.10000000000000001:
            t = 0.20000000000000001
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self._MasterHuman__blinkCloseEyes, self._MasterHuman__blinkName)
        return Task.done


    def _MasterHuman__blinkCloseEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() != 'open':
            taskMgr.doMethodLater(4.0, self._MasterHuman__blinkCloseEyes, self._MasterHuman__blinkName)
        else:
            self.eyeFSM.request('closed')
            taskMgr.doMethodLater(0.125, self._MasterHuman__blinkOpenEyes, self._MasterHuman__blinkName)
        return Task.done


    def startBlink(self):
        taskMgr.remove(self._MasterHuman__blinkName)
        if self.eyeLids:
            self.openEyes()

        taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1, self._MasterHuman__blinkCloseEyes, self._MasterHuman__blinkName)


    def stopBlink(self):
        taskMgr.remove(self._MasterHuman__blinkName)
        if self.eyeLids:
            self.eyeFSM.request('open')



    def closeEyes(self):
        self.eyeFSM.request('closed')


    def openEyes(self):
        self.eyeFSM.request('open')


    def enterEyeFSMOff(self):
        pass


    def exitEyeFSMOff(self):
        pass


    def enterEyeFSMOpen(self):
        for lodName in self.getLODNames():
            if not self.eyeLids[lodName].isEmpty():
                self.eyeLids[lodName].hide()
                self.eyeBalls[lodName].show()
                self.eyeIris[lodName].show()
                continue



    def exitEyeFSMOpen(self):
        pass


    def enterEyeFSMClosed(self):
        return None
        for lodName in self.getLODNames():
            if not self.eyeLids[lodName].isEmpty():
                self.eyeLids[lodName].show()
                self.eyeBalls[lodName].hide()
                self.eyeIris[lodName].hide()
                continue



    def exitEyeFSMClosed(self):
        pass


    def setControlValue(self, r, name):
        if self.style.getGender() == 'f':
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
        else:
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
        shapes = self.controlShapes
        ctl = shapes[name]
        slider = ctl[0]
        if r < 0.0:
            if len(ctl) > 1:
                slider = ctl[1]


        for i in range(0, len(slider)):
            jointName = slider[i][0]
            jointCtls = self.findAllMatches(jointName)
            posI = matrixI[jointName][0]
            hprI = matrixI[jointName][1]
            sclI = matrixI[jointName][2]
            posF = VBase3(posI[0], posI[1], posI[2])
            hprF = VBase3(hprI[0], hprI[1], hprI[2])
            sclF = VBase3(sclI[0], sclI[1], sclI[2])
            self.notify.debug('scv: %s initial %s' % (jointName, posI))
            dr = slider[i][4] * r
            ctl[0][i][5] = dr
            posDelta = VBase3(0, 0, 0)
            hprDelta = VBase3(0, 0, 0)
            sclDelta = VBase3(0, 0, 0)
            for sliderIdx in xrange(0, len(matrixF[jointName])):
                sliderName = matrixF[jointName][sliderIdx]
                jointSet = shapes[sliderName][0]
                for jointIdx in xrange(0, len(jointSet)):
                    if jointSet[jointIdx][0] == jointName:
                        if jointSet[jointIdx][1] == TX:
                            posDelta.setX(posDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == TY:
                            posDelta.setY(posDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == TZ:
                            posDelta.setZ(posDelta.getZ() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RX:
                            hprDelta.setX(hprDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RY:
                            hprDelta.setY(hprDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RZ:
                            hprDelta.setZ(hprDelta.getZ() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SX:
                            if r < 0.0:
                                sclDelta.setX(sclDelta.getX() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setX(sclDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SY:
                            if r < 0.0:
                                sclDelta.setY(sclDelta.getY() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setY(sclDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SZ:
                            if r < 0.0:
                                sclDelta.setZ(sclDelta.getZ() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setZ(sclDelta.getZ() + jointSet[jointIdx][5])
                        else:
                            self.notify.warning('scv:wrong element = %s' % jointSet[jointIdx][1])
                    jointSet[jointIdx][1] == TX


            self.notify.debug('scv: %s composite posDelta = %s' % (jointName, posDelta))
            posF.setX(posI[0] + posDelta[0])
            posF.setY(posI[1] + posDelta[1])
            posF.setZ(posI[2] + posDelta[2])
            self.notify.debug('scv: %s final posDelta%s' % (jointName, posF))
            self.notify.debug('scv: %s composite hprDelta = %s' % (jointName, hprDelta))
            hprF.setX(hprI[0] + hprDelta[0])
            hprF.setY(hprI[1] + hprDelta[1])
            hprF.setZ(hprI[2] + hprDelta[2])
            self.notify.debug('scv: %s final hprDelta%s' % (jointName, hprF))
            self.notify.debug('scv: %s composite sclDelta = %s' % (jointName, sclDelta))
            sclF.setX(sclI[0] + sclDelta[0])
            sclF.setY(sclI[1] + sclDelta[1])
            sclF.setZ(sclI[2] + sclDelta[2])
            self.notify.debug('scv: %s final sclDelta%s' % (jointName, sclF))
            for j in range(0, jointCtls.getNumPaths()):
                jointCtl = jointCtls[j]
                jointCtl.setPosHprScale(posF, hprF, sclF)




    def applyBodyShaper(self):
        if self.style.getGender() == 'f':
            tjs = FemaleBodyShapeTranslateJoints
            sjs = FemaleBodyShapeScaleJoints
            matrix = FemaleBodyShapeControlJointMatrix
        else:
            tjs = MaleBodyShapeTranslateJoints
            sjs = MaleBodyShapeScaleJoints
            matrix = MaleBodyShapeControlJointMatrix
        type = 0 #self.style.getBodyShape()
        for jointName in tjs:
            transData = self.jointTrans[jointName]
            vector = matrix[jointName][type]
            self.joints[jointName].applyFreezeMatrix(vector, transData[1], transData[2])

        for jointName in sjs:
            transData = self.jointTrans[jointName]
            vector = matrix[jointName][type]
            self.joints[jointName].applyFreezeMatrix(transData[0], transData[1], vector)

        value = self.style.getHeadSize()
        mappedValue = 0.90000000000000002 + (1 + value) * 0.10000000000000001
        transData = self.jointTrans['def_extra_jt']
        self.joints['def_extra_jt'].applyFreezeMatrix(transData[0], transData[1], Vec3(2 - mappedValue, mappedValue, 1))
        transData = self.jointTrans['def_head01']
        idx = 0
        if self.style.gender == 'f':
            idx = 1

        self.joints['def_head01'].applyFreezeMatrix(transData[0], transData[1], Vec3(HeadScales[idx][0])) #self.style.getBodyShape()]))
        self.setGlobalScale(self.calcBodyScale())


    def undoBodyShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleBodyShapeControlJoints
        else:
            cjs = MaleBodyShapeControlJoints


    def applyHeadShaper(self):
        self.setHeadControlShapeValues()


    def undoHeadShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
        else:
            cjs = MaleHeadShapeControlJoints


    def createControlJoints(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
        else:
            cjs = MaleHeadShapeControlJoints
        for jointName in cjs:
            for lodName in self.getLODNames():
                if lodName == '2000':
                    joint = self.controlJoint(None, 'legs', jointName, lodName)
                    continue
                if lodName == '1000':
                    continue
                    continue
                if lodName == '500':
                    continue
                    continue




    def initHeadControlShapes(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
            matrixIHelper = FemaleHeadInitHelper
        else:
            cjs = MaleHeadShapeControlJoints
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
            matrixIHelper = MaleHeadInitHelper
        if len(matrixF['initialized']) > 0:
            return None

        initializedMatrixI = len(matrixI['initialized'])
        initializedMatrixF = len(matrixF['initialized'])
        for jointName in cjs:
            transform = TransformState.makeMat(self.getJointTransform('legs', jointName, '2000'))
            pos = Vec3(transform.getPos())
            hpr = Vec3(transform.getHpr())
            scale = Vec3(transform.getScale())
            matrixI[jointName].append(pos)
            matrixI[jointName].append(hpr)
            matrixI[jointName].append(scale)
            matrixIHelper[jointName] = [
                pos[0],
                pos[1],
                pos[2],
                hpr[0],
                hpr[1],
                hpr[2],
                scale[0],
                scale[1],
                scale[2]]

        matrixI['initialized'].append('initialized')
        shapes = self.controlShapes
        names = self.sliderNames
        for i in xrange(0, len(shapes)):
            slider = shapes[names[i]]
            for k in xrange(0, len(slider[0])):
                slider[0][k][4] = slider[0][k][2]
                if len(slider) > 1:
                    slider[1][k][4] = slider[1][k][2]
                    continue


        for i in xrange(0, len(shapes)):
            slider = shapes[names[i]]
            for k in xrange(0, len(slider[0])):
                jointCtl = slider[0][k]
                jointName = jointCtl[0]
                matrixF[jointName].append(names[i])
                pos = matrixI[jointName][0]
                hpr = matrixI[jointName][1]
                scl = matrixI[jointName][2]
                if jointCtl[1] < 3:
                    posDelta = jointCtl[4] - pos[jointCtl[1]]
                    jointCtl[4] = posDelta
                    if len(slider) > 1:
                        jointCtl = slider[1][k]
                        jointCtl[4] = posDelta

                len(slider) > 1
                if jointCtl[1] > 2 and jointCtl[1] < 6:
                    hprDelta = jointCtl[4] - hpr[jointCtl[1] - 3]
                    jointCtl[4] = hprDelta
                    if len(slider) > 1:
                        jointCtl = slider[1][k]
                        jointCtl[4] = hprDelta

                len(slider) > 1
                sclDelta = 12 #jointCtl[4] - scl[jointCtl[1] - 6]
                jointCtl[4] = sclDelta
                if len(slider) > 1:
                    jointCtl = slider[1][k]
                    jointCtl[4] = sclDelta
                    continue


        matrixF['initialized'].append('initialized')


    def setHeadControlShapeValues_old(self):
        value = self.style.getHeadSize()
        mappedValue = 0.90000000000000002 + (1 + value) * 0.10000000000000001
        self.setControlValue(self.style.getHeadWidth(), 'headWidth')
        self.setControlValue(self.style.getHeadHeight(), 'headHeight')
        self.setControlValue(self.style.getHeadRoundness(), 'headRoundness')
        self.setControlValue(self.style.getJawWidth(), 'jawWidth')
        self.setControlValue(self.style.getJawAngle(), 'jawChinAngle')
        self.setControlValue(self.style.getJawChinSize(), 'jawChinSize')
        self.setControlValue(self.style.getJawLength(), 'jawLength')
        self.setControlValue(self.style.getMouthWidth(), 'mouthWidth')
        self.setControlValue(self.style.getMouthLipThickness(), 'mouthLipThickness')
        self.setControlValue(self.style.getCheekFat(), 'cheekFat')
        self.setControlValue(self.style.getBrowProtruding(), 'browProtruding')
        self.setControlValue(self.style.getEyeCorner(), 'eyeCorner')
        self.setControlValue(self.style.getEyeOpeningSize(), 'eyeOpeningSize')
        self.setControlValue(self.style.getEyeBulge(), 'eyeSpacing')
        self.setControlValue(self.style.getNoseBridgeWidth(), 'noseBridgeWidth')
        self.setControlValue(self.style.getNoseNostrilWidth(), 'noseNostrilWidth')
        self.setControlValue(self.style.getNoseLength(), 'noseLength')
        self.setControlValue(self.style.getNoseBump(), 'noseBump')
        self.setControlValue(self.style.getNoseNostrilHeight(), 'noseNostrilHeight')
        self.setControlValue(self.style.getNoseNostrilAngle(), 'noseNostrilAngle')
        self.setControlValue(self.style.getNoseBridgeBroke(), 'noseBridgeBroke')
        self.setControlValue(self.style.getNoseNostrilBroke(), 'noseNostrilBroke')
        self.setControlValue(self.style.getEarScale(), 'earScale')
        self.setControlValue(self.style.getEarFlapAngle(), 'earFlap')
        self.setControlValue(self.style.getEarPosition(), 'earPosition')


    def getGlobalScale(self):
        return self.scaleNode.getScale()


    def setGlobalScale(self, scale):
        transData = self.jointTrans['def_head01']
        pos = Vec3(transData[0])
        pos.setZ(-(self.floorOffsetZ * (1 - scale)))
        self.joints['def_scale_jt'].applyFreezeMatrix(pos, transData[1], Vec3(scale))
        self.rootScale = scale


    def calcBodyScale(self):
        idx = 0
        if self.gender == 'f':
            idx = 1

        mappedValue = (0.80000000000000004 + (1 + self.style.getBodyHeight()) * 0.20000000000000001) * BodyScales[idx][0]#self.style.getBodyShape()]
        return mappedValue


    def showZombie(self):
        self.model.irises.stash()
        self.model.faces[0].stash()
        self.model.faceZomb.unstash()
        self.generateSkinTexture()


    def showNormal(self):
        self.model.irises.unstash()
        self.model.faces[0].unstash()
        self.model.faceZomb.stash()
        self.generateSkinTexture()


    def takeAwayTexture(self, geoms, omitFace = False):
        emptyRenderState = RenderState.makeEmpty()
        eyeIrisColor = VBase4(0, 0, 0, 1)
        for i in range(0, geoms.getNumPaths()):
            element = geoms[i]
            if 'eye_iris' in element.getName():
                element.setColorScale(eyeIrisColor)
            elif omitFace and 'master_face' in element.getName():
                continue

            element.setTextureOff()
            geom = element.node()
            for j in range(0, geom.getNumGeoms()):
                geom.setGeomState(j, emptyRenderState)




    def optimizeMedLOD(self):
        medLOD = self.getLOD('1000')
        geoms = medLOD.findAllMatches('**/teeth*')
        geoms.stash()
        self.medSkinGone = False
        geoms = medLOD.find('**/body_forearm*')
        if geoms.isEmpty():
            self.medSkinGone = True
            geoms = medLOD.findAllMatches('**/body_*')
            self.takeAwayTexture(geoms, True)

        geoms = medLOD.findAllMatches('**/hair_*')
        self.takeAwayTexture(geoms)
        if self.gender != 'f':
            geoms = medLOD.findAllMatches('**/beard_*')
            self.takeAwayTexture(geoms)
            geoms = medLOD.findAllMatches('**/mustache_*')
            self.takeAwayTexture(geoms)

        geoms = medLOD.findAllMatches('**/eye_*')
        self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/clothing_layer2_belt_*')
        self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/clothing_layer1_shoe_*')
        self.takeAwayTexture(geoms)


    def optimizeLowLOD(self):
        lowLOD = self.getLOD('500')
        geoms = lowLOD.findAllMatches('**/teeth*')
        geoms.stash()
        geoms = lowLOD.findAllMatches('**/+GeomNode')
        self.takeAwayTexture(geoms)


    def setHeadControlShapeValues(self):
        value = self.style.getHeadSize()
        mappedValue = 0.90000000000000002 + (1 + value) * 0.10000000000000001
        self.setControlValue_new(self.style.getHeadWidth(), 'headWidth')
        self.setControlValue_new(self.style.getHeadHeight(), 'headHeight')
        self.setControlValue_new(self.style.getHeadRoundness(), 'headRoundness')
        self.setControlValue_new(self.style.getJawWidth(), 'jawWidth')
        self.setControlValue_new(self.style.getJawAngle(), 'jawChinAngle')
        self.setControlValue_new(self.style.getJawChinSize(), 'jawChinSize')
        self.setControlValue_new(self.style.getJawLength(), 'jawLength')
        self.setControlValue_new(self.style.getMouthWidth(), 'mouthWidth')
        self.setControlValue_new(self.style.getMouthLipThickness(), 'mouthLipThickness')
        self.setControlValue_new(self.style.getCheekFat(), 'cheekFat')
        self.setControlValue_new(self.style.getBrowProtruding(), 'browProtruding')
        self.setControlValue_new(self.style.getEyeCorner(), 'eyeCorner')
        self.setControlValue_new(self.style.getEyeOpeningSize(), 'eyeOpeningSize')
        self.setControlValue_new(self.style.getEyeBulge(), 'eyeSpacing')
        self.setControlValue_new(self.style.getNoseBridgeWidth(), 'noseBridgeWidth')
        self.setControlValue_new(self.style.getNoseNostrilWidth(), 'noseNostrilWidth')
        self.setControlValue_new(self.style.getNoseLength(), 'noseLength')
        self.setControlValue_new(self.style.getNoseBump(), 'noseBump')
        self.setControlValue_new(self.style.getNoseNostrilHeight(), 'noseNostrilHeight')
        self.setControlValue_new(self.style.getNoseNostrilAngle(), 'noseNostrilAngle')
        self.setControlValue_new(self.style.getNoseBridgeBroke(), 'noseBridgeBroke')
        self.setControlValue_new(self.style.getNoseNostrilBroke(), 'noseNostrilBroke')
        self.setControlValue_new(self.style.getEarScale(), 'earScale')
        self.setControlValue_new(self.style.getEarFlapAngle(), 'earFlap')
        self.setControlValue_new(self.style.getEarPosition(), 'earPosition')
        self.postProcess_setHeadControlShapeValues()


    def setControlValue_new(self, r, name):
        ctl = self.controlShapes[name]
        zeroindex = ctl[0]
        sliders = zeroindex
        if r < 0.0:
            if len(ctl) > 1:
                sliders = ctl[1]


        for i in range(0, len(sliders)):
            zeroindex[i][5] = sliders[i][4] * r



    def postProcess_setHeadControlShapeValues(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixIHelper = FemaleHeadInitHelper
        else:
            cjs = MaleHeadShapeControlJoints
            matrixF = MaleHeadShapeControlJointMatrix
            matrixIHelper = MaleHeadInitHelper
        for jointName in cjs:
            transList = list(matrixIHelper[jointName])
            for sliderName in matrixF[jointName]:
                for sliderJoint in self.controlShapes[sliderName][0]:
                    if sliderJoint[0] == jointName:
                        transList[sliderJoint[1]] += sliderJoint[5]
                        continue


            self.joints[jointName].applyFreezeMatrix(Vec3(*transList[0:3]), Vec3(*transList[3:6]), Vec3(*transList[6:9]))



    def quickGetJointTransform(self, jointName):
        return self.joints[jointName][0].getDefaultValue()


    def storeJoints(self):
        if self.style.gender == 'm':
            bJoints = MaleBodyShapeControlJoints
            hJoints = MaleHeadShapeControlJoints
        else:
            bJoints = FemaleBodyShapeControlJoints
            hJoints = FemaleHeadShapeControlJoints
        for name in bJoints + hJoints + ('def_head01', 'def_extra_jt', 'def_scale_jt'):
            joint = self.getJoints(jointName = name)
            self.joints[name] = joint[0]
            ts = TransformState.makeMat(joint[0].getDefaultValue())
            self.jointTrans[name] = (Vec3(ts.getPos()), Vec3(ts.getHpr()), Vec3(ts.getScale()))



    def setupAnimDicts(cls):
        for t in BodyDefs.maleFrames:
            cls.storeAnimDict('models/char/mp', 'm', t)

        for t in BodyDefs.femaleFrames:
            cls.storeAnimDict('models/char/fp', 'f', t)


    setupAnimDicts = classmethod(setupAnimDicts)

    def storeAnimDict(cls, prefix, gender, type):
        qualifier = gender + type
        animList = AnimListDict[type]
        cls.prebuiltAnimData[qualifier] = { }
        for anim in animList:
            animSuffix = ''
            for i in range(0, len(CustomAnimDict[qualifier])):
                if anim[0] == CustomAnimDict[qualifier][i]:
                    animSuffix = '_' + gender + NewModelDict.get(type)
                    break
                    continue

            cls.prebuiltAnimData[qualifier][anim[0]] = prefix + '_' + anim[1] + animSuffix

        cls.prebuiltAnimData[qualifier].pop('intro')

    storeAnimDict = classmethod(storeAnimDict)
예제 #3
0
class MasterHuman(HumanBase.HumanBase, Biped.Biped):
    __module__ = __name__
    notify = DirectNotifyGlobal.directNotify.newCategory('Human')
    prebuiltAnimData = {}

    def __init__(self, other=None):
        Biped.Biped.__init__(self, other, HumanAnimationMixer)
        self.model = None
        self.useFaceTex = True
        self.joints = {}
        self.jointTrans = {}
        self.jointTrans2 = {}
        self.zombie = False
        self.crazyColorSkin = False
        self.crazyColorSkinIndex = 0
        self.flattenPending = None
        self.optimizeLOD = base.config.GetBool('optimize-avatar-lod', 1)
        self.master = 0
        self.loaded = 0
        self.playingRate = None
        self.shadowFileName = 'models/misc/drop_shadow'
        self.setFont(PiratesGlobals.getInterfaceFont())
        self.__blinkName = 'blink-' + str(self.this)
        self.eyeLids = None
        self.eyeBalls = None
        self.eyeIris = None
        self.reducedAnimList = None
        self.rootScale = 1.0
        self.headNode = None
        self.extraNode = None
        self.scaleNode = None
        self.rootNode = None
        self.floorOffsetZ = 0.0
        self.headFudgeHpr = Vec3(0, 0, 0)
        self.frozeSomething = True
        self.randGen = random.Random()
        self.randGen.seed(random.random())
        self.eyeFSM = ClassicFSM('eyeFSM', [
         State('off', self.enterEyeFSMOff, self.exitEyeFSMOff, [
          'open', 'closed']),
         State('open', self.enterEyeFSMOpen, self.exitEyeFSMOpen, [
          'closed', 'off']),
         State('closed', self.enterEyeFSMClosed, self.exitEyeFSMClosed, [
          'open', 'off'])], 'off', 'off')
        self.eyeFSM.enterInitialState()
        if other != None:
            self.copyHuman(other)
        self.isPaid = False
        return

    def removeCopiedNodes(self):
        self.dropShadow = self.find('**/drop_shadow*')
        if not self.dropShadow.isEmpty():
            self.deleteDropShadow()
        else:
            self.dropShadow = None
        return

    def flattenHuman(self):
        self.deleteNametag3d()
        self.getWeaponJoints()

    def __doneFlattenHuman(self, models):
        self.flattenPending = None
        self.getWeaponJoints()
        return

    def copyHuman(self, other):
        self.gender = other.gender
        self.loaded = other.loaded
        self.type = other.type
        self.loadAnimatedHead = other.loadAnimatedHead
        self.model = None
        return

    def delete(self):
        try:
            self.Human_deleted
        except:
            self.Human_deleted = 1
            taskMgr.remove(self.__blinkName)
            if self.dropShadow and not self.dropShadow.isEmpty():
                self.deleteDropShadow()
            del self.eyeFSM
            self.controlShapes = None
            self.sliderNames = None
            if self.model:
                self.model.delete()
                del self.model
            Biped.Biped.delete(self)

        return

    def isDeleted(self):
        try:
            self.Human_deleted
            if self.Human_deleted == 1:
                return True
        except:
            return False

    def fixEyes(self):
        self.eyeLids = {}
        self.eyeBalls = {}
        self.eyeIris = {}
        for lodName in self.getLODNames():
            geom = self.getPart('head', lodName)
            self.eyeLids[lodName] = geom.findAllMatches('**/*eyelid*')
            self.eyeBalls[lodName] = geom.findAllMatches('**/eye_ball*')
            self.eyeIris[lodName] = geom.findAllMatches('**/eye_iris*')
            self.eyeLids[lodName].stash()
            self.eyeBalls[lodName].unstash()
            self.eyeIris[lodName].unstash()

    def getCrazyColorSkinIndex(self):
        return self.crazyColorSkinIndex

    def setCrazyColorSkinIndex(self, index):
        if len(HumanDNA.crazySkinColors) > index:
            self.crazyColorSkinIndex = index
        else:
            self.notify.warning('(MasterHuman)index: %d is out of bounds for crazyColorSkin: %d' % (index, len(HumanDNA.crazySkinColors)))

    def generateSkinColor(self):
        skinColor = self.style.getSkinColor()
        lowColor = self.model.lowLODSkinColor
        color = VBase4(lowColor[0] * skinColor[0], lowColor[1] * skinColor[1], lowColor[2] * skinColor[2], 1.0)
        self.model.faces[0].setColorScale(skinColor)
        if self.model.newAvatars:
            self.model.currentBody.setColorScale(skinColor)
            if self.optimizeLOD:
                self.model.currentBody[2].setColorScale(color)
                self.model.faces[0][2].setColorScale(color)
        else:
            numPaths = self.model.body.getNumPaths()
            medIdx = numPaths / 3
            lowIdx = numPaths / 3 * 2
            if self.zombie:
                self.model.body.setColorScale(Vec4(1, 1, 1, 1))
                if self.optimizeLOD:
                    color = VBase4(121 / 255.0, 124 / 255.0, 103 / 255.0, 1.0)
                    for i in xrange(lowIdx, numPaths):
                        self.model.body[i].setColorScale(color)

                    self.model.faceZomb[2].setColorScale(color)
            else:
                self.model.body.setColorScale(skinColor)
                lowColor = self.model.lowLODSkinColor
                if self.optimizeLOD:
                    color = VBase4(lowColor[0] * skinColor[0], lowColor[1] * skinColor[1], lowColor[2] * skinColor[2], 1.0)
                    for i in xrange(lowIdx, numPaths):
                        self.model.body[i].setColorScale(color)

    def generateSkinTexture(self):
        bodyTextureIdx = self.style.body.skin
        if self.zombie:
            if self.gender == 'f':
                bodyTextureIdx = PirateFemale.ZOMB_BODY_TEXTURE
            else:
                bodyTextureIdx = PirateMale.ZOMB_BODY_TEXTURE
        if self.gender == 'f':
            body_textures = PirateFemale.body_textures[self.style.body.shape]
        else:
            body_textures = PirateMale.body_textures[self.style.body.shape]
        tex_name = self.getTrySafe(body_textures, bodyTextureIdx)
        if tex_name != None:
            tex = self.model.bodyTextures.findTexture(tex_name)
        else:
            return
        if tex:
            for parts in self.model.bodys:
                numPaths = parts.getNumPaths()
                for i in xrange(numPaths):
                    parts[i].setTexture(tex, 1)

        return

    def generateFaceTexture(self, default):
        if default:
            faceTextureIdx = 0
        else:
            faceTextureIdx = self.style.head.texture
        if faceTextureIdx >= len(self.model.faceTexturesSet):
            faceTextureIdx = 0
        self.model.faces[0].setTexture(self.model.faceTexturesSet[faceTextureIdx])

    def generateHairColor(self, colorName=None, colorModel=None):
        self.model.setHairBaseColor()

    def getTrySafe(self, list, idx):
        try:
            if type(idx) == str:
                lookup = idx.split('_cut')[0]
            else:
                lookup = idx
            return list[lookup]
        except:
            return

        return

    def generateEyesTexture(self):
        eyesTextureIdx = self.style.head.eyes.color
        if self.gender == 'f':
            eye_iris_textures = PirateFemale.eye_iris_textures
        else:
            eye_iris_textures = PirateMale.eye_iris_textures
        tex_name = self.getTrySafe(eye_iris_textures, eyesTextureIdx)
        if tex_name != None:
            tex = self.eyeIrisTextures.findTexture(tex_name)
        else:
            return
        if tex:
            self.model.irises.setTexture(tex, 1)
        return

    def generateHatColor(self):
        style = self.model.dna
        if self.zombie:
            style = self.model.dnaZomb
        hatColor = style.lookupHatColor()
        geom = self.getGeomNode()
        parts = geom.findAllMatches('**/hat_band*')
        parts.setColorScale(hatColor)

    def generateClothesColor(self):
        style = self.model.dna
        if self.zombie:
            style = self.model.dnaZomb
        clothesTopColor = style.lookupClothesTopColor()
        clothesBotColor = style.lookupClothesBotColor()
        geom = self.getGeomNode()
        if self.optimizeLOD:

            def tempColorParts(parts, ct):
                numPaths = parts.getNumPaths()
                lowIdx = numPaths / 3 * 2
                for j in xrange(lowIdx):
                    parts[j].setColorScale(ct)

                for j in xrange(lowIdx, numPaths):
                    cl = parts[j].getColorScale()
                    compoundColor = VBase4(cl[0] * ct[0], cl[1] * ct[1], cl[2] * ct[2], 1.0)
                    parts[j].setColorScale(compoundColor)

        else:

            def tempColorParts(parts, ct):
                parts.setColorScale(ct)

        colorParts = tempColorParts
        parts = geom.findAllMatches('**/clothing_layer1_shirt*')
        colorParts(parts, clothesTopColor[0])
        parts = geom.findAllMatches('**/clothing_layer2_vest*')
        colorParts(parts, clothesTopColor[1])
        parts = geom.findAllMatches('**/clothing_layer3_coat*')
        colorParts(parts, clothesTopColor[2])
        parts = geom.findAllMatches('**/clothing_layer1_pant*')
        colorParts(parts, clothesBotColor[0])
        del colorParts

    def generateTexture(self):
        self.generateFaceTexture(not self.useFaceTex)
        self.generateEyesTexture()

    def generateColor(self):
        self.generateSkinColor()
        self.generateHairColor()
        self.generateHatColor()

    def makeAnimDict(self, gender, animNames):
        self.animTable = []
        for currAnim in animNames:
            anim = animNames.get(currAnim)
            for currAnimName in anim:
                self.animTable.append([currAnimName, currAnimName])

        self.reducedAnimList = self.animTable

    def forceLoadAnimDict(self):
        for anim in self.animTable:
            self.getAnimControls(anim[0])

    def createAnimDict(self, customList=None):
        if self.reducedAnimList is None:
            self.animDict = self.prebuiltAnimData[self.gender + self.type]
            return
        if self.gender == 'f':
            filePrefix = 'models/char/f'
            genderPrefix = 'f'
        else:
            filePrefix = 'models/char/m'
            genderPrefix = 'm'
        filePrefix += 'p'
        animList = self.reducedAnimList
        if animList is None:
            animList = AnimListDict[self.type]
        self.animDict = {}
        for anim in animList:
            animSuffix = ''
            for i in range(0, len(CustomAnimDict[genderPrefix + self.type])):
                if anim[0] == CustomAnimDict[genderPrefix + self.type][i]:
                    animSuffix = '_' + genderPrefix + NewModelDict.get(self.type)
                    break

            self.animDict[anim[0]] = filePrefix + '_' + anim[1] + animSuffix

        if self.reducedAnimList is None:
            self.animDict.pop('intro')
        return filePrefix

    def generateBody(self, copy=1):
        if self.gender == 'm':
            filePrefix = 'models/char/mp'
        else:
            filePrefix = 'models/char/fp'
        messenger.send('tick')
        lodString = '2000'
        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '2000', copy)
        messenger.send('tick')
        if loader.loadModel(filePrefix + '_' + '1000', allowInstance=True) != None:
            lodString = '1000'
        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '1000', copy)
        messenger.send('tick')
        if loader.loadModel(filePrefix + '_' + '500', allowInstance=True) != None:
            lodString = '500'
        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '500', copy)
        messenger.send('tick')
        self.makeSubpart('head', ['zz_head01'], [])
        self.makeSubpart('torso', ['zz_spine01'], ['zz_head01'])
        self.makeSubpart('legs', ['dx_root'], ['zz_spine01'])
        self.setSubpartsComplete(True)
        self.eyeIrisTextures = loader.loadModel('models/misc/eye_iris.bam')
        return

    def setLODs(self):
        self.setLODNode()
        avatarDetail = base.config.GetString('avatar-detail', 'high')
        if avatarDetail == 'high':
            dist = [
             0, 20, 80, 1000000000]
        else:
            if avatarDetail == 'med':
                dist = [
                 0, 10, 20, 1000000000]
            else:
                if avatarDetail == 'low':
                    dist = [
                     0, 0, 10, 1000000000]
                else:
                    raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
        self.addLOD(2000, dist[1], dist[0])
        self.addLOD(1000, dist[2], dist[1])
        self.addLOD(500, dist[3], dist[2])
        if self.optimizeLOD:
            lowLOD = self.getLOD('500')
            lowLOD.setTransparency(0, 1000)
        self.getLODNode().setCenter(Point3(0, 0, 5))

    def showLOD(self, lodName):
        self.generateTexture()
        self.model.setFromDNA()
        tex = self.model.faces[0][2].findTexture('*face*')

    def loadHuman(self, gender='m', other=None):
        self.gender = gender
        if self.gender == 'f':
            controlShapes = PirateFemale.ControlShapes
            sliderNames = PirateFemale.SliderNames
        else:
            controlShapes = PirateMale.ControlShapes
            sliderNames = PirateMale.SliderNames
        self.setLODs()
        self.loadAnimatedHead = True
        self.generateBody()
        if self.gender == 'f':
            self.type = BodyDefs.femaleFrames[self.style.getBodyShape()]
            self.model = PirateFemale.PirateFemale(self, self.style)
        else:
            self.type = BodyDefs.maleFrames[self.style.getBodyShape()]
            self.model = PirateMale.PirateMale(self, self.style)
        self.faceAwayFromViewer()
        self.lods = self.getLODNames()
        if self.gender == 'f':
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 1
        else:
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 0
        messenger.send('tick')
        self.model.setupHead(2000)
        messenger.send('tick')
        self.model.setupBody(2000)
        messenger.send('tick')
        self.model.setupClothing(2000)
        messenger.send('tick')
        if self.master:
            self.model.setupSelectionChoices('NPC')
        self.showNormal()
        self.createAnimDict()
        messenger.send('tick')
        self.initAnimsOnAllLODs(['head', 'legs', 'torso', 'modelRoot'])
        messenger.send('tick')
        self.controlShapes = controlShapes
        self.sliderNames = sliderNames
        self.initHeadControlShapes()
        self.storeJoints()
        self.find('**/nametag3d').detachNode()
        self.findAllMatches('**/name_tag').detach()
        self.rootNode = self.getLOD('2000').find('**/dx_root')
        self.floorOffsetZ = self.rootNode.getZ()
        messenger.send('tick')
        root = self.getLOD('500')
        gr = SceneGraphReducer()
        gr.applyAttribs(root.node(), gr.TTTransform | gr.TTTexMatrix | gr.TTOther)
        gr.makeCompatibleFormat(root.node(), 0)
        gr.premunge(root.node(), RenderState.makeEmpty())
        gr.decompose(root.node())
        stashedSet = root.findAllMatches('**/@@*')
        stashedSet.unstash()
        gr.makeCompatibleFormat(root.node(), 0)
        stashedSet.stash()
        messenger.send('tick')
        for face in self.model.faces[0]:
            node = face.node()
            for i in range(node.getNumGeoms()):
                face.node().setGeomState(i, RenderState.makeEmpty())

    def initializeMiscNodes(self):
        self.initializeNametag3d()
        self.initializeDropShadow()
        if self.getLOD('2000') == None:
            return
        exposedHeadJoint = self.getLOD('2000').find('**/def_head01')
        if not exposedHeadJoint.isEmpty():
            idx = 0
            if self.gender == 'f':
                idx = 1
            exposedHeadJoint.setScale(1)
            self.headNode.reparentTo(exposedHeadJoint)
            self.headNode.setScale(HeadScales[idx][self.style.getBodyShape()])
        return

    def undoControlJoints(self):
        self.getGeomNode().getParent().findAllMatches('def_*').detach()
        self.getGeomNode().getParent().findAllMatches('trs_*').detach()
        self.findAllMatches('def_*').detach()
        self.findAllMatches('trs_*').detach()

    def cleanupHuman(self, gender='m'):
        self.eyeFSM.request('off')
        self.undoControlJoints()
        self.eyeLids = {}
        self.eyeBalls = {}
        self.eyeIris = {}
        self.flush()
        self.loaded = 0
        self.master = 0

    def generateHuman(self, gender='m'):
        self.loadHuman(self.style.gender)

    def getShadowJoint(self):
        return self

    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('legs', lodName)
            joint = bundle.findChild('name_tag')
            if joint:
                joints.append(joint)

        return joints

    def __blinkOpenEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() == 'closed':
            self.eyeFSM.request('open')
        r = self.randGen.random()
        if r < 0.1:
            t = 0.2
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self.__blinkCloseEyes, self.__blinkName)
        return Task.done

    def __blinkCloseEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() != 'open':
            taskMgr.doMethodLater(4.0, self.__blinkCloseEyes, self.__blinkName)
        else:
            self.eyeFSM.request('closed')
            taskMgr.doMethodLater(0.125, self.__blinkOpenEyes, self.__blinkName)
        return Task.done

    def startBlink(self):
        taskMgr.remove(self.__blinkName)
        if self.eyeLids:
            self.openEyes()
        taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1, self.__blinkCloseEyes, self.__blinkName)

    def stopBlink(self):
        taskMgr.remove(self.__blinkName)
        if self.eyeLids:
            self.eyeFSM.request('open')

    def closeEyes(self):
        self.eyeFSM.request('closed')

    def openEyes(self):
        self.eyeFSM.request('open')

    def enterEyeFSMOff(self):
        pass

    def exitEyeFSMOff(self):
        pass

    def enterEyeFSMOpen(self):
        for lodName in self.getLODNames():
            if not self.eyeLids[lodName].isEmpty():
                self.eyeLids[lodName].hide()
                self.eyeBalls[lodName].show()
                self.eyeIris[lodName].show()

    def exitEyeFSMOpen(self):
        pass

    def enterEyeFSMClosed(self):
        return
        for lodName in self.getLODNames():
            if not self.eyeLids[lodName].isEmpty():
                self.eyeLids[lodName].show()
                self.eyeBalls[lodName].hide()
                self.eyeIris[lodName].hide()

    def exitEyeFSMClosed(self):
        pass

    def setControlValue(self, r, name):
        if self.style.getGender() == 'f':
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
        else:
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
        shapes = self.controlShapes
        ctl = shapes[name]
        slider = ctl[0]
        if r < 0.0:
            if len(ctl) > 1:
                slider = ctl[1]
        for i in range(0, len(slider)):
            jointName = slider[i][0]
            jointCtls = self.findAllMatches(jointName)
            posI = matrixI[jointName][0]
            hprI = matrixI[jointName][1]
            sclI = matrixI[jointName][2]
            posF = VBase3(posI[0], posI[1], posI[2])
            hprF = VBase3(hprI[0], hprI[1], hprI[2])
            sclF = VBase3(sclI[0], sclI[1], sclI[2])
            self.notify.debug('scv: %s initial %s' % (jointName, posI))
            dr = slider[i][4] * r
            ctl[0][i][5] = dr
            posDelta = VBase3(0, 0, 0)
            hprDelta = VBase3(0, 0, 0)
            sclDelta = VBase3(0, 0, 0)
            for sliderIdx in xrange(0, len(matrixF[jointName])):
                sliderName = matrixF[jointName][sliderIdx]
                jointSet = shapes[sliderName][0]
                for jointIdx in xrange(0, len(jointSet)):
                    if jointSet[jointIdx][0] == jointName:
                        if jointSet[jointIdx][1] == TX:
                            posDelta.setX(posDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == TY:
                            posDelta.setY(posDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == TZ:
                            posDelta.setZ(posDelta.getZ() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RX:
                            hprDelta.setX(hprDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RY:
                            hprDelta.setY(hprDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RZ:
                            hprDelta.setZ(hprDelta.getZ() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SX:
                            if r < 0.0:
                                sclDelta.setX(sclDelta.getX() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setX(sclDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SY:
                            if r < 0.0:
                                sclDelta.setY(sclDelta.getY() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setY(sclDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SZ:
                            if r < 0.0:
                                sclDelta.setZ(sclDelta.getZ() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setZ(sclDelta.getZ() + jointSet[jointIdx][5])
                        else:
                            self.notify.warning('scv:wrong element = %s' % jointSet[jointIdx][1])

            self.notify.debug('scv: %s composite posDelta = %s' % (jointName, posDelta))
            posF.setX(posI[0] + posDelta[0])
            posF.setY(posI[1] + posDelta[1])
            posF.setZ(posI[2] + posDelta[2])
            self.notify.debug('scv: %s final posDelta%s' % (jointName, posF))
            self.notify.debug('scv: %s composite hprDelta = %s' % (jointName, hprDelta))
            hprF.setX(hprI[0] + hprDelta[0])
            hprF.setY(hprI[1] + hprDelta[1])
            hprF.setZ(hprI[2] + hprDelta[2])
            self.notify.debug('scv: %s final hprDelta%s' % (jointName, hprF))
            self.notify.debug('scv: %s composite sclDelta = %s' % (jointName, sclDelta))
            sclF.setX(sclI[0] + sclDelta[0])
            sclF.setY(sclI[1] + sclDelta[1])
            sclF.setZ(sclI[2] + sclDelta[2])
            self.notify.debug('scv: %s final sclDelta%s' % (jointName, sclF))
            for j in range(0, jointCtls.getNumPaths()):
                jointCtl = jointCtls[j]
                jointCtl.setPosHprScale(posF, hprF, sclF)

    def applyBodyShaper(self):
        if self.style.getGender() == 'f':
            tjs = FemaleBodyShapeTranslateJoints
            sjs = FemaleBodyShapeScaleJoints
            matrix = FemaleBodyShapeControlJointMatrix
        else:
            tjs = MaleBodyShapeTranslateJoints
            sjs = MaleBodyShapeScaleJoints
            matrix = MaleBodyShapeControlJointMatrix
        type = self.style.getBodyShape()
        for jointName in tjs:
            transData = self.jointTrans[jointName]
            vector = matrix[jointName][type]
            self.joints[jointName].applyFreezeMatrix(vector, transData[1], transData[2])

        for jointName in sjs:
            transData = self.jointTrans[jointName]
            vector = matrix[jointName][type]
            self.joints[jointName].applyFreezeMatrix(transData[0], transData[1], vector)

        value = self.style.getHeadSize()
        mappedValue = 0.9 + (1 + value) * 0.1
        transData = self.jointTrans['def_extra_jt']
        self.joints['def_extra_jt'].applyFreezeMatrix(transData[0], transData[1], Vec3(2 - mappedValue, mappedValue, 1))
        transData = self.jointTrans['def_head01']
        idx = 0
        if self.style.gender == 'f':
            idx = 1
        self.joints['def_head01'].applyFreezeMatrix(transData[0], transData[1], Vec3(HeadScales[idx][self.style.getBodyShape()]))
        self.setGlobalScale(self.calcBodyScale())

    def undoBodyShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleBodyShapeControlJoints
        else:
            cjs = MaleBodyShapeControlJoints

    def applyHeadShaper(self):
        self.setHeadControlShapeValues()

    def undoHeadShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
        else:
            cjs = MaleHeadShapeControlJoints

    def createControlJoints(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
        else:
            cjs = MaleHeadShapeControlJoints
        for jointName in cjs:
            for lodName in self.getLODNames():
                if lodName == '2000':
                    joint = self.controlJoint(None, 'legs', jointName, lodName)
                elif lodName == '1000':
                    continue
                elif lodName == '500':
                    continue

        return

    def initHeadControlShapes(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
            matrixIHelper = FemaleHeadInitHelper
        else:
            cjs = MaleHeadShapeControlJoints
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
            matrixIHelper = MaleHeadInitHelper
        if len(matrixF['initialized']) > 0:
            return
        initializedMatrixI = len(matrixI['initialized'])
        initializedMatrixF = len(matrixF['initialized'])
        for jointName in cjs:
            transform = TransformState.makeMat(self.getJointTransform('legs', jointName, '2000'))
            pos = Vec3(transform.getPos())
            hpr = Vec3(transform.getHpr())
            scale = Vec3(transform.getScale())
            matrixI[jointName].append(pos)
            matrixI[jointName].append(hpr)
            matrixI[jointName].append(scale)
            matrixIHelper[jointName] = [
             pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2], scale[0], scale[1], scale[2]]

        matrixI['initialized'].append('initialized')
        shapes = self.controlShapes
        names = self.sliderNames
        for i in xrange(0, len(shapes)):
            slider = shapes[names[i]]
            for k in xrange(0, len(slider[0])):
                slider[0][k][4] = slider[0][k][2]
                if len(slider) > 1:
                    slider[1][k][4] = slider[1][k][2]

        for i in xrange(0, len(shapes)):
            slider = shapes[names[i]]
            for k in xrange(0, len(slider[0])):
                jointCtl = slider[0][k]
                jointName = jointCtl[0]
                matrixF[jointName].append(names[i])
                pos = matrixI[jointName][0]
                hpr = matrixI[jointName][1]
                scl = matrixI[jointName][2]
                if jointCtl[1] < 3:
                    posDelta = jointCtl[4] - pos[jointCtl[1]]
                    jointCtl[4] = posDelta
                    if len(slider) > 1:
                        jointCtl = slider[1][k]
                        jointCtl[4] = posDelta
                elif jointCtl[1] > 2:
                    if jointCtl[1] < 6:
                        hprDelta = jointCtl[4] - hpr[jointCtl[1] - 3]
                        jointCtl[4] = hprDelta
                        jointCtl = len(slider) > 1 and slider[1][k]
                        jointCtl[4] = hprDelta
                else:
                    sclDelta = jointCtl[4] - scl[jointCtl[1] - 6]
                    jointCtl[4] = sclDelta
                    if len(slider) > 1:
                        jointCtl = slider[1][k]
                        jointCtl[4] = sclDelta

        matrixF['initialized'].append('initialized')

    def setHeadControlShapeValues_old(self):
        value = self.style.getHeadSize()
        mappedValue = 0.9 + (1 + value) * 0.1
        self.setControlValue(self.style.getHeadWidth(), 'headWidth')
        self.setControlValue(self.style.getHeadHeight(), 'headHeight')
        self.setControlValue(self.style.getHeadRoundness(), 'headRoundness')
        self.setControlValue(self.style.getJawWidth(), 'jawWidth')
        self.setControlValue(self.style.getJawAngle(), 'jawChinAngle')
        self.setControlValue(self.style.getJawChinSize(), 'jawChinSize')
        self.setControlValue(self.style.getJawLength(), 'jawLength')
        self.setControlValue(self.style.getMouthWidth(), 'mouthWidth')
        self.setControlValue(self.style.getMouthLipThickness(), 'mouthLipThickness')
        self.setControlValue(self.style.getCheekFat(), 'cheekFat')
        self.setControlValue(self.style.getBrowProtruding(), 'browProtruding')
        self.setControlValue(self.style.getEyeCorner(), 'eyeCorner')
        self.setControlValue(self.style.getEyeOpeningSize(), 'eyeOpeningSize')
        self.setControlValue(self.style.getEyeBulge(), 'eyeSpacing')
        self.setControlValue(self.style.getNoseBridgeWidth(), 'noseBridgeWidth')
        self.setControlValue(self.style.getNoseNostrilWidth(), 'noseNostrilWidth')
        self.setControlValue(self.style.getNoseLength(), 'noseLength')
        self.setControlValue(self.style.getNoseBump(), 'noseBump')
        self.setControlValue(self.style.getNoseNostrilHeight(), 'noseNostrilHeight')
        self.setControlValue(self.style.getNoseNostrilAngle(), 'noseNostrilAngle')
        self.setControlValue(self.style.getNoseBridgeBroke(), 'noseBridgeBroke')
        self.setControlValue(self.style.getNoseNostrilBroke(), 'noseNostrilBroke')
        self.setControlValue(self.style.getEarScale(), 'earScale')
        self.setControlValue(self.style.getEarFlapAngle(), 'earFlap')
        self.setControlValue(self.style.getEarPosition(), 'earPosition')

    def getGlobalScale(self):
        return self.scaleNode.getScale()

    def setGlobalScale(self, scale):
        transData = self.jointTrans['def_head01']
        pos = Vec3(transData[0])
        pos.setZ(-(self.floorOffsetZ * (1 - scale)))
        self.joints['def_scale_jt'].applyFreezeMatrix(pos, transData[1], Vec3(scale))
        self.rootScale = scale

    def calcBodyScale(self):
        idx = 0
        if self.gender == 'f':
            idx = 1
        mappedValue = (0.8 + (1 + self.style.getBodyHeight()) * 0.2) * BodyScales[idx][self.style.getBodyShape()]
        return mappedValue

    def showZombie(self):
        self.model.irises.stash()
        self.model.faces[0].stash()
        self.model.faceZomb.unstash()
        self.generateSkinTexture()

    def showNormal(self):
        self.model.irises.unstash()
        self.model.faces[0].unstash()
        self.model.faceZomb.stash()
        self.generateSkinTexture()

    def takeAwayTexture(self, geoms, omitFace=False):
        emptyRenderState = RenderState.makeEmpty()
        eyeIrisColor = VBase4(0, 0, 0, 1)
        for i in range(0, geoms.getNumPaths()):
            element = geoms[i]
            if 'eye_iris' in element.getName():
                element.setColorScale(eyeIrisColor)
            else:
                if omitFace and 'master_face' in element.getName():
                    continue
            element.setTextureOff()
            geom = element.node()
            for j in range(0, geom.getNumGeoms()):
                geom.setGeomState(j, emptyRenderState)

    def optimizeMedLOD(self):
        medLOD = self.getLOD('1000')
        geoms = medLOD.findAllMatches('**/teeth*')
        geoms.stash()
        self.medSkinGone = False
        geoms = medLOD.find('**/body_forearm*')
        if geoms.isEmpty():
            self.medSkinGone = True
            geoms = medLOD.findAllMatches('**/body_*')
            self.takeAwayTexture(geoms, True)
        geoms = medLOD.findAllMatches('**/hair_*')
        self.takeAwayTexture(geoms)
        if self.gender != 'f':
            geoms = medLOD.findAllMatches('**/beard_*')
            self.takeAwayTexture(geoms)
            geoms = medLOD.findAllMatches('**/mustache_*')
            self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/eye_*')
        self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/clothing_layer2_belt_*')
        self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/clothing_layer1_shoe_*')
        self.takeAwayTexture(geoms)

    def optimizeLowLOD(self):
        lowLOD = self.getLOD('500')
        geoms = lowLOD.findAllMatches('**/teeth*')
        geoms.stash()
        geoms = lowLOD.findAllMatches('**/+GeomNode')
        self.takeAwayTexture(geoms)

    def setHeadControlShapeValues(self):
        value = self.style.getHeadSize()
        mappedValue = 0.9 + (1 + value) * 0.1
        self.setControlValue_new(self.style.getHeadWidth(), 'headWidth')
        self.setControlValue_new(self.style.getHeadHeight(), 'headHeight')
        self.setControlValue_new(self.style.getHeadRoundness(), 'headRoundness')
        self.setControlValue_new(self.style.getJawWidth(), 'jawWidth')
        self.setControlValue_new(self.style.getJawAngle(), 'jawChinAngle')
        self.setControlValue_new(self.style.getJawChinSize(), 'jawChinSize')
        self.setControlValue_new(self.style.getJawLength(), 'jawLength')
        self.setControlValue_new(self.style.getMouthWidth(), 'mouthWidth')
        self.setControlValue_new(self.style.getMouthLipThickness(), 'mouthLipThickness')
        self.setControlValue_new(self.style.getCheekFat(), 'cheekFat')
        self.setControlValue_new(self.style.getBrowProtruding(), 'browProtruding')
        self.setControlValue_new(self.style.getEyeCorner(), 'eyeCorner')
        self.setControlValue_new(self.style.getEyeOpeningSize(), 'eyeOpeningSize')
        self.setControlValue_new(self.style.getEyeBulge(), 'eyeSpacing')
        self.setControlValue_new(self.style.getNoseBridgeWidth(), 'noseBridgeWidth')
        self.setControlValue_new(self.style.getNoseNostrilWidth(), 'noseNostrilWidth')
        self.setControlValue_new(self.style.getNoseLength(), 'noseLength')
        self.setControlValue_new(self.style.getNoseBump(), 'noseBump')
        self.setControlValue_new(self.style.getNoseNostrilHeight(), 'noseNostrilHeight')
        self.setControlValue_new(self.style.getNoseNostrilAngle(), 'noseNostrilAngle')
        self.setControlValue_new(self.style.getNoseBridgeBroke(), 'noseBridgeBroke')
        self.setControlValue_new(self.style.getNoseNostrilBroke(), 'noseNostrilBroke')
        self.setControlValue_new(self.style.getEarScale(), 'earScale')
        self.setControlValue_new(self.style.getEarFlapAngle(), 'earFlap')
        self.setControlValue_new(self.style.getEarPosition(), 'earPosition')
        self.postProcess_setHeadControlShapeValues()

    def setControlValue_new(self, r, name):
        ctl = self.controlShapes[name]
        zeroindex = ctl[0]
        sliders = zeroindex
        if r < 0.0:
            if len(ctl) > 1:
                sliders = ctl[1]
        for i in range(0, len(sliders)):
            zeroindex[i][5] = sliders[i][4] * r

    def postProcess_setHeadControlShapeValues(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixIHelper = FemaleHeadInitHelper
        else:
            cjs = MaleHeadShapeControlJoints
            matrixF = MaleHeadShapeControlJointMatrix
            matrixIHelper = MaleHeadInitHelper
        for jointName in cjs:
            transList = list(matrixIHelper[jointName])
            for sliderName in matrixF[jointName]:
                for sliderJoint in self.controlShapes[sliderName][0]:
                    if sliderJoint[0] == jointName:
                        transList[sliderJoint[1]] += sliderJoint[5]

            self.joints[jointName].applyFreezeMatrix(Vec3(*transList[0:3]), Vec3(*transList[3:6]), Vec3(*transList[6:9]))

    def quickGetJointTransform(self, jointName):
        return self.joints[jointName][0].getDefaultValue()

    def storeJoints(self):
        if self.style.gender == 'm':
            bJoints = MaleBodyShapeControlJoints
            hJoints = MaleHeadShapeControlJoints
        else:
            bJoints = FemaleBodyShapeControlJoints
            hJoints = FemaleHeadShapeControlJoints
        for name in bJoints + hJoints + ('def_head01', 'def_extra_jt', 'def_scale_jt'):
            joint = self.getJoints(jointName=name)
            self.joints[name] = joint[0]
            ts = TransformState.makeMat(joint[0].getDefaultValue())
            self.jointTrans[name] = (Vec3(ts.getPos()), Vec3(ts.getHpr()), Vec3(ts.getScale()))

    @classmethod
    def setupAnimDicts(cls):
        for t in BodyDefs.maleFrames:
            cls.storeAnimDict('models/char/mp', 'm', t)

        for t in BodyDefs.femaleFrames:
            cls.storeAnimDict('models/char/fp', 'f', t)

    @classmethod
    def storeAnimDict(cls, prefix, gender, type):
        qualifier = gender + type
        animList = AnimListDict[type]
        cls.prebuiltAnimData[qualifier] = {}
        for anim in animList:
            animSuffix = ''
            for i in range(0, len(CustomAnimDict[qualifier])):
                if anim[0] == CustomAnimDict[qualifier][i]:
                    animSuffix = '_' + gender + NewModelDict.get(type)
                    break

            cls.prebuiltAnimData[qualifier][anim[0]] = prefix + '_' + anim[1] + animSuffix

        cls.prebuiltAnimData[qualifier].pop('intro')
예제 #4
0
class DistributedWeatherCycle(DistributedObject):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedWeatherCycle')

    def __init__(self, cr):
        DistributedObject.__init__(self, cr)
        self.fsm = ClassicFSM(self.__class__.__name__, [
            State('off', self.enterOff, self.exitOff, ['morning']),
            State('morning', self.enterMorning, self.exitMorning,
                  ['afternoon']),
            State('afternoon', self.enterAfternoon, self.exitAfternoon,
                  ['evening']),
            State('evening', self.enterEvening, self.exitEvening,
                  ['midnight']),
            State('midnight', self.enterMidnight, self.exitMidnight,
                  ['morning'])
        ], 'off', 'off')
        self.fsm.enterInitialState()
        self.__interval = None

    def setState(self, state):
        self.fsm.request(state)

    def getState(self):
        return self.fsm.getCurrentState()._State__name

    def setDuration(self, duration):
        self.duration = duration

    def getDuration(self):
        return self.duration

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterMorning(self):
        self.__interval = LerpColorScaleInterval(
            render,
            self.getDuration(),
            WeatherGlobals.cycleColors[0],
            startColorScale=render.getColorScale())
        self.__interval.start()

    def exitMorning(self):
        if not self.__interval:
            return
        self.__interval.finish()
        self.__interval = None

    def enterAfternoon(self):
        self.__interval = LerpColorScaleInterval(
            render,
            self.getDuration(),
            WeatherGlobals.cycleColors[1],
            startColorScale=render.getColorScale())
        self.__interval.start()

    def exitAfternoon(self):
        if not self.__interval:
            return
        self.__interval.finish()
        self.__interval = None

    def enterEvening(self):
        self.__interval = LerpColorScaleInterval(
            render,
            self.getDuration(),
            WeatherGlobals.cycleColors[2],
            startColorScale=render.getColorScale())
        self.__interval.start()

    def exitEvening(self):
        if not self.__interval:
            return
        self.__interval.finish()
        self.__interval = None

    def enterMidnight(self):
        self.__interval = LerpColorScaleInterval(
            render,
            self.getDuration(),
            WeatherGlobals.cycleColors[3],
            startColorScale=render.getColorScale())
        self.__interval.start()

    def exitMidnight(self):
        if not self.__interval:
            return
        self.__interval.finish()
        self.__interval = None
예제 #5
0
파일: Human.py 프로젝트: Kealigal/POS2013
class Human(HumanBase.HumanBase, Biped.Biped):
    notify = DirectNotifyGlobal.directNotify.newCategory('Human')
    prebuiltAnimData = {}

    def __init__(self, other=None):
        Biped.Biped.__init__(self, other, HumanAnimationMixer)
        self.zombie = False
        self.crazyColorSkin = False
        self.crazyColorSkinIndex = 0
        self.flattenPending = None
        self.flattenSuperLowName = None
        self.optimizeLOD = base.config.GetBool('optimize-avatar-lod', 1)
        self.loaded = 0
        self.playingRate = None
        self.shadowFileName = 'models/misc/drop_shadow'
        self.setFont(PiratesGlobals.getInterfaceFont())
        self._Human__blinkName = 'blink-' + str(self.this)
        self.eyeLids = None
        self.eyeBalls = None
        self.eyeIris = None
        self.reducedAnimList = None
        self.headNode = None
        self.headEffects = NodePath('headEffects')
        self.extraNode = None
        self.scaleNode = None
        self.rootNode = None
        self.floorOffsetZ = 0.0
        self.isGhost = 0
        self.headFudgeHpr = Vec3(0, 0, 0)
        self.randGen = random.Random()
        self.randGen.seed(random.random())
        self.eyeFSM = ClassicFSM('eyeFSM', [
            State('off', self.enterEyeFSMOff, self.exitEyeFSMOff,
                  ['open', 'closed']),
            State('open', self.enterEyeFSMOpen, self.exitEyeFSMOpen,
                  ['closed', 'off']),
            State('closed', self.enterEyeFSMClosed, self.exitEyeFSMClosed,
                  ['open', 'off'])
        ], 'off', 'off')
        self.eyeFSM.enterInitialState()
        if other != None:
            self.copyHuman(other)

    def removeCopiedNodes(self):
        self.dropShadow = self.find('**/drop_shadow*')
        if not self.dropShadow.isEmpty():
            self.deleteDropShadow()
        else:
            self.dropShadow = None
        billboardNode = self.find('**/billboardNode')
        if not billboardNode.isEmpty():
            billboardNode.removeNode()

        self.getGeomNode().getParent().removeNode()

    def flattenHuman(self):
        self.getWeaponJoints()

    def flattenSuperLow(self):
        name = 'flattenSuperLow-%s' % self.this
        self.flattenSuperLowName = name
        model = self.getLOD('500')
        self.accept(name, self._Human__doneFlattenSuperLow)
        taskMgr.remove(name)
        taskMgr.add(self.flattenSuperLowTask,
                    name,
                    extraArgs=[model],
                    taskChain='background')

    def flattenSuperLowTask(self, model):
        model = model.copyTo(NodePath())
        rhn = model.find('**/rightHand')
        lhn = model.find('**/leftHand')
        if lhn:
            lhn.detachNode()

        if rhn:
            rhn.detachNode()

        node = model.node()
        gr = SceneGraphReducer()
        model.node().setAttrib(TransparencyAttrib.make(0), 2000)
        gr.applyAttribs(
            node, SceneGraphReducer.TTApplyTextureColor
            | SceneGraphReducer.TTTexMatrix | SceneGraphReducer.TTOther
            | SceneGraphReducer.TTCullFace | SceneGraphReducer.TTTransform
            | SceneGraphReducer.TTColor | SceneGraphReducer.TTColorScale)
        num_removed = gr.flatten(node, -1)
        gr.makeCompatibleState(node)
        gr.collectVertexData(
            node, ~(SceneGraphReducer.CVDFormat | SceneGraphReducer.CVDName
                    | SceneGraphReducer.CVDAnimationType))
        gr.unify(node, 0)
        name = self.flattenSuperLowName
        if name:
            messenger.send(name, [model], taskChain='default')

    def _Human__doneFlattenSuperLow(self, flat):
        self.headNode = flat.find('**/def_head01')
        self.rootNode = flat.find('**/dx_root')
        self.getWeaponJoints()
        orig = self.getLOD('500')
        orig.getChildren().detach()
        self.loadModel(flat, lodName='500', copy=False, autoBindAnims=False)
        self.getWeaponJoints()
        if hasattr(self, 'animProp') and self.animProp:
            self.resetAnimProp()

        self.findAllMatches('**/def_head01').detach()
        self.findAllMatches('**/dx_root').detach()
        for lodName in self.getLODNames():
            if lodName == '500':
                self.headNode.reparentTo(
                    self.getLOD(lodName).find('**/+Character'))
                self.rootNode.reparentTo(
                    self.getLOD(lodName).find('**/+Character'))
                continue
            self.headNode.instanceTo(
                self.getLOD(lodName).find('**/+Character'))
            self.rootNode.instanceTo(
                self.getLOD(lodName).find('**/+Character'))

        self.headEffects.reparentTo(self.headNode)

    def _Human__doneFlattenHuman(self, models):
        self.flattenPending = None
        self.getWeaponJoints()

    def copyHuman(self, other):
        self.gender = other.gender
        self.loaded = other.loaded
        self.loadAnimatedHead = other.loadAnimatedHead
        self.rootScale = other.rootScale

    def delete(self):

        try:
            pass
        except:
            self.Human_deleted = 1
            taskMgr.remove(self._Human__blinkName)
            name = self.flattenSuperLowName
            if name:
                self.flattenSuperLowName = None
                self.ignore(name)
                taskMgr.remove(name)

            if self.dropShadow and not self.dropShadow.isEmpty():
                self.deleteDropShadow()

            del self.eyeFSM
            self.controlShapes = None
            self.sliderNames = None
            Biped.Biped.delete(self)

    def isDeleted(self):

        try:
            if self.Human_deleted == 1:
                return True
        except:
            return False

    def fixEyes(self):
        self.eyeLids = {}
        self.eyeBalls = {}
        self.eyeIris = {}
        for lodName in self.getLODNames():
            geom = self.getPart('head', lodName)
            self.eyeLids[lodName] = geom.findAllMatches('**/*eyelid*')
            self.eyeBalls[lodName] = geom.findAllMatches('**/eye_ball*')
            self.eyeIris[lodName] = geom.findAllMatches('**/eye_iris*')
            self.eyeLids[lodName].stash()
            self.eyeBalls[lodName].unstash()
            self.eyeIris[lodName].unstash()

    def makeAnimDict(self, gender, animNames):
        self.animTable = []
        for currAnim in animNames:
            anim = animNames.get(currAnim)
            for currAnimName in anim:
                self.animTable.append([currAnimName, currAnimName])

        self.reducedAnimList = self.animTable

    def forceLoadAnimDict(self):
        for anim in self.animDict.keys():
            self.getAnimControls(anim)

    def createAnimDict(self, customList=None):
        filePrefix = 'models/char/m'
        genderPrefix = 'm'
        if self.style.gender == 'f':
            self.type = BodyDefs.femaleFrames[self.style.getBodyShape()]
            filePrefix = 'models/char/f'
            genderPrefix = 'f'
        else:
            self.type = BodyDefs.maleFrames[self.style.getBodyShape()]
        if self.reducedAnimList is None:
            self.animDict = self.prebuiltAnimData[genderPrefix + self.type]
            return None

        filePrefix += 'p'
        animList = self.reducedAnimList
        self.animDict = {}
        for anim in animList:
            animSuffix = ''
            for i in range(0, len(CustomAnimDict[genderPrefix + self.type])):
                if anim[0] == CustomAnimDict[genderPrefix + self.type][i]:
                    animSuffix = '_' + genderPrefix + NewModelDict.get(
                        self.type)
                    break
                    continue

            self.animDict[anim[0]] = filePrefix + '_' + anim[1] + animSuffix

        return filePrefix

    def getIsPaid(self):
        return True

    def setupGhostNodes(self):
        lod = NodePath(self.getLODNode())
        for node in lod.getChildren():
            eyes = node.findAllMatches('**/eye*')
            if eyes:
                eyes.wrtReparentTo(eyes[0].getParent().attachNewNode(
                    ModelNode('eyes')))
                continue

    def loadHuman(self, other):
        other.style = self.style
        other.gender = self.style.gender
        other.model.dna = self.style
        self.createAnimDict()
        if self.style.gender == 'f':
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 1
        else:
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 0
        other.zombie = self.zombie
        other.crazyColorSkin = self.crazyColorSkin
        other.setCrazyColorSkinIndex(self.getCrazyColorSkinIndex())
        yieldThread('anim dict')
        other.isPaid = self.getIsPaid()
        other.showLOD(2000)
        yieldThread('showLOD')
        base.loadingScreen.tick()
        if other.zombie:
            other.showZombie()

        if hasattr(self, 'motionFSM'):
            self.motionFSM.setAvatar(self)

        base.loadingScreen.tick()
        yieldThread('zombie')
        other.applyBodyShaper()
        base.loadingScreen.tick()
        yieldThread('body shaper')
        base.loadingScreen.tick()
        other.applyHeadShaper()
        yieldThread('head shaper')
        base.loadingScreen.tick()
        if self.zombie == 2:
            other.model.eyeBalls.unstash()
            other.model.irises.stash()
        elif self.zombie:
            other.model.eyeBalls.stash()
            other.model.irises.stash()
        else:
            other.model.eyeBalls.unstash()
            other.model.irises.unstash()
        base.loadingScreen.tick()
        self.copyActor(other)
        base.loadingScreen.tick()
        self.floorOffsetZ = other.rootNode.getZ()
        yieldThread('copyActor')
        self.copyHuman(other)
        if self.isGhost:
            self.setupGhostNodes()

        gnodes = self.getLOD('500').findAllMatches('**/+GeomNode')
        for node in gnodes:
            node.setTextureOff(other.model.tattooStage)

        base.loadingScreen.tick()
        self.flattenSuperLow()
        self.rootNode = self.getLOD('500').find('**/dx_root')
        self.headNode = self.getLOD('500').find('**/def_head01')
        lodNames = self.getLODNames()
        self.scaleNode = self.controlJoint(None, 'legs', 'def_scale_jt',
                                           lodNames[0])
        if len(lodNames) > 1:
            for i in range(1, len(lodNames)):
                self.controlJoint(self.scaleNode, 'legs', 'def_scale_jt',
                                  lodNames[i])

        self.setGlobalScale(self.calcBodyScale())
        yieldThread('copyHuman')
        base.loadingScreen.tick()
        self.loadAnimsOnAllLODs(self.animDict, 'modelRoot')
        base.loadingScreen.tick()
        yieldThread('loadAnims')
        other.zombie = 0
        other.crazyColorSkin = 0
        other.setCrazyColorSkinIndex(0)
        other.showNormal()
        yieldThread('show normal')
        self.initializeNametag3d()
        self.initializeDropShadow()
        self.setName(self.getName())
        yieldThread('misc nodes')
        base.loadingScreen.tick()
        self.loaded = 1

    def setGlobalScale(self, scale):
        self.scaleNode.setScale(scale)
        self.rootScale = scale
        self.scaleNode.setZ(-(self.floorOffsetZ * (1 - scale)))

    def initializeMiscNodes(self):
        self.initializeNametag3d()
        self.initializeDropShadow()

    def undoControlJoints(self):
        self.getGeomNode().getParent().findAllMatches('def_*').detach()
        self.getGeomNode().getParent().findAllMatches('trs_*').detach()
        self.findAllMatches('def_*').detach()
        self.findAllMatches('trs_*').detach()

    def cleanupHuman(self, gender='m'):
        self.eyeFSM.request('off')
        self.undoControlJoints()
        self.removeCopiedNodes()
        self.eyeLids = {}
        self.eyeBalls = {}
        self.eyeIris = {}
        self.flush()
        self.loaded = 0
        self.master = 0

    def getCrazyColorSkinIndex(self):
        return self.crazyColorSkinIndex

    def setCrazyColorSkinIndex(self, index):
        if len(HumanDNA.crazySkinColors) > index:
            self.crazyColorSkinIndex = index
        else:
            self.notify.warning(
                '(Human)index: %d is out of bounds for crazyColorSkin: %d' %
                (index, len(HumanDNA.crazySkinColors)))

    def generateHuman(self, gender, others, useFaceTex=False):
        parent = self.getParent()
        self.detachNode()
        if gender == 'f':
            other = others[1]
        else:
            other = others[0]
        if self.loaded:
            self.cleanupHuman()

        other.useFaceTex = useFaceTex
        self.loadHuman(other)
        if self.isLocal():
            self.renderReflection = True

        self.setRenderReflection()
        self.resetEffectParent()
        self.enableMixing()
        self.reparentTo(parent)

    def getShadowJoint(self):
        return self.nametagNodePath

    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('modelRoot', lodName)
            joint = bundle.findChild('name_tag')
            if joint:
                joints.append(joint)
                continue

        return joints

    def _Human__blinkOpenEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() == 'closed':
            self.eyeFSM.request('open')

        r = self.randGen.random()
        if r < 0.10000000000000001:
            t = 0.20000000000000001
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self._Human__blinkCloseEyes,
                              self._Human__blinkName)
        return Task.done

    def _Human__blinkCloseEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() != 'open':
            taskMgr.doMethodLater(4.0, self._Human__blinkCloseEyes,
                                  self._Human__blinkName)
        else:
            self.eyeFSM.request('closed')
            taskMgr.doMethodLater(0.125, self._Human__blinkOpenEyes,
                                  self._Human__blinkName)
        return Task.done

    def startBlink(self):
        taskMgr.remove(self._Human__blinkName)
        if self.eyeLids:
            self.openEyes()

        taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1,
                              self._Human__blinkCloseEyes,
                              self._Human__blinkName)

    def stopBlink(self):
        taskMgr.remove(self._Human__blinkName)
        if self.eyeLids:
            self.eyeFSM.request('open')

    def closeEyes(self):
        self.eyeFSM.request('closed')

    def openEyes(self):
        self.eyeFSM.request('open')

    def enterEyeFSMOff(self):
        pass

    def exitEyeFSMOff(self):
        pass

    def enterEyeFSMOpen(self):
        for lodName in self.getLODNames():
            self.eyeLids[lodName].hide()
            self.eyeBalls[lodName].show()
            self.eyeIris[lodName].show()

    def exitEyeFSMOpen(self):
        pass

    def enterEyeFSMClosed(self):
        return None
        for lodName in self.getLODNames():
            self.eyeLids[lodName].show()
            self.eyeBalls[lodName].hide()
            self.eyeIris[lodName].hide()

    def exitEyeFSMClosed(self):
        pass

    def getGlobalScale(self):
        return self.rootScale

    def calcBodyScale(self):
        idx = 0
        if self.gender == 'f':
            idx = 1

        mappedValue = (0.80000000000000004 +
                       (1 + self.style.getBodyHeight()) * 0.20000000000000001
                       ) * BodyScales[idx][self.style.getBodyShape()]
        return mappedValue

    def setupAnimDicts(cls):
        for t in BodyDefs.maleFrames:
            cls.storeAnimDict('models/char/mp', 'm', t)

        for t in BodyDefs.femaleFrames:
            cls.storeAnimDict('models/char/fp', 'f', t)

    setupAnimDicts = classmethod(setupAnimDicts)

    def storeAnimDict(cls, prefix, gender, type):
        qualifier = gender + type
        animList = AnimListDict[type]
        cls.prebuiltAnimData[qualifier] = {}
        for anim in animList:
            if anim[0] == 'intro':
                continue

            animSuffix = ''
            for i in range(0, len(CustomAnimDict[qualifier])):
                if anim[0] == CustomAnimDict[qualifier][i]:
                    animSuffix = '_' + gender + NewModelDict.get(type)
                    break
                    continue

            cls.prebuiltAnimData[qualifier][
                anim[0]] = prefix + '_' + anim[1] + animSuffix

    storeAnimDict = classmethod(storeAnimDict)
class CogInvasionClientRepository(AstronClientRepository):
    notify = directNotify.newCategory("CIClientRepository")
    GameGlobalsId = DO_ID_COGINVASION
    SetZoneDoneEvent = 'CICRSetZoneDone'
    EmuSetZoneDoneEvent = 'CICREmuSetZoneDone'
    SetInterest = 'Set'
    ClearInterest = 'Clear'
    ClearInterestDoneEvent = 'CICRClearInterestDone'
    ITAG_PERM = 'perm'
    ITAG_AVATAR = 'avatar'
    ITAG_SHARD = 'shard'
    ITAG_WORLD = 'world'
    ITAG_GAME = 'game'

    def __init__(self, serverVersion):
        self.serverVersion = serverVersion
        AstronClientRepository.__init__(
            self, ['phase_3/etc/direct.dc', 'phase_3/etc/toon.dc'])
        self.loginFSM = ClassicFSM('login', [
            State('off', self.enterOff, self.exitOff),
            State('connect', self.enterConnect, self.exitConnect),
            State('disconnect', self.enterDisconnect, self.exitDisconnect),
            State('avChoose', self.enterAvChoose, self.exitAvChoose),
            State('playingGame', self.enterPlayingGame, self.exitPlayingGame),
            State('serverUnavailable', self.enterServerUnavailable,
                  self.exitServerUnavailable),
            State('makeAToon', self.enterMakeAToon, self.exitMakeAToon),
            State('submitNewToon', self.enterSubmitNewToon,
                  self.exitSubmitNewToon),
            State('noShards', self.enterNoShards, self.exitNoShards),
            State('waitForSetAvatarResponse',
                  self.enterWaitForSetAvatarResponse,
                  self.exitWaitForSetAvatarResponse),
            State('waitForShardList', self.enterWaitForShardList,
                  self.exitWaitForShardList),
            State('ejected', self.enterEjected, self.exitEjected),
            State('districtReset', self.enterDistrictReset,
                  self.exitDistrictReset),
            State('died', self.enterDied, self.exitDied),
            State('betaInform', self.enterBetaInform, self.exitBetaInform)
        ], 'off', 'off')
        self.loginFSM.enterInitialState()
        self.gameFSM = ClassicFSM('game', [
            State('off', self.enterGameOff, self.exitGameOff),
            State('waitForGameEnterResponse',
                  self.enterWaitForGameEnterResponse,
                  self.exitWaitForGameEnterResponse),
            State('playGame', self.enterPlayGame, self.exitPlayGame),
            State('closeShard', self.enterCloseShard, self.exitCloseShard),
            State('switchShards', self.enterSwitchShards,
                  self.exitSwitchShards)
        ], 'off', 'off')
        self.gameFSM.enterInitialState()
        #self.taskNameAllocator = UniqueIdAllocator(0, 1000000000)
        self.avChooser = AvChooser(self.loginFSM)
        self.playGame = PlayGame(self.gameFSM, "playGameDone")
        self.hoodMgr = HoodMgr()
        self.makeAToon = MakeAToon()
        self.loginToken = os.environ.get("LOGIN_TOKEN")
        self.serverAddress = os.environ.get("GAME_SERVER")
        self.serverURL = URLSpec("http://%s" % self.serverAddress)
        self.parentMgr.registerParent(CIGlobals.SPRender, render)
        self.parentMgr.registerParent(CIGlobals.SPHidden, hidden)
        self.adminAccess = False
        self.localAvChoice = None
        self.SuitsActive = 0
        self.BossActive = 0
        self.accServerTimesNA = 0
        self.maxAccServerTimesNA = 10
        self.setZonesEmulated = 0
        self.old_setzone_interest_handle = None
        self.setZoneQueue = Queue()
        self.accept(self.SetZoneDoneEvent, self._handleEmuSetZoneDone)
        self.handler = None
        self.__currentAvId = 0
        self.myDistrict = None
        self.activeDistricts = {}
        self.shardListHandle = None
        self.uberZoneInterest = None
        self.isShowingPlayerIds = False
        self.doBetaInform = False
        self.dTutorial = None
        self.requestedName = None
        self.whisperNoise = base.loadSfx(
            'phase_3.5/audio/sfx/GUI_whisper_3.ogg')
        self.checkHttp()
        #self.http.addPreapprovedServerCertificateFilename(self.serverURL, Filename('phase_3/etc/gameserver.crt'))
        #self.tournamentMusicChunks = {}
        #self.threadedTaskChain = taskMgr.setupTaskChain("threadedTaskChainForSoundIntervals", numThreads = 2)

        self.attackMgr = base.cl_attackMgr

        base.minigame = None

        self.newToonSlot = None

        base.finalExitCallbacks.insert(0, self.__handleExit)

        self.accountName = os.environ.get('ACCOUNT_NAME', '')
        self.csm = self.generateGlobalObject(DO_ID_CLIENT_SERVICES_MANAGER,
                                             'ClientServicesManager')
        self.friendsManager = self.generateGlobalObject(
            DO_ID_FRIENDS_MANAGER, 'FriendsManager')
        self.uin = self.generateGlobalObject(DO_ID_UNIQUE_INTEREST_NOTIFIER,
                                             'UniqueInterestNotifier')
        self.statsManager = self.generateGlobalObject(DO_ID_STATS_MANAGER,
                                                      'StatsManager')

        self.pingToggle = False
        self.currentPing = None

        self.pingText = OnscreenText("",
                                     align=TextNode.ALeft,
                                     parent=base.a2dBottomLeft,
                                     fg=(1, 1, 1, 1),
                                     shadow=(0, 0, 0, 0.5),
                                     pos=(0.3, 0.09))
        self.pingText.setBin('gsg-popup', 1000)
        self.pingText.hide()

        SpeedHackChecker.startChecking()
        self.loginFSM.request('connect')
        return

    def readerPollUntilEmpty(self, task):
        while self.readerPollOnce():
            pass

        if not metadata.IS_PRODUCTION:
            if ConfigVariableBool('simulated-latency', False).getValue():
                latency = random.uniform(
                    ConfigVariableDouble('simulated-latency-min',
                                         0.125).getValue(),
                    ConfigVariableDouble('simulated-latency-max',
                                         0.15).getValue())
                task.delayTime = latency
                return task.again

        return task.cont

    def togglePing(self):
        self.pingToggle = not self.pingToggle

        if self.pingToggle:
            taskMgr.add(self.__districtPingTask, "CICR.districtPingTask")
            self.showPing()
        else:
            self.hidePing()
            taskMgr.remove("CICR.districtPingTask")

    def showPing(self):
        self.pingText.show()

    def hidePing(self):
        self.pingText.hide()

    def handleNewPing(self):
        if self.currentPing is None:
            display = "?"
        else:
            display = int(round(self.currentPing))

        self.pingText.setText("Ping: {0} ms".format(display))

    def __districtPingTask(self, task):
        if self.myDistrict:
            # Figure out how much network latency there is.
            self.myDistrict.d_ping()

        task.delayTime = 1.0
        return task.again

    def deleteObject(self, doId):
        """
        implementation copied from AstronClientRepository.py

        Brian: modified to also delete owner views

        Removes the object from the client's view of the world.  This
        should normally not be called directly except in the case of
        error recovery, since the server will normally be responsible
        for deleting and disabling objects as they go out of scope.

        After this is called, future updates by server on this object
        will be ignored (with a warning message).  The object will
        become valid again the next time the server sends a generate
        message for this doId.

        This is not a distributed message and does not delete the
        object on the server or on any other client.
        """

        if doId in self.doId2do:
            # If it is in the dictionary, remove it.
            obj = self.doId2do[doId]
            # Remove it from the dictionary
            del self.doId2do[doId]
            # Disable, announce, and delete the object itself...
            # unless delayDelete is on...
            obj.deleteOrDelay()
            if self.isLocalId(doId):
                self.freeDoId(doId)
        elif doId in self.doId2ownerView:
            # If it is in the owner dictionary, remove it.
            obj = self.doId2ownerView[doId]
            # Remove it from the dictionary
            del self.doId2ownerView[doId]
            # Disable, announce, and delete the object itself...
            # unless delayDelete is on...
            obj.deleteOrDelay()
            if self.isLocalId(doId):
                self.freeDoId(doId)
        elif self.cache.contains(doId):
            # If it is in the cache, remove it.
            self.cache.delete(doId)
            if self.isLocalId(doId):
                self.freeDoId(doId)
        elif self.cacheOwner.contains(doId):
            # If it is in the owner cache, remove it.
            self.cacheOwner.delete(doId)
            if self.isLocalId(doId):
                self.freeDoId(doId)
        else:
            # Otherwise, ignore it
            self.notify.warning("Asked to delete non-existent DistObj " +
                                str(doId))

    #def uniqueName(self, idString):
    #	return "%s-%s" % (idString, self.taskNameAllocator.allocate())

    #def removeTask(self, taskName):
    #	div = taskName.split('-')
    #	self.taskNameAllocator.free(div[1])
    #	taskMgr.remove(taskName)

    def __handleExit(self):
        try:
            base.localAvatar.b_setAnimState('teleportOut')
        except:
            pass

        ccoginvasion.CTMusicData.stop_am_update_task()

        self.gameFSM.request('closeShard', ['off'])

    def isChristmas(self):
        return self.holidayManager.getHoliday() == HolidayType.CHRISTMAS

    def showPlayerIds(self):
        print "Showing player ids..."
        self.isShowingPlayerIds = True
        for av in self.doId2do.values():
            if av.__class__.__name__ in [
                    "DistributedPlayerToon", "LocalToon", "DistributedSuit"
            ]:
                av.showAvId()

    def hidePlayerIds(self):
        print "Hiding player ids..."
        self.isShowingPlayerIds = False
        for av in self.doId2do.values():
            if av.__class__.__name__ in [
                    "DistributedPlayerToon", "LocalToon", 'DistributedSuit'
            ]:
                av.showName()

    def sendSetLocation(self, doId, parentId, zoneId):
        dg = PyDatagram()
        dg.addUint16(CLIENT_OBJECT_LOCATION)
        dg.addUint32(doId)
        dg.addUint32(parentId)
        dg.addUint32(zoneId)
        self.send(dg)

    def getNextSetZoneDoneEvent(self):
        return '%s-%s' % (self.EmuSetZoneDoneEvent, self.setZonesEmulated + 1)

    def getLastSetZoneDoneEvent(self):
        return '%s-%s' % (self.EmuSetZoneDoneEvent, self.setZonesEmulated)

    def getQuietZoneLeftEvent(self):
        return 'leftQuietZone-%s' % (id(self), )

    def b_setLocation(self, do, parentId, zoneId):
        self.sendSetLocation(do.doId, parentId, zoneId)
        do.setLocation(parentId, zoneId)

    def sendSetZoneMsg(self, zoneId, visibleZoneList=None):
        event = self.getNextSetZoneDoneEvent()
        self.setZonesEmulated += 1
        parentId = base.localAvatar.defaultShard
        self.sendSetLocation(base.localAvatar.doId, parentId, zoneId)
        localAvatar.setLocation(parentId, zoneId)
        interestZones = zoneId
        if visibleZoneList is not None:
            interestZones = visibleZoneList
        self._addInterestOpToQueue(
            self.SetInterest, [parentId, interestZones, 'OldSetZoneEmulator'],
            event)
        return

    def resetInterestStateForConnectionLoss(self):
        self.old_setzone_interest_handle = None
        self.setZoneQueue.clear()
        return

    def _removeEmulatedSetZone(self, doneEvent):
        self._addInterestOpToQueue(self.ClearInterest, None, doneEvent)
        return

    def _addInterestOpToQueue(self, op, args, event):
        self.setZoneQueue.push([op, args, event])
        if len(self.setZoneQueue) == 1:
            self._sendNextSetZone()

    def _sendNextSetZone(self):
        op, args, event = self.setZoneQueue.top()
        if op == self.SetInterest:
            parentId, interestZones, name = args
            if self.old_setzone_interest_handle is None:
                self.old_setzone_interest_handle = self.addInterest(
                    parentId, interestZones, name, self.SetZoneDoneEvent)
            else:
                self.alterInterest(self.old_setzone_interest_handle, parentId,
                                   interestZones, name, self.SetZoneDoneEvent)
        elif op == self.ClearInterest:
            self.removeInterest(self.old_setzone_interest_handle,
                                self.SetZoneDoneEvent)
            self.old_setzone_interest_handle = None
        else:
            self.notify.error('unknown setZone op: %s' % op)
        return

    def _handleEmuSetZoneDone(self):
        op, args, event = self.setZoneQueue.pop()
        queueIsEmpty = self.setZoneQueue.isEmpty()
        if event is not None:
            messenger.send(event)
        if not queueIsEmpty:
            self._sendNextSetZone()
        return

    def enterSwitchShards(self, shardId, hoodId, zoneId, avId):
        self._switchShardParams = [shardId, hoodId, zoneId, avId]
        self.removeShardInterest(self._handleOldShardGone)

    def _handleOldShardGone(self):
        status = {}
        status['hoodId'] = self._switchShardParams[1]
        status['zoneId'] = self._switchShardParams[2]
        status['avId'] = self._switchShardParams[3]
        self.gameFSM.request('waitForGameEnterResponse',
                             [status, self._switchShardParams[0]])

    def exitSwitchShards(self):
        del self._switchShardParams

    def enterBetaInform(self):
        msg = (
            "Welcome to Cog Invasion Online!\n\nBefore playing, please remember that the game is in Alpha, "
            "and that you may encounter bugs and incomplete features.\n\nIf you happen to encounter any bugs, "
            "please report them to us by using the Contact Us Page at coginvasion.com.\n\nHave fun!"
        )
        self.dialog = GlobalDialog(message=msg,
                                   style=3,
                                   doneEvent="gameEnterChoice")
        self.dialog.show()
        self.acceptOnce("gameEnterChoice", self.handleGameEnterChoice)

    def handleGameEnterChoice(self):
        self.loginFSM.request('avChoose')

    def exitBetaInform(self):
        self.ignore("gameEnterChoice")
        self.dialog.cleanup()
        del self.dialog

    def enterCloseShard(self, nextState='avChoose'):
        self.setNoNewInterests(True)
        self._removeLocalAvFromStateServer(nextState)

    def exitCloseShard(self):
        self.setNoNewInterests(False)
        self.ignore(self.ClearInterestDoneEvent)
        return

    def _removeLocalAvFromStateServer(self, nextState):
        self.sendSetAvatarIdMsg(0)
        self._removeAllOV()
        callback = Functor(self.loginFSM.request, nextState)
        self.removeShardInterest(callback)

    def removeShardInterest(self, callback):
        self._removeCurrentShardInterest(
            Functor(self._removeShardInterestComplete, callback))

    def _removeShardInterestComplete(self, callback):
        self.cache.flush()
        self.doDataCache.flush()

        callback()
        return

    def _removeCurrentShardInterest(self, callback):
        if self.old_setzone_interest_handle is None:
            callback()
            return
        self.acceptOnce(self.ClearInterestDoneEvent,
                        Functor(self._removeCurrentUberZoneInterest, callback))
        self._removeEmulatedSetZone(self.ClearInterestDoneEvent)
        return

    def _removeCurrentUberZoneInterest(self, callback):
        self.acceptOnce(self.ClearInterestDoneEvent,
                        Functor(self._removeShardInterestDone, callback))
        self.removeInterest(self.uberZoneInterest, self.ClearInterestDoneEvent)

    def _removeShardInterestDone(self, callback):
        self.uberZoneInterest = None
        callback()
        return

    def _removeAllOV(self):
        owners = self.doId2ownerView.keys()
        for doId in owners:
            self.disableDoId(doId, ownerView=True)

    def enterDied(self):
        self.deathDialog = GlobalDialog(message=CIGlobals.SuitDefeatMsg,
                                        style=2,
                                        doneEvent="deathChoice")
        self.deathDialog.show()
        self.acceptOnce("deathChoice", self.handleDeathChoice)

    def handleDeathChoice(self):
        value = self.deathDialog.getValue()
        if value:
            self.loginFSM.request('avChoose')
        else:
            sys.exit()

    def exitDied(self):
        self.deathDialog.cleanup()
        del self.deathDialog
        self.ignore("deathChoice")

    def enterConnect(self):
        self.connectingDialog = GlobalDialog(message=CIGlobals.ConnectingMsg)
        self.connectingDialog.show()
        self.connect([self.serverURL],
                     successCallback=self.handleConnected,
                     failureCallback=self.handleConnectFail)

    def handleConnected(self):
        self.notify.info("Sending CLIENT_HELLO...")
        self.acceptOnce("CLIENT_HELLO_RESP", self.handleClientHelloResp)
        self.acceptOnce("CLIENT_EJECT", self.handleEjected)
        self.acceptOnce("LOST_CONNECTION", self.handleLostConnection)
        AstronClientRepository.sendHello(self, self.serverVersion)

    def handleLostConnection(self):
        self.deleteAllObjects()
        self.loginFSM.request('disconnect', [1])

    def deleteAllObjects(self):
        for doId in self.doId2do.keys():
            obj = self.doId2do[doId]

            if not isinstance(obj, DistributedObjectGlobal) and not hasattr(
                    obj, 'isDistrict'):
                if hasattr(base,
                           'localAvatar') and doId != base.localAvatar.doId:
                    self.deleteObject(doId)

    def handleEjected(self, errorCode, reason):
        self.notify.info("OMG I WAS EJECTED!")
        self.ignore("LOST_CONNECTION")
        errorMsg = ErrorCode2ErrorMsg.get(errorCode,
                                          None) or UnknownErrorMsg % errorCode
        self.loginFSM.request('ejected', [errorMsg])

    def handleClientHelloResp(self):
        self.notify.info("Got CLIENT_HELLO_RESP!")
        self.acceptOnce(self.csm.getLoginAcceptedEvent(),
                        self.handleLoginAccepted)
        self.csm.d_requestLogin(self.loginToken, self.accountName)

    def handleLoginAccepted(self):
        self.notify.info("Woo-hoo, I am authenticated!")
        base.cr.holidayManager = self.generateGlobalObject(
            DO_ID_HOLIDAY_MANAGER, 'HolidayManager')
        base.cr.nameServicesManager = self.generateGlobalObject(
            DO_ID_NAME_SERVICES_MANAGER, 'NameServicesManager')
        self.loginFSM.request('waitForShardList')

    def handleConnectFail(self, _, __):
        self.notify.info("Could not connect to gameserver, notifying user.")
        self.connectingDialog.cleanup()
        self.connectingDialog = GlobalDialog(
            message=CIGlobals.NoConnectionMsg % self.serverAddress + " " +
            CIGlobals.TryAgain,
            style=2,
            doneEvent="connectFail")
        self.connectingDialog.show()
        self.acceptOnce("connectFail", self.handleConnectFailButton)

    def handleConnectFailButton(self):
        value = self.connectingDialog.getValue()
        if value:
            self.loginFSM.request('connect')
        else:
            sys.exit()

    def exitConnect(self):
        self.ignore("connectFail")
        self.ignore("CLIENT_HELLO_RESP")
        self.ignore(self.csm.getLoginAcceptedEvent())
        self.connectingDialog.cleanup()
        del self.connectingDialog

    def enterEjected(self, errorMsg):
        self.ejectDialog = GlobalDialog(message=errorMsg,
                                        style=3,
                                        doneEvent='ejectDone')
        self.ejectDialog.show()
        self.acceptOnce('ejectDone', sys.exit)

    def exitEjected(self):
        self.ignore('ejectDone')
        self.ejectDialog.cleanup()
        del self.ejectDialog

    def enterServerUnavailable(self):
        self.notify.info(CIGlobals.ServerUnavailable)
        self.serverNA = GlobalDialog(message=CIGlobals.ServerUnavailable,
                                     style=4,
                                     doneEvent="serverNAEvent")
        self.serverNA.show()
        self.acceptOnce("serverNAEvent", sys.exit)
        self.startServerNAPoll()

    def startServerNAPoll(self):
        self.notify.info("Starting server poll...")
        self.accServerTimesNA = 1
        taskMgr.add(self.serverNAPoll, "serverNAPoll")

    def serverNAPoll(self, task):
        dg = PyDatagram()
        dg.addUint16(ACC_IS_SERVER_UP)
        self.send(dg)
        task.delayTime = 3.0
        return Task.again

    def __handleServerNAResp(self, resp):
        if resp == ACC_SERVER_UP:
            taskMgr.remove("serverNAPoll")
            # Enter the previous state that we were in, which should have
            # been some state where we communicate with the acc server.
            self.loginFSM.request(self.loginFSM.getLastState().getName())
        else:
            self.accServerTimesNA += 1
            if self.accServerTimesNA >= self.maxAccServerTimesNA:
                taskMgr.remove("serverNAPoll")
                self.notify.info(
                    "Giving up on polling account server after %s times." %
                    self.accServerTimesNA)
                self.loginFSM.request("disconnect", enterArgList=[1])
                self.accServerTimesNA = 0

    def exitServerUnavailable(self):
        self.ignore("serverNAEvent")
        self.serverNA.cleanup()
        del self.serverNA

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def playTheme(self):
        base.playMusic(CIGlobals.getThemeSong(), looping=1)

    def enterAvChoose(self, newToonSlot=None):
        ModelPool.garbageCollect()
        TexturePool.garbageCollect()
        self.avChooser.load()
        self.avChooser.enter(newToonSlot)
        if newToonSlot is None:
            self.playTheme()
        self.accept("enterMakeAToon", self.__handleMakeAToonReq)
        self.accept("avChooseDone", self.__handleAvChooseDone)

    def __handleMakeAToonReq(self, slot):
        self.loginFSM.request('makeAToon', [slot])

    def __handleAvChooseDone(self, avChoice):
        print "------- AvChooseDone -------"
        print "Toon name: %s" % avChoice.getName()
        print "Slot:      %s" % avChoice.getSlot()
        print "DNA:       %s" % avChoice.getDNA()
        self.loginFSM.request("waitForSetAvatarResponse", [avChoice])

    def exitAvChoose(self):
        self.avChooser.exit()
        self.avChooser.unload()
        self.ignore("enterMakeAToon")
        self.ignore("avChooseDone")

    def handlePlayGame(self, msgType, di):
        if msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER:
            self.handleGenerateWithRequiredOtherOwner(msgType, di)
        else:
            AstronClientRepository.handleDatagram(self, di)

    def enterPlayingGame(self):
        zoneId = localAvatar.getLastHood()
        hoodId = ZoneUtil.getHoodId(zoneId)
        status = {"hoodId": hoodId, "zoneId": zoneId, "avId": self.localAvId}
        shardId = self.myDistrict.doId
        self.gameFSM.request('waitForGameEnterResponse', [status, shardId])

    def exitPlayingGame(self):
        self.deleteAllObjects()
        self.handler = None
        self.gameFSM.request('off')
        camera.reparentTo(render)
        camera.setPos(0, 0, 0)
        camera.setHpr(0, 0, 0)
        self.localAvChoice = None
        if loader.inBulkBlock:
            loader.endBulkLoad(loader.blockName)

    def enterNoShards(self):
        self.noShardDialog = GlobalDialog(message=CIGlobals.NoShardsMsg + " " +
                                          CIGlobals.TryAgain,
                                          style=2,
                                          doneEvent='noShardsDone')
        self.noShardDialog.show()
        self.acceptOnce('noShardsDone', self.handleNoShardsDone)

    def handleNoShardsDone(self):
        value = self.noShardDialog.getValue()
        if value:
            self.loginFSM.request('waitForShardList')
        else:
            sys.exit()

    def exitNoShards(self):
        self.noShardDialog.cleanup()
        del self.noShardDialog
        self.ignore('noShardsDone')

    def enterWaitForShardList(self):
        self.shardListHandle = self.addTaggedInterest(
            self.GameGlobalsId,
            ZoneUtil.DistrictZone,
            self.ITAG_PERM,
            'localShardList',
            event='shardList_complete')
        self.acceptOnce('shardList_complete', self._handleShardListComplete)

    def _handleShardListComplete(self):
        if self._shardsAreAvailable():
            self.myDistrict = self._chooseAShard()
            if self.doBetaInform:
                self.loginFSM.request('betaInform')
            else:
                self.loginFSM.request('avChoose')
            taskMgr.add(self.monitorDistrict, "monitorMyDistrict")
        else:
            self.loginFSM.request('noShards')

    def monitorDistrict(self, task):
        if self.myDistrict is None and self.isConnected():
            self.loginFSM.request('districtReset')
            return task.done
        return task.cont

    def _shardsAreAvailable(self):
        for shard in self.activeDistricts.values():
            if shard.available:
                return True
        return False

    def _chooseAShard(self):
        choices = []
        for shard in self.activeDistricts.values():
            choices.append(shard)
        return random.choice(choices)

    def exitWaitForShardList(self):
        self.ignore('shardList_complete')

    def enterDistrictReset(self):
        self.districtResetDialog = GlobalDialog(
            message=CIGlobals.DistrictResetMsg,
            style=3,
            doneEvent='distresetdone')
        self.districtResetDialog.show()
        self.acceptOnce('distresetdone', sys.exit)

    def exitDistrictReset(self):
        self.districtResetDialog.cleanup()
        del self.districtResetDialog

    def enterWaitForSetAvatarResponse(self, choice):
        #self.acceptOnce(self.csm.getSetAvatarEvent(), self.__handleSetAvatarResponse)
        self.sendSetAvatarMsg(choice)

    def enterLoadDone(self):
        self.loginFSM.request("playingGame")

    def __handleSetAvatarResponse(self, avId, di):
        print "Entering game..."
        enterLoad = EnterLoad(self.enterLoadDone)
        dclass = self.dclassesByName['DistributedPlayerToon']
        localAvatar = LocalToon.LocalToon(base.cr)
        localAvatar.dclass = dclass
        base.localAvatar = localAvatar
        __builtins__['localAvatar'] = base.localAvatar
        localAvatar.doId = avId
        self.localAvId = avId
        parentId = None
        zoneId = None
        localAvatar.setLocation(parentId, zoneId)
        localAvatar.generateInit()
        localAvatar.generate()
        dclass.receiveUpdateBroadcastRequiredOwner(localAvatar, di)
        localAvatar.announceGenerate()
        localAvatar.postGenerateMessage()
        self.doId2do[avId] = localAvatar

        # TEMPORARY:
        #localAvatar.hoodsDiscovered = [1000, 2000, 3000, 4000, 5000, 9000]
        #localAvatar.teleportAccess = [1000, 2000, 3000, 4000, 5000, 9000]

        enterLoad.load()
        del enterLoad

    def exitWaitForSetAvatarResponse(self):
        self.ignore(self.csm.getSetAvatarEvent())

    def enterWaitForGameEnterResponse(self, status, shardId):
        if shardId is not None:
            district = self.activeDistricts[shardId]
        else:
            district = None
        if not district:
            self.loginFSM.request('noShards')
            return
        else:
            self.myDistrict = district
        self.notify.info("Entering shard %s" % shardId)
        localAvatar.setLocation(shardId, status['zoneId'])
        localAvatar.defaultShard = shardId
        self.handleEnteredShard(status)
        return

    def handleEnteredShard(self, status):
        self.uberZoneInterest = self.addInterest(localAvatar.defaultShard,
                                                 ZoneUtil.UberZone, 'uberZone',
                                                 'uberZoneInterestComplete')
        self.acceptOnce('uberZoneInterestComplete',
                        self.uberZoneInterestComplete, [status])

    def uberZoneInterestComplete(self, status):
        self.__gotTimeSync = 0
        if self.timeManager is None:
            print "No time manager"
            DistributedSmoothNode.globalActivateSmoothing(0, 0)
            self.gotTimeSync(status)
        else:
            print "Time manager found"
            DistributedSmoothNode.globalActivateSmoothing(1, 0)
            #h = HashVal()
            #hashPrcVariables(h)
            #pyc = HashVal()
            #if not __dev__:
            #	self.hashFiles(pyc)
            #self.timeManager.d_setSignature(self.userSignature, h.asBin(), pyc.asBin())
            #self.timeManager.sendCpuInfo()

            self.timeManager.lastAttempt = -self.timeManager.minWait * 2
            if self.timeManager.synchronize('startup'):
                self.accept('gotTimeSync', self.gotTimeSync, [status])
            else:
                self.gotTimeSync(status)
        return

    def getPing(self):
        if self.myDistrict:
            return self.myDistrict.currentPing
        return 0

    def exitWaitForGameEnterResponse(self):
        self.ignore('uberZoneInterestComplete')
        return

    def gotTimeSync(self, status):
        self.notify.info('gotTimeSync')
        self.ignore('gotTimeSync')
        self.__gotTimeSync = 1
        self.prepareToEnter(status)

    def prepareToEnter(self, status):
        if not self.__gotTimeSync:
            self.notify.info("still waiting for time sync")
            return
        self.gameFSM.request('playGame', [status])

    def enterMakeAToon(self, slot):
        base.stopMusic()
        self.makeAToon.setSlot(slot)
        self.makeAToon.loadEnviron()
        self.makeAToon.load()
        self.makeAToon.matFSM.request('genderShop')
        self.acceptOnce("quitCreateAToon", self.__handleMakeAToonQuit)
        self.acceptOnce("createAToonFinished", self.__handleMakeAToonDone)

    def __handleMakeAToonQuit(self):
        self.loginFSM.request("avChoose")

    def __handleMakeAToonDone(self, dnaStrand, slot, name):
        self.loginFSM.request('submitNewToon',
                              enterArgList=[dnaStrand, slot, name])

    def exitMakeAToon(self):
        self.makeAToon.setSlot(-1)
        self.makeAToon.enterExit(None)
        self.ignore("quitCreateAToon")
        self.ignore("createAToonFinished")

    def enterSubmitNewToon(self, dnaStrand, slot, name, skipTutorial=0):
        self.newToonSlot = slot
        self.submittingDialog = GlobalDialog(message=CIGlobals.Submitting)
        self.submittingDialog.show()
        self.acceptOnce(self.csm.getToonCreatedEvent(),
                        self.__handleSubmitNewToonResp)
        self.csm.sendSubmitNewToon(dnaStrand, slot, name, skipTutorial)

    def __handleSubmitNewToonResp(self, avId):
        # Now that our toon exists in the database, we can add send over the name we wanted to NameServicesManagerUD.
        if self.requestedName is not None:
            self.nameServicesManager.d_requestName(self.requestedName, avId)
            self.requestedName = None

        self.loginFSM.request('avChoose', [self.newToonSlot])

    def exitSubmitNewToon(self):
        self.newToonSlot = None
        self.ignore(self.csm.getToonCreatedEvent())
        self.submittingDialog.cleanup()
        del self.submittingDialog

    def enterGameOff(self):
        pass

    def exitGameOff(self):
        pass

    def enterPlayGame(self, status):
        base.stopMusic()
        base.transitions.noFade()
        if self.localAvChoice is None:
            self.notify.error(
                "called enterPlayGame() without self.localAvChoice being set!")
            return
        if localAvatar.getTutorialCompleted() == 1:
            zoneId = status['zoneId']
            hoodId = status['hoodId']
            avId = status['avId']
            self.playGame.load()
            self.playGame.enter(hoodId, zoneId, avId)
        else:
            self.sendQuietZoneRequest()
            localAvatar.sendUpdate('createTutorial')
        self.myDistrict.d_joining()

    def tutorialCreated(self, zoneId):
        # zoneId = the zone the tutorial resides in
        # tutId = the doId of the tutorial
        requestStatus = {'zoneId': zoneId}
        self.tutQuietZoneState = QuietZoneState('tutQuietZoneDone')
        self.tutQuietZoneState.load()
        self.tutQuietZoneState.enter(requestStatus)
        self.acceptOnce('tutQuietZoneDone', self.__handleTutQuietZoneDone)

    def __handleTutQuietZoneDone(self):
        # We've entered the zone that the tutorial is in.
        self.tutQuietZoneState.exit()
        self.tutQuietZoneState.unload()
        del self.tutQuietZoneState

    def exitPlayGame(self):
        self.ignore('tutQuietZoneDone')
        if hasattr(self, 'tutQuietZoneDone'):
            self.tutQuietZoneState.exit()
            self.tutQuietZoneState.unload()
            del self.tutQuietZoneState
        base.stopMusic()
        self.playGame.exit()
        self.playGame.unload()

    def enterDisconnect(self, isPlaying, booted=0, bootReason=None):
        self.notify.info(
            "Disconnect details: isPlaying = %s, booted = %s, bootReason = %s"
            % (isPlaying, booted, bootReason))
        style = 3
        if isPlaying == 1:
            if not booted:
                msg = CIGlobals.DisconnectionMsg
        else:
            if not booted:
                msg = CIGlobals.JoinFailureMsg
        if self.isConnected():
            self.sendDisconnect()
        self.disconnectDialog = GlobalDialog(message=msg,
                                             style=style,
                                             doneEvent="disconnectDone")
        self.disconnectDialog.show()
        if style == 3:
            self.acceptOnce("disconnectDone", sys.exit)
        else:
            self.acceptOnce("disconnectDone", self.handleDisconnectDone)

    def handleDisconnectDone(self):
        value = self.disconnectDialog.getValue()
        if value:
            self.loginFSM.request('connect')
        else:
            sys.exit()

    def exitDisconnect(self):
        self.ignore("disconnectDone")
        self.disconnectDialog.cleanup()
        del self.disconnectDialog

    def renderFrame(self):
        gsg = base.win.getGsg()
        if gsg:
            render2d.prepareScene(gsg)
        base.graphicsEngine.renderFrame()

    def renderFrames(self):
        base.graphicsEngine.renderFrame()
        base.graphicsEngine.renderFrame()

    def handleDatagram(self, di):
        if self.notify.getDebug():
            print "ClientRepository received datagram:"
            #di.getDatagram().dumpHex(ostream)
        msgType = self.getMsgType()
        self.currentSenderId = None
        if self.handler is None:
            self.astronHandle(di)
        else:
            self.handler(msgType, di)
        self.considerHeartbeat()

    def astronHandle(self, di):
        AstronClientRepository.handleDatagram(self, di)

    def handleQuietZoneGenerateWithRequired(self, di):
        doId = di.getUint32()
        parentId = di.getUint32()
        zoneId = di.getUint32()
        classId = di.getUint16()
        dclass = self.dclassesByNumber[classId]
        if dclass.getClassDef().neverDisable:
            dclass.startGenerate()
            distObj = self.generateWithRequiredFields(dclass, doId, di,
                                                      parentId, zoneId)
            dclass.stopGenerate()

    def handleQuietZoneGenerateWithRequiredOther(self, di):
        doId = di.getUint32()
        parentId = di.getUint32()
        zoneId = di.getUint32()
        classId = di.getUint16()
        dclass = self.dclassesByNumber[classId]
        if dclass.getClassDef().neverDisable:
            dclass.startGenerate()
            distObj = self.generateWithRequiredOtherFields(
                dclass, doId, di, parentId, zoneId)
            dclass.stopGenerate()

    def handleQuietZoneUpdateField(self, di):
        di2 = DatagramIterator(di)
        doId = di2.getUint32()
        if doId in self.deferredDoIds:
            args, deferrable, dg0, updates = self.deferredDoIds[doId]
            dclass = args[2]
            if not dclass.getClassDef().neverDisable:
                return
        else:
            do = self.getDo(doId)
            if do:
                if not do.neverDisable:
                    return
        AstronClientRepository.handleUpdateField(self, di)

    def handleDelete(self, di):
        doId = di.getUint32()
        self.deleteObject(doId)

    def _abandonShard(self):
        for doId, obj in self.doId2do.items():
            if obj.parentId == localAvatar.defaultShard and obj is not localAvatar:
                self.deleteObject(doId)

    def handleEnterObjectRequiredOwner(self, di):
        if self.loginFSM.getCurrentState().getName(
        ) == 'waitForSetAvatarResponse':
            doId = di.getUint32()
            parentId = di.getUint32()
            zoneId = di.getUint32()
            dclassId = di.getUint16()
            self.__handleSetAvatarResponse(doId, di)
        else:
            AstronClientRepository.handleEnterObjectRequiredOwner(self, di)

    def addTaggedInterest(self,
                          parentId,
                          zoneId,
                          mainTag,
                          desc,
                          otherTags=[],
                          event=None):
        return self.addInterest(parentId, zoneId, desc, event)

    def sendSetAvatarMsg(self, choice):
        avId = choice.getAvId()
        self.sendSetAvatarIdMsg(avId)
        self.localAvChoice = choice

    def sendSetAvatarIdMsg(self, avId):
        if avId != self.__currentAvId:
            self.__currentAvId = avId
            self.csm.sendSetAvatar(avId)

    def sendQuietZoneRequest(self):
        self.sendSetZoneMsg(ZoneUtil.QuietZone)
예제 #7
0
class DistributedWeatherCycleAI(DistributedObjectAI):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedWeatherCycleAI')

    def __init__(self, air):
        DistributedObjectAI.__init__(self, air)

        self.fsm = ClassicFSM(self.__class__.__name__, [
            State('off', self.enterOff, self.exitOff, ['morning']),
            State('morning', self.enterMorning, self.exitMorning,
                  ['afternoon']),
            State('afternoon', self.enterAfternoon, self.exitAfternoon,
                  ['evening']),
            State('evening', self.enterEvening, self.exitEvening,
                  ['midnight']),
            State('midnight', self.enterMidnight, self.exitMidnight,
                  ['morning'])
        ], 'off', 'off')

        self.fsm.enterInitialState()
        self.duration = 0

    def generate(self):
        DistributedObjectAI.generate(self)

    def announceGenerate(self):
        DistributedObjectAI.announceGenerate(self)

        taskMgr.doMethodLater(self.getDuration(), self.update, 'update-time')

    def delete(self):
        DistributedObjectAI.delete(self)

        taskMgr.remove('update-time')

    def update(self, task):
        self.b_setState(self.fsm.getCurrentState()._State__transitions[0])
        return task.again

    def setState(self, state):
        self.fsm.request(state)

    def d_setState(self, state):
        self.sendUpdate('setState', [state])

    def b_setState(self, state):
        self.setState(state)
        self.d_setState(state)

    def getState(self):
        return self.fsm.getCurrentState()._State__name

    def setDuration(self, duration):
        self.duration = duration

    def d_setDuration(self, duration):
        self.sendUpdate('setDuration', [duration])

    def b_setDuration(self, duration):
        self.setDuration(duration)
        self.d_setDuration(duration)

    def getDuration(self):
        return self.duration

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterMorning(self):
        pass

    def exitMorning(self):
        pass

    def enterAfternoon(self):
        pass

    def exitAfternoon(self):
        pass

    def enterEvening(self):
        pass

    def exitEvening(self):
        pass

    def enterMidnight(self):
        pass

    def exitMidnight(self):
        pass
예제 #8
0
class GameEnemy(Enemy):
    def __init__(self, arena):
        Enemy.__init__(self)
        self.arena = arena
        self.aiCharacter = None
        self.spotToken = self.uniqueName('spot')
        self.lastAnimation = None
        self.tunnelSequence = None
        self.hurtSequence = None
        self.gridPos = None

        self.fsm = ClassicFSM('GameEnemy', [
            State('Off', self.enterOff, self.exitOff),
            State('TunnelIn', self.enterTunnelIn, self.exitTunnelIn),
            State('WalkToGrid', self.enterWalkToGrid, self.exitWalkToGrid),
            State('AttackHQ', self.enterAttackHQ, self.exitAttackHQ),
            State('Idle', self.enterIdle, self.exitIdle)
        ], 'Off', 'Off')
        self.fsm.enterInitialState()

    def announceDead(self):
        messenger.send('enemyDied', [self])

    def isAlive(self):
        return self.fsm.getCurrentState() != 'Off' and self.currHP > 0

    def delete(self):
        self.removeFromGame()

        if self.fsm.getCurrentState().getName() != 'Off':
            self.fsm.request('Off')

        if self in self.arena.enemies:
            self.arena.enemies.remove(self)

        Enemy.delete(self)

    def getGridPos(self):
        if self.gridPos is None:
            self.gridPos = self.arena.enemyGrid.occupyRandomSpot(
                self.spotToken)

        return self.gridPos

    def enableAI(self):
        if self.aiCharacter:
            print('Tried to enable AI when already enabled.')
            return

        self.aiCharacter = AICharacter(self.uniqueName('enemy'), self, 10, 12,
                                       16)
        self.arena.aiWorld.addAiChar(self.aiCharacter)

    def disableAI(self):
        if not self.aiCharacter:
            print('Tried to disable AI when already disabled.')
            return

        self.arena.aiWorld.removeAiChar(self.uniqueName('enemy'))
        self.aiCharacter = None

    def pauseTunnel(self):
        if self.tunnelSequence:
            self.tunnelSequence.pause()

        self.tunnelSequence = None

    def setTunnelRunning(self, running):
        if not self.tunnelSequence:
            return

        if running:
            self.tunnelSequence.resume()
        else:
            self.tunnelSequence.pause()

    def loop(self,
             animName,
             restart=1,
             partName=None,
             fromFrame=None,
             toFrame=None):
        # Keep track of the last played animation.
        result = Enemy.loop(self, animName, restart, partName, fromFrame,
                            toFrame)
        self.lastAnimation = animName
        return result

    def takeDamage(self):
        if self.currHP <= 0:
            return

        if not self.lastAnimation:
            self.lastAnimation = self.getCurrentAnim()

        hpTaken = random.randrange(30, 50)
        self.currHP = max(0, self.currHP - hpTaken)
        self.showHpText(-hpTaken)
        self.updateHealthBar()

        if self.hurtSequence:
            self.hurtSequence.pause()

        if self.currHP > 0:
            self.hurtSequence = Sequence(
                Func(self.setTunnelRunning, False),
                Func(self.getLeftHand().getChildren().hide),
                Func(self.getRightHand().getChildren().hide),
                ActorInterval(self, 'pie-small-react'),
                Func(self.getLeftHand().getChildren().show),
                Func(self.getRightHand().getChildren().show),
                Func(self.loop, self.lastAnimation),
                Func(self.setTunnelRunning, True))
        else:
            self.hurtSequence = Sequence(Func(self.pauseTunnel),
                                         self.getExplosionTrack(),
                                         Func(self.announceDead))

        self.hurtSequence.start()

    def removeFromGame(self):
        if self.arena.enemyGrid:
            self.arena.enemyGrid.releaseSpot(self.spotToken)

        if self.hurtSequence:
            self.hurtSequence.pause()
            self.hurtSequence = None

        self.pauseTunnel()

    def enterOff(self):
        self.removeFromGame()

        if self.getCurrentAnim() != 'neutral':
            self.loop('neutral')

        if self.aiCharacter:
            self.disableAI()

    def exitOff(self):
        pass

    def enterTunnelIn(self, tunnel):
        self.pauseTunnel()
        pivotNode = tunnel.attachNewNode(self.uniqueName('pivotNode'))
        pivotNode.setPos(tunnelPivotPos)
        pivotNode.setHpr(0, 0, 0)
        endX = tunnel.getX(render) * 0.4
        pivotLerpDur = pivotDur * (90.0 / pivotAngle)
        self.reparentTo(pivotNode)
        self.setPos(0, 0, 0)
        self.setX(tunnel, endX)
        startX = self.getX()
        targetX = startX - random.uniform(1.0, 15.0)
        self.setHpr(tunnel, 0, 0, 0)
        pivotNode.setH(-pivotAngle)

        finalX = startX - random.uniform(10.5, 14.5)
        finalY = -48
        finalPos = Point3(finalX, finalY, 0)

        self.tunnelSequence = Sequence(
            Func(self.loop, 'walk'),
            Parallel(
                pivotNode.hprInterval(pivotDur,
                                      hpr=Point3(0, 0, 0),
                                      name=self.uniqueName('tunnelInPivot')),
                Sequence(
                    Wait(pivotDur - pivotLerpDur),
                    self.posInterval(
                        pivotLerpDur,
                        pos=Point3(targetX, 0, 0),
                        name=self.uniqueName('tunnelInPivotLerpPos')),
                )), Func(self.wrtReparentTo, render),
            Func(pivotNode.removeNode), Func(self.headsUp, finalPos),
            self.posInterval(pivotLerpDur + 1.0, pos=finalPos),
            Func(self.fsm.request, 'WalkToGrid'))
        self.tunnelSequence.start()

    def exitTunnelIn(self):
        self.pauseTunnel()

    def enterWalkToGrid(self):
        pos = self.getGridPos()
        targetHpr = GameUtils.getHeadsUpHpr(self.getPos(render), pos)
        targetHpr[0] = PythonUtil.fitDestAngle2Src(self.getH(), targetHpr[0])
        hqHpr = GameUtils.getHeadsUpHpr(pos, self.arena.tempHQ.getPos(render))
        hqHpr[0] = PythonUtil.fitDestAngle2Src(targetHpr[0], hqHpr[0])

        distance = (self.getPos() - pos).length()
        hDiff = abs(self.getH() - targetHpr.getX())
        hqHDiff = abs(targetHpr.getX() - hqHpr.getX())

        self.tunnelSequence = Sequence(
            Func(self.loop, 'walk'),
            self.hprInterval(hDiff / GameGlobals.EnemyTurnSpeed,
                             targetHpr,
                             blendType='easeInOut'),
            self.posInterval(distance / GameGlobals.EnemyForwardSpeed,
                             pos,
                             blendType='easeInOut'),
            self.hprInterval(hqHDiff / GameGlobals.EnemyTurnSpeed,
                             hqHpr,
                             blendType='easeInOut'),
            Func(self.loop, 'neutral'), Func(self.__reachedGrid))
        self.tunnelSequence.start()

    def exitWalkToGrid(self):
        pass

    def __reachedGrid(self):
        if random.random() > 0.5:
            self.fsm.request('AttackHQ')
        else:
            self.fsm.request('Idle')

    def enterIdle(self, *args):
        pos = self.getGridPos()
        hpr = GameUtils.getHeadsUpHpr(pos, self.arena.tempHQ.getPos(render))
        self.reparentTo(render)
        self.setPosHpr(pos, hpr)

        if self.getCurrentAnim() != 'neutral':
            self.loop('neutral')

        taskMgr.doMethodLater(random.randrange(1.0, 10.0), self.attackNow,
                              self.uniqueName('nextAttack'))

    def exitIdle(self):
        taskMgr.remove(self.uniqueName('nextAttack'))

    def enterAttackHQ(self):
        attack = EnemyAttacks.chooseAttack(self)
        hitPoint = self.arena.getHQHitPoint()
        self.pauseTunnel()

        self.tunnelSequence = Sequence(attack(self, hitPoint),
                                       Func(self.fsm.request, 'Idle'))
        self.tunnelSequence.start()

    def exitAttackHQ(self):
        self.pauseTunnel()

    def attackNow(self, task):
        if self.hurtSequence and self.hurtSequence.isPlaying():
            return task.again

        self.fsm.request('AttackHQ')
class Toon(Avatar.Avatar, ToonHead, ToonDNA.ToonDNA):
    def __init__(self, cr, mat=0):
        self.cr = cr
        try:
            self.Toon_initialized
            return
        except:
            self.Toon_initialized = 1
        Avatar.Avatar.__init__(self, mat)
        ToonDNA.ToonDNA.__init__(self)
        ToonHead.__init__(self, cr)
        self.forwardSpeed = 0.0
        self.rotateSpeed = 0.0
        self.strafeSpeed = 0.0
        self.avatarType = CIGlobals.Toon
        self.track = None
        self.standWalkRunReverse = None
        self.playingAnim = None
        self.playingRate = None
        self.tag = None
        self.money = 0
        self.lookAtTrack = None
        self.portal1 = None
        self.portal2 = None
        self.gunAttached = False
        self.gun = None
        self.tokenIcon = None
        self.tokenIconIval = None
        self.forcedTorsoAnim = None
        self.fallSfx = base.audio3d.loadSfx(
            "phase_4/audio/sfx/MG_cannon_hit_dirt.ogg")
        base.audio3d.attachSoundToObject(self.fallSfx, self)
        self.eyes = loader.loadTexture("phase_3/maps/eyes.jpg",
                                       "phase_3/maps/eyes_a.rgb")
        self.myTaskId = random.uniform(0, 1231231232132131231232)
        self.closedEyes = loader.loadTexture("phase_3/maps/eyesClosed.jpg",
                                             "phase_3/maps/eyesClosed_a.rgb")
        self.soundChatBubble = loader.loadSfx(
            "phase_3/audio/sfx/GUI_balloon_popup.ogg")
        self.shadowCaster = None
        self.chatSoundDict = {}
        self.backpack = None
        self.animFSM = ClassicFSM('Toon', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral),
            State('swim', self.enterSwim, self.exitSwim),
            State('walk', self.enterWalk, self.exitWalk),
            State('run', self.enterRun, self.exitRun),
            State('openBook', self.enterOpenBook, self.exitOpenBook),
            State('readBook', self.enterReadBook, self.exitReadBook),
            State('closeBook', self.enterCloseBook, self.exitCloseBook),
            State('teleportOut', self.enterTeleportOut, self.exitTeleportOut),
            State('teleportIn', self.enterTeleportIn, self.exitTeleportIn),
            State('died', self.enterDied, self.exitDied),
            State('fallFWD', self.enterFallFWD, self.exitFallFWD),
            State('fallBCK', self.enterFallBCK, self.exitFallBCK),
            State('jump', self.enterJump, self.exitJump),
            State('leap', self.enterLeap, self.exitLeap),
            State('laugh', self.enterLaugh, self.exitLaugh),
            State('happy', self.enterHappyJump, self.exitHappyJump),
            State('shrug', self.enterShrug, self.exitShrug),
            State('hdance', self.enterHDance, self.exitHDance),
            State('wave', self.enterWave, self.exitWave),
            State('scientistEmcee', self.enterScientistEmcee,
                  self.exitScientistEmcee),
            State('scientistWork', self.enterScientistWork,
                  self.exitScientistWork),
            State('scientistGame', self.enterScientistGame,
                  self.exitScientistGame),
            State('scientistJealous', self.enterScientistJealous,
                  self.exitScientistJealous),
            State('cringe', self.enterCringe, self.exitCringe),
            State('conked', self.enterConked, self.exitConked),
            State('win', self.enterWin, self.exitWin),
            State('walkBack', self.enterWalkBack, self.exitWalkBack),
            State('deadNeutral', self.enterDeadNeutral, self.exitDeadNeutral),
            State('deadWalk', self.enterDeadWalk, self.exitDeadWalk),
            State('squish', self.enterSquish, self.exitSquish),
            State('Happy', self.enterHappy, self.exitHappy),
            State('Sad', self.enterSad, self.exitSad)
        ], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()

        if not hasattr(base, 'localAvatar') or not base.localAvatar == self:
            Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 3, 1)

    def showAvId(self):
        pass

    def showName(self):
        pass

    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('legs', lodName)
            joint = bundle.findChild('joint_nameTag')
            if joint:
                joints.append(joint)

        return joints

    def enterHappy(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = None
        self.standWalkRunReverse = (('neutral', 1.0), ('walk', 1.0),
                                    ('run', 1.0), ('walk', -1.0),
                                    ('strafe', 1.0), ('strafe', -1.0))
        self.setSpeed(self.forwardSpeed, self.rotateSpeed)

    def exitHappy(self):
        self.standWalkRunReverse = None
        self.stop()

    def enterSad(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'sad'
        self.standWalkRunReverse = (('dneutral', 1.0), ('dwalk', 1.2),
                                    ('dwalk', 1.2), ('dwalk', -1.0))
        self.setSpeed(0, 0)

    def exitSad(self):
        self.standWalkRunReverse = None
        self.stop()
        #if hasattr(self, 'doId'):
        #    if hasattr(base, 'localAvatar'):
        #        if base.localAvatar.doId == self.doId:
        #            self.controlManager.enableAvatarJump()

    def setSpeed(self, forwardSpeed, rotateSpeed, strafeSpeed=0.0):
        self.forwardSpeed = forwardSpeed
        self.rotateSpeed = rotateSpeed
        self.strafeSpeed = strafeSpeed
        action = None
        if self.standWalkRunReverse != None:
            if (forwardSpeed >= CIGlobals.RunCutOff
                    and strafeSpeed < CIGlobals.RunCutOff
                    and strafeSpeed > -CIGlobals.RunCutOff):
                action = CIGlobals.RUN_INDEX
            elif strafeSpeed >= CIGlobals.RunCutOff or strafeSpeed <= -CIGlobals.RunCutOff:
                if strafeSpeed > 0:
                    action = CIGlobals.STRAFE_RIGHT_INDEX
                elif strafeSpeed < 0:
                    action = CIGlobals.STRAFE_LEFT_INDEX
            elif forwardSpeed > CIGlobals.WalkCutOff:
                action = CIGlobals.WALK_INDEX
            elif forwardSpeed < -CIGlobals.WalkCutOff:
                action = CIGlobals.REVERSE_INDEX
            elif rotateSpeed != 0.0:
                action = CIGlobals.WALK_INDEX
            else:
                action = CIGlobals.STAND_INDEX
            anim, rate = self.standWalkRunReverse[action]
            if anim != self.playingAnim or rate != self.playingRate:
                self.playingAnim = anim
                self.playingRate = rate
                doingGagAnim = False
                if self.backpack:
                    if self.backpack.getCurrentGag():
                        if self.backpack.getCurrentGag().getState() in [
                                GagState.START, GagState.RELEASED
                        ]:
                            doingGagAnim = True
                            self.loop(anim, partName="legs")
                            if self.animal == "dog":
                                self.loop(anim, partName="head")
                if not doingGagAnim:
                    if self.forcedTorsoAnim == None:
                        self.loop(anim)
                    else:
                        self.loop(self.forcedTorsoAnim, partName='head')
                        self.loop(self.forcedTorsoAnim, partName='torso')
                        self.loop(anim, partName='legs')
                self.setPlayRate(rate, anim)
        return action

    def enterSquish(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'squish'
        sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.ogg')
        lerpTime = 0.1
        node = self.getGeomNode().getChild(0)
        origScale = node.getScale()
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('getSquished')
        else:
            name = 'getSquished'
        self.track = Sequence(LerpScaleInterval(node,
                                                lerpTime,
                                                VBase3(2, 2, 0.025),
                                                blendType='easeInOut'),
                              Wait(1.0),
                              Parallel(
                                  Sequence(
                                      Wait(0.4),
                                      LerpScaleInterval(node,
                                                        lerpTime,
                                                        VBase3(1.4, 1.4, 1.4),
                                                        blendType='easeInOut'),
                                      LerpScaleInterval(node,
                                                        lerpTime / 2.0,
                                                        VBase3(0.8, 0.8, 0.8),
                                                        blendType='easeInOut'),
                                      LerpScaleInterval(
                                          node,
                                          lerpTime / 3.0,
                                          origScale,
                                          blendType='easeInOut')),
                                  ActorInterval(self, 'happy', startTime=0.2),
                                  SoundInterval(sound)),
                              name=name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.squishDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.start(ts)

    def squishDone(self, callback=None, extraArgs=[]):
        self.__doCallback(callback, extraArgs)

    def exitSquish(self):
        if self.track:
            self.ignore(self.track.getName())
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track.finish()
            self.track = None
        self.playingAnim = 'neutral'

    def enterDeadNeutral(self, ts=0, callback=None, extraArgs=[]):
        self.loop('dneutral')

    def exitDeadNeutral(self):
        self.stop()

    def enterDeadWalk(self, ts=0, callback=None, extraArgs=[]):
        self.loop('dwalk')

    def exitDeadWalk(self):
        self.stop()

    def setBackpack(self, pack):
        self.backpack = pack

    def getGhost(self):
        return 0

    def updateChatSoundDict(self):
        self.chatSoundDict['exclaim'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('exclaim'))
        self.chatSoundDict['question'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('question'))
        self.chatSoundDict['short'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('short'))
        self.chatSoundDict['medium'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('med'))
        self.chatSoundDict['long'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('long'))
        self.chatSoundDict['howl'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('howl'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['exclaim'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['question'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['short'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['medium'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['long'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['howl'],
                                         self.getPart('head'))

    def ghostOn(self):
        self.getGeomNode().hide()
        self.nametag3d.hide()
        self.getShadow().hide()
        if self.tokenIcon:
            self.tokenIcon.hide()
        self.stashBodyCollisions()

    def ghostOff(self):
        self.unstashBodyCollisions()
        if self.tokenIcon:
            self.tokenIcon.show()
        self.getShadow().show()
        self.nametag3d.show()
        self.getGeomNode().show()

    def attachGun(self, gunName):
        self.detachGun()
        if gunName == "pistol":
            self.gun = loader.loadModel("phase_4/models/props/water-gun.bam")
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(0.28, 0.1, 0.08))
            self.gun.setHpr(VBase3(85.6, -4.44, 94.43))
            self.gunAttached = True
        elif gunName == "shotgun":
            self.gun = loader.loadModel("phase_4/models/props/shotgun.egg")
            self.gun.setScale(0.75)
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(-0.5, -0.2, 0.19))
            self.gun.setHpr(Vec3(350, 272.05, 0))
            color = random.choice([
                VBase4(1, 0.25, 0.25, 1),
                VBase4(0.25, 1, 0.25, 1),
                VBase4(0.25, 0.25, 1, 1)
            ])
            self.gun.setColorScale(color)
            self.gunAttached = True
        elif gunName == "sniper":
            self.gun = loader.loadModel("phase_4/models/props/sniper.egg")
            self.gun.setScale(0.75)
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(-0.5, -0.2, 0.19))
            self.gun.setHpr(Vec3(350, 272.05, 0))
            color = random.choice([
                VBase4(1, 0.25, 0.25, 1),
                VBase4(0.25, 1, 0.25, 1),
                VBase4(0.25, 0.25, 1, 1)
            ])
            self.gun.setColorScale(color)
            self.gunAttached = True

    def detachGun(self):
        if self.gun and self.gunAttached:
            self.gun.removeNode()
            self.gun = None
            self.gunAttached = False

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                notify.warning(
                    "animFSM in flux, state=%s, not requesting off" %
                    self.animFSM.getCurrentState().getName())
        else:
            notify.warning("animFSM has been deleted")
        if self.track != None:
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        return

    def disable(self):
        try:
            self.Toon_disabled
        except:
            self.Toon_disabled = 1
            self.backpack = None
            self.stopAnimations()
            self.removeAdminToken()
            ToonHead.delete(self)
            self.deleteCurrentToon()
            self.chatSoundDict = {}
            Avatar.Avatar.disable(self)

    def delete(self):
        try:
            self.Toon_deleted
        except:
            self.Toon_deleted = 1
            del self.animFSM
            self.forwardSpeed = None
            self.chatSoundDict = None
            self.rotateSpeed = None
            self.avatarType = None
            self.track = None
            self.standWalkRunReverse = None
            self.currentAnim = None
            self.toon_head = None
            self.forcedTorsoAnim = None
            self.toon_torso = None
            self.toon_legs = None
            self.gender = None
            self.headtype = None
            self.head = None
            self.legtype = None
            self.torsotype = None
            self.hr = None
            self.hg = None
            self.hb = None
            self.tr = None
            self.tg = None
            self.tb = None
            self.lr = None
            self.lg = None
            self.lb = None
            self.shir = None
            self.shig = None
            self.shib = None
            self.shor = None
            self.shog = None
            self.shob = None
            self.shirt = None
            self.sleeve = None
            self.short = None
            self.tag = None
            self.money = None
            self.lookAtTrack = None
            self.portal1 = None
            self.portal2 = None
            self.backpack = None
            self.fallSfx = None
            self.eyes = None
            self.myTaskId = None
            self.closedEyes = None
            self.soundChatBubble = None
            self.lastAction = None
            self.lastState = None
            self.playingAnim = None
            self.playingRate = None
            Avatar.Avatar.delete(self)
        return

    def initCollisions(self):
        self.collNodePath.setCollideMask(BitMask32(0))
        self.collNodePath.node().setFromCollideMask(CIGlobals.WallBitmask)

        pusher = CollisionHandlerPusher()
        pusher.setInPattern("%in")
        pusher.addCollider(self.collNodePath, self)
        base.cTrav.addCollider(self.collNodePath, pusher)

    def deleteCurrentToon(self):
        if self.shadowCaster:
            self.shadowCaster.clear()
            self.shadowCaster = None
        try:
            self.stopLookAround()
            self.stopBlink()
        except:
            pass
        self.pupils = []
        if 'head' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['head']
        if 'torso' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['torso']
        if 'legs' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['legs']
        self.deleteShadow()
        self.removePart('head')
        self.removePart('torso')
        self.removePart('legs')
        self.detachGun()

    def setAdminToken(self, tokenId):
        if tokenId in ToonGlobals.STAFF_TOKENS.keys():
            icons = loader.loadModel("phase_3/models/props/gm_icons.bam")
            self.tokenIcon = icons.find('**/access_level_%s' %
                                        (ToonGlobals.STAFF_TOKENS[tokenId]))
            self.tokenIcon.reparentTo(self)
            x = self.nametag3d.getX()
            y = self.nametag3d.getY()
            z = self.nametag3d.getZ()
            self.tokenIcon.setPos(Vec3(x, y, z) + (0, 0, 0.5))
            self.tokenIcon.setScale(0.4)
            self.tokenIconIval = Sequence(
                LerpHprInterval(self.tokenIcon,
                                duration=3.0,
                                hpr=Vec3(360, 0, 0),
                                startHpr=Vec3(0, 0, 0)))
            self.tokenIconIval.loop()
            icons.removeNode()

    def removeAdminToken(self):
        if self.tokenIcon != None and self.tokenIconIval != None:
            self.tokenIconIval.finish()
            self.tokenIcon.removeNode()
            self.tokenIconIval = None
            self.tokenIcon = None

    def setChat(self, chatString):
        if not self.isThought(chatString):
            if not self.getGhost() or self.doId == base.localAvatar.doId:
                if "ooo" in chatString.lower():
                    sfx = self.chatSoundDict['howl']
                elif "!" in chatString.lower():
                    sfx = self.chatSoundDict['exclaim']
                elif "?" in chatString.lower():
                    sfx = self.chatSoundDict['question']
                elif len(chatString) <= 9:
                    sfx = self.chatSoundDict['short']
                elif 10 <= len(chatString) <= 19:
                    sfx = self.chatSoundDict['medium']
                elif len(chatString) >= 20:
                    sfx = self.chatSoundDict['long']
                base.playSfx(sfx, node=self)

        Avatar.Avatar.setChat(self, chatString)

    def setName(self, nameString):
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType)

    def setDNAStrand(self, dnaStrand, makeTag=1):
        ToonDNA.ToonDNA.setDNAStrand(self, dnaStrand)
        self.deleteCurrentToon()
        self.generateToon(makeTag)

    def generateToon(self, makeTag=1):
        self.generateLegs()
        self.generateTorso()
        self.generateHead()
        self.setToonColor()
        self.setClothes()
        self.setGloves()
        self.parentToonParts()
        self.rescaleToon()
        if makeTag:
            self.setupNameTag()
        Avatar.Avatar.initShadow(self)
        if self.cr.isShowingPlayerIds:
            self.showAvId()
        self.updateChatSoundDict()

    def attachTNT(self):
        self.pies.attachTNT()
        self.holdTNTAnim()

    def detachTNT(self):
        self.pies.detachTNT()
        self.animFSM.request(self.animFSM.getCurrentState().getName())

    def holdTNTAnim(self):
        self.pose("toss", 22, partName="torso")

    def parentToonParts(self):
        self.attach('head', 'torso', 'def_head')
        self.attach('torso', 'legs', 'joint_hips')

    def unparentToonParts(self):
        self.getPart('head').reparentTo(self.getGeomNode())
        self.getPart('torso').reparentTo(self.getGeomNode())
        self.getPart('legs').reparentTo(self.getGeomNode())

    def rescaleToon(self):
        animal = self.getAnimal()
        bodyScale = CIGlobals.toonBodyScales[animal]
        headScale = CIGlobals.toonHeadScales[animal][2]
        shoulderHeight = CIGlobals.legHeightDict[
            self.legs] * bodyScale + CIGlobals.torsoHeightDict[
                self.torso] * bodyScale
        height = shoulderHeight + CIGlobals.headHeightDict[
            self.head] * headScale
        bodyScale = CIGlobals.toonBodyScales[animal]
        self.setAvatarScale(bodyScale)
        self.getPart('head').setScale(headScale)
        self.setHeight(height)

    def setGloves(self):
        color = self.getGloveColor()
        gloves = self.find('**/hands')
        gloves.setColor(color)

    def setClothes(self):
        shirt, shirtcolor = self.getShirtStyle()
        short, shortcolor = self.getShortStyle()
        sleeve, sleevecolor = self.getSleeveStyle()
        torsot = self.findAllMatches('**/torso-top')
        torsob = self.findAllMatches('**/torso-bot')
        sleeves = self.findAllMatches('**/sleeves')
        torsot.setTexture(loader.loadTexture(shirt), 1)
        torsob.setTexture(loader.loadTexture(short), 1)
        sleeves.setTexture(loader.loadTexture(sleeve), 1)
        torsot.setColor(shirtcolor)
        sleeves.setColor(sleevecolor)
        torsob.setColor(shortcolor)

    def generateLegs(self):
        ToonGlobals.generateBodyPart(self, 'legs', self.getLegs(), 3, 'shorts')
        self.find('**/boots_long').stash()
        self.find('**/boots_short').stash()
        self.find('**/shoes').stash()

    def generateTorso(self):
        ToonGlobals.generateBodyPart(self, 'torso', self.getTorso(), 3, '')

    def generateHead(self, pat=0):
        gender = self.getGender()
        head = self.getAnimal()
        headtype = self.getHead()
        ToonHead.generateHead(self, gender, head, headtype)

    def setToonColor(self):
        self.setHeadColor()
        self.setTorsoColor()
        self.setLegColor()

    def setLegColor(self):
        legcolor = self.getLegColor()
        self.findAllMatches('**/legs').setColor(legcolor)
        self.findAllMatches('**/feet').setColor(legcolor)

    def setTorsoColor(self):
        torsocolor = self.getTorsoColor()
        self.findAllMatches('**/arms').setColor(torsocolor)
        self.findAllMatches('**/neck').setColor(torsocolor)
        self.findAllMatches('**/hands').setColor(1, 1, 1, 1)

    def setForcedTorsoAnim(self, string):
        self.forcedTorsoAnim = string
        self.loop(string, partName="torso")

    def clearForcedTorsoAnim(self):
        self.forcedTorsoAnim = None
        self.animFSM.request(self.animFSM.getCurrentState().getName())

    def enterOff(self, ts=0, callback=None, extraArgs=[]):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterWin(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'win'
        self.sfx = base.audio3d.loadSfx("phase_3.5/audio/sfx/ENC_Win.ogg")
        self.sfx.setLoop(True)
        base.audio3d.attachSoundToObject(self.sfx, self)
        base.playSfx(self.sfx, node=self)
        self.loop("win")

    def exitWin(self):
        self.stop()
        self.sfx.stop()
        del self.sfx
        self.playingAnim = 'neutral'

    def enterShrug(self, ts=0, callback=None, extraArgs=[]):
        self.play("shrug")

    def exitShrug(self):
        self.exitGeneral()

    def enterHDance(self, ts=0, callback=None, extraArgs=[]):
        self.play("hdance")

    def exitHDance(self):
        self.exitGeneral()

    def enterScientistWork(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scwork")

    def exitScientistWork(self):
        self.exitGeneral()

    def enterScientistEmcee(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scemcee")

    def exitScientistEmcee(self):
        self.exitGeneral()

    def enterScientistGame(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scgame")

    def exitScientistGame(self):
        self.exitGeneral()

    def enterScientistJealous(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scjealous")

    def exitScientistJealous(self):
        self.exitGeneral()

    def enterWave(self, ts=0, callback=None, extraArgs=[]):
        self.play("wave")

    def exitWave(self):
        self.exitGeneral()

    def enterLaugh(self, ts=0, callback=None, extraArgs=[]):
        self.setPlayRate(5.0, "neutral")
        self.loop("neutral")

    def exitLaugh(self):
        self.setPlayRate(1.0, "neutral")
        self.stop()

    def enterNeutral(self, ts=0, callback=None, extraArgs=[]):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() in [
                        GagState.START, GagState.RELEASED
                ]:
                    self.loop("neutral", partName="legs")
                    if self.animal == "dog":
                        self.loop("neutral", partName="head")
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop("neutral", partName="legs")
            return
        self.loop("neutral")
        self.playingAnim = 'neutral'

    def exitNeutral(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def exitGeneral(self):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() in [
                        GagState.START, GagState.RELEASED
                ]:
                    self.stop(partName='legs')
                else:
                    self.stop()
            else:
                self.stop()
        else:
            self.stop()

    def enterRun(self, ts=0, callback=None, extraArgs=[]):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() in [
                        GagState.START, GagState.RELEASED
                ]:
                    self.loop("run", partName="legs")
                    if self.animal == "dog":
                        self.loop("run", partName="head")
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop("run", partName="legs")
            return
        self.loop("run")

    def exitRun(self):
        self.exitGeneral()

    def enterWalk(self, ts=0, callback=None, extraArgs=[]):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() in [
                        GagState.START, GagState.RELEASED
                ]:
                    self.loop("walk", partName="legs")
                    if self.animal == "dog":
                        self.loop("walk", partName="head")
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop("walk", partName="legs")
            return
        self.loop("walk")

    def exitWalk(self):
        self.exitGeneral()

    def enterWalkBack(self, ts=0, callback=None, extraArgs=[]):
        self.setPlayRate(-1.0, "walk")
        self.enterWalk()

    def exitWalkBack(self):
        self.exitWalk()
        self.setPlayRate(1.0, "walk")

    def enterOpenBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book1 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book1.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self,
                                   "book",
                                   startFrame=CIGlobals.OpenBookFromFrame,
                                   endFrame=CIGlobals.OpenBookToFrame,
                                   name=self.uniqueName('enterOpenBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback,
                        [callback, extraArgs])
        self.track.start(ts)
        self.book1.play("chan",
                        fromFrame=CIGlobals.OpenBookFromFrame,
                        toFrame=CIGlobals.OpenBookToFrame)

    def exitOpenBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book1:
            self.book1.cleanup()
            self.book1 = None
        self.playingAnim = 'neutral'

    def enterReadBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book2 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book2.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))

        self.pingpong("book",
                      fromFrame=CIGlobals.ReadBookFromFrame,
                      toFrame=CIGlobals.ReadBookToFrame)
        self.book2.pingpong("chan",
                            fromFrame=CIGlobals.ReadBookFromFrame,
                            toFrame=CIGlobals.ReadBookToFrame)

    def exitReadBook(self):
        if self.book2:
            self.book2.cleanup()
            self.book2 = None
        self.playingAnim = 'neutral'

    def enterCloseBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book3 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book3.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self,
                                   "book",
                                   startFrame=CIGlobals.CloseBookFromFrame,
                                   endFrame=CIGlobals.CloseBookToFrame,
                                   name=self.uniqueName('enterCloseBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback,
                        [callback, extraArgs])
        self.track.start(ts)
        self.book3.play("chan",
                        fromFrame=CIGlobals.CloseBookFromFrame,
                        toFrame=CIGlobals.CloseBookToFrame)

    def exitCloseBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book3:
            self.book3.cleanup()
            self.book3 = None
        self.playingAnim = 'neutral'

    def enterTeleportOut(self, ts=0, callback=None, extraArgs=[]):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": enterTeleportOut")
        self.playingAnim = 'tele'
        self.portal1 = Actor(
            "phase_3.5/models/props/portal-mod.bam",
            {"chan": "phase_3.5/models/props/portal-chan.bam"})
        self.portal1.play("chan")
        self.portal1.reparentTo(
            self.getPart('legs').find('**/def_joint_right_hold'))
        self.play("tele")
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterTeleportOut')
        else:
            name = 'enterTeleportOut'
        self.track = Sequence(Wait(0.4),
                              Func(self.teleportOutSfx),
                              Wait(1.3),
                              Func(self.throwPortal),
                              Wait(3.4),
                              name=name)
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportOutDone,
                        [callback, extraArgs])
        self.track.start(ts)

    def doPortalBins(self, portal):
        portal.setBin('shadow', 0)
        portal.setDepthWrite(0)
        portal.setDepthTest(0)

    def teleportOutDone(self, callback, requestStatus):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": teleportOutDone")
        self.__doCallback(callback, requestStatus)
        self.exitTeleportOut()

    def teleportOutSfx(self):
        self.outSfx = base.audio3d.loadSfx(
            "phase_3.5/audio/sfx/AV_teleport.ogg")
        base.audio3d.attachSoundToObject(self.outSfx, self.portal1)
        base.playSfx(self.outSfx, node=self)

    def throwPortal(self):
        self.doPortalBins(self.portal1)
        self.portal1.reparentTo(self.getPart('legs').find('**/joint_nameTag'))
        self.portal1.setScale(CIGlobals.PortalScale)
        self.portal1.setY(6.5)
        self.portal1.setH(180)

    def exitTeleportOut(self):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": exitTeleportOut")
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal1:
            self.portal1.cleanup()
            self.portal1 = None
        self.playingAnim = 'neutral'

    def getTeleportInTrack(self, portal):
        self.doPortalBins(portal)

        holeTrack = Sequence()
        holeTrack.append(Func(portal.reparentTo, self))
        pos = Point3(0, -2.4, 0)
        holeTrack.append(Func(portal.setPos, pos))
        holeTrack.append(
            ActorInterval(portal, 'chan', startTime=3.4, endTime=3.1))
        holeTrack.append(Wait(0.6))
        holeTrack.append(
            ActorInterval(portal, 'chan', startTime=3.1, endTime=3.4))

        def restorePortal(portal):
            portal.setPos(0, 0, 0)
            portal.detachNode()
            portal.clearBin()
            portal.clearDepthTest()
            portal.clearDepthWrite()

        holeTrack.append(Func(restorePortal, portal))
        toonTrack = Sequence(Wait(0.3), Func(self.getGeomNode().show),
                             Func(self.nametag3d.show),
                             ActorInterval(self, 'happy', startTime=0.45))
        if hasattr(self, 'uniqueName'):
            trackName = self.uniqueName('teleportIn')
        else:
            trackName = 'teleportIn'
        return Parallel(toonTrack, holeTrack, name=trackName)

    def enterTeleportIn(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'happy'
        self.portal2 = Actor(
            "phase_3.5/models/props/portal-mod.bam",
            {"chan": "phase_3.5/models/props/portal-chan.bam"})
        self.show()
        self.getGeomNode().hide()
        self.nametag3d.hide()
        self.track = self.getTeleportInTrack(self.portal2)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportInDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(
            self, self.track.getName())
        self.track.start(ts)

    def teleportInDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitTeleportIn()

    def exitTeleportIn(self):
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal2:
            self.portal2.cleanup()
            self.portal2 = None
        if self.getGeomNode():
            self.getGeomNode().show()
        if self.nametag3d:
            self.nametag3d.show()
        self.playingAnim = 'neutral'

    def enterFallFWD(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'fallf'
        self.play("fallf")
        Sequence(Wait(0.5), SoundInterval(self.fallSfx, node=self)).start()

    def exitFallFWD(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterFallBCK(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'fallb'
        self.play("fallb")
        Sequence(Wait(0.5), SoundInterval(self.fallSfx, node=self)).start()

    def exitFallBCK(self):
        self.playingAnim = 'neutral'
        self.exitGeneral()

    def enterHappyJump(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'happy'
        self.play("happy")

    def exitHappyJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterSwim(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'swim'
        self.loop("swim")
        self.getGeomNode().setP(-89.0)
        self.getGeomNode().setZ(4.0)
        nt = self.nametag3d
        nt.setX(0)
        nt.setY(-2)
        nt.setZ(5.0)

    def exitSwim(self):
        self.exitGeneral()
        self.getGeomNode().setP(0.0)
        self.getGeomNode().setZ(0.0)
        nt = self.nametag3d
        nt.setX(0)
        nt.setY(0)
        nt.setZ(self.getHeight() + 0.3)
        self.playingAnim = 'neutral'

    def enterDied(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'lose'
        self.isdying = True
        self.play("lose")
        self.track = Sequence(Wait(2.2),
                              Func(self.dieSfx),
                              Wait(2.8),
                              self.getGeomNode().scaleInterval(
                                  2,
                                  Point3(0.01),
                                  startScale=(self.getGeomNode().getScale())),
                              Func(self.delToon),
                              name=self.uniqueName('enterDied'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.diedDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(
            self, 'enterTeleportOut')
        self.track.start(ts)

    def diedDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitDied()

    def __doCallback(self, callback, extraArgs):
        if callback:
            if extraArgs:
                callback(*extraArgs)
            else:
                callback()

    def dieSfx(self):
        self.Losesfx = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_Lose.ogg")
        base.audio3d.attachSoundToObject(self.Losesfx, self)
        base.playSfx(self.Losesfx, node=self)

    def delToon(self):
        self.isdead = True

    def exitDied(self):
        if self.track != None:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        self.rescaleToon()
        self.playingAnim = 'neutral'

    def enterJump(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'jump'
        self.loop("jump")

    def exitJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterLeap(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'leap'
        self.loop("leap")

    def exitLeap(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterCringe(self, ts=0, callback=None, extraArgs=[]):
        self.play("cringe")

    def exitCringe(self):
        self.exitGeneral()

    def enterConked(self, ts=0, callback=None, extraArgs=[]):
        self.play("conked")

    def exitConked(self):
        self.exitGeneral()
class ToonFPS(DirectObject):
    notify = directNotify.newCategory("ToonFPS")

    WeaponName2DamageData = {"pistol": (30.0, 10.0, 150.0, 0.3),
        "shotgun": (40.0, 15.0, 155.0, 0.5), "sniper": (40.0, 15.0, 155.0, 0.5)}

    def __init__(self, mg, weaponName = "pistol"):
        self.mg = mg
        self.weaponName = weaponName
        self.v_model_root = None
        self.v_model = None
        self.weapon = None
        self.track = None
        self.draw = None
        self.shoot = None
        self.reload = None
        self.empty = None
        self.cockBack = None
        self.cockFwd = None
        self.player_node = None

        # blach (02Aug15)
        # Drastically improved the accuracy of bullets... DRASTICALLY
        self.shooterTrav = None
        self.shooterRay = None
        self.shooterRayNode = None
        self.shooterHandler = None

        self.gui = ToonFPSGui(self)
        self.fsm = ClassicFSM('ToonFPS', [State('off', self.enterOff, self.exitOff),
                State('alive', self.enterAlive, self.exitAlive),
                State('dead', self.enterDead, self.exitDead)],
                'off', 'off')
        #self.deadFSM = ClassicFSM('dead', [State('off', self.enterOff, self.exitOff),
        #		State('])
        self.aliveFSM = ClassicFSM('alive', [State('off', self.enterOff, self.exitOff),
                State('draw', self.enterDraw, self.exitDraw, ['idle']),
                State('idle', self.enterIdle, self.exitIdle, ['shoot', 'reload']),
                State('shoot', self.enterShoot, self.exitShoot, ['idle']),
                State('reload', self.enterReload, self.exitReload, ['idle'])],
                'off', 'off')
        self.fsm.getStateNamed('alive').addChild(self.aliveFSM)
        #self.fsm.getStateNamed('dead').addChild(self.deadFSM)
        self.fsm.enterInitialState()
        self.aliveFSM.enterInitialState()
        if self.weaponName == "pistol":
            self.ammo = 14
        elif self.weaponName == "shotgun":
            self.ammo = 7
        self.hp = 125
        self.max_hp = 125
        self.firstPerson = FirstPerson()

    def movementTask(self, task):

        if (inputState.isSet('jump') or
            base.localAvatar.walkControls.isAirborne):

            if base.localAvatar.getAnimState() != "jump":

                base.localAvatar.setAnimState("jump")
                base.localAvatar.playMovementSfx(None)

                self.mg.sendUpdate("jumpingAvatar", [base.localAvatar.doId])

        return Task.cont

    def enterAlive(self):
        if not self.mg.fsm.getCurrentState().getName() in ['gameOver', 'announceGameOver', 'finalScores']:
            self.start()
            self.resetHp()
            self.resetAmmo()
            if self.mg.fsm.getCurrentState().getName() == "play":
                self.reallyStart()

    def exitAlive(self):
        self.end()
        self.v_model.reparentTo(hidden)
        if self.mg.fsm.getCurrentState().getName() != "play":
            self.reallyEnd()

    def updatePoints(self):
        self.points = self.kills - self.deaths

    def enterDead(self, killer):
        base.localAvatar.getGeomNode().show()
        self.gui.end()
        base.localAvatar.attachCamera()
        self.freezeCamSfx = base.loadSfx("phase_4/audio/sfx/freeze_cam.ogg")
        self.freezeCamImage = None
        self.freezeCamImageFile = None
        base.camera.setZ(base.camera.getZ() + 2.0)
        taskMgr.add(
            self.cameraLookAtKillerTask,
            "lookAtKiller",
            extraArgs = [killer],
            appendTask = True
        )
        taskMgr.doMethodLater(
            2.0,
            self.startZoomOnKiller,
            "startFreezeCam",
            extraArgs = [killer],
            appendTask = True
        )

    def startZoomOnKiller(self, killer, task):
        taskMgr.add(
            self.__zoomOnKillerTask,
            "zoomOnKiller",
            extraArgs = [killer],
            appendTask = True
        )
        return task.done

    def __zoomOnKillerTask(self, killer, task):
        if base.camera.getDistance(killer) <= 10.0 and self.freezeCamSfx.status() == self.freezeCamSfx.READY:
            base.playSfx(self.freezeCamSfx)
        if base.camera.getDistance(killer) < 7.0:
            self.doFreezeCam()
            return task.done
        base.camera.setY(base.camera, 60 * globalClock.getDt())
        return task.again

    def doFreezeCam(self):
        taskMgr.remove("lookAtKiller")
        self.frameBuffer = PNMImage()
        base.win.getScreenshot(self.frameBuffer)
        self.freezeCamTex = Texture()
        self.freezeCamTex.load(self.frameBuffer)
        self.freezeCamImage = OnscreenImage(image = self.freezeCamTex, parent=render2d)

    def cameraLookAtKillerTask(self, killer, task):
        try:
            base.camera.lookAt(killer, 0, 0, 3)
        except AssertionError:
            pass
        return task.cont

    def exitDead(self):
        taskMgr.remove("zoomOnKiller")
        taskMgr.remove("lookAtKiller")
        taskMgr.remove("startFreezeCam")
        del self.freezeCamSfx
        if self.freezeCamImage:
            self.freezeCamImage.destroy()
        del self.freezeCamImage
        self.frameBuffer.clear()
        self.freezeCamTex.clear()
        del self.frameBuffer
        del self.freezeCamTex
        base.localAvatar.detachCamera()
        base.localAvatar.getGeomNode().hide()
        self.gui.start()

    def load(self):
        if self.weaponName == "pistol":
            self.draw = base.loadSfx("phase_4/audio/sfx/draw_secondary.ogg")
            self.shoot = base.loadSfx("phase_4/audio/sfx/pistol_shoot.ogg")
            self.reload = base.loadSfx("phase_4/audio/sfx/pistol_worldreload.ogg")
        elif self.weaponName == "sniper":
            self.draw = base.loadSfx("phase_4/audio/sfx/draw_primary.ogg")
            self.shoot = base.loadSfx("phase_4/audio/sfx/shotgun_shoot.ogg")
            self.cockBack = base.loadSfx("phase_4/audio/sfx/shotgun_cock_back.ogg")
            self.cockFwd = base.loadSfx("phase_4/audio/sfx/shotgun_cock_forward.ogg")
        elif self.weaponName == "shotgun":
            self.draw = base.loadSfx("phase_4/audio/sfx/draw_primary.ogg")
            self.shoot = base.loadSfx("phase_4/audio/sfx/shotgun_shoot.ogg")
            self.cockBack = base.loadSfx("phase_4/audio/sfx/shotgun_cock_back.ogg")
            self.cockFwd = base.loadSfx("phase_4/audio/sfx/shotgun_cock_forward.ogg")
        self.empty = base.loadSfx("phase_4/audio/sfx/shotgun_empty.ogg")
        self.v_model_root = base.camera.attachNewNode('v_model_root')
        self.v_model = Actor('phase_4/models/minigames/v_dgm.egg', {'pidle': 'phase_4/models/minigames/v_dgm-pistol-idle.egg',
                        'pshoot': 'phase_4/models/minigames/v_dgm-pistol-shoot.egg',
                        'preload': 'phase_4/models/minigames/v_dgm-pistol-reload.egg',
                        'pdraw': 'phase_4/models/minigames/v_dgm-pistol-draw.egg',
                        'sidle': 'phase_4/models/minigames/v_dgm-shotgun-idle.egg',
                        'sshoot': 'phase_4/models/minigames/v_dgm-shotgun-shoot.egg'})
        if self.weaponName == "pistol":
            self.weapon = loader.loadModel("phase_4/models/props/water-gun.bam")
            self.weapon.reparentTo(self.v_model.exposeJoint(None, "modelRoot", "Bone.011"))
            self.weapon.setX(-0.125)
            self.weapon.setY(0.5)
            self.weapon.setScale(0.65)
        elif self.weaponName == "sniper":
            self.weapon = loader.loadModel("phase_4/models/props/sniper.egg")
            self.weapon.reparentTo(self.v_model.exposeJoint(None, "modelRoot", "Bone.029"))
            self.weapon.setScale(0.75)
            self.weapon.setPos(0.45, -1.03, -1.17)
            self.weapon.setHpr(9.46, 308.19, 75.78)
        elif self.weaponName == "shotgun":
            self.weapon = loader.loadModel("phase_4/models/props/shotgun.egg")
            self.weapon.reparentTo(self.v_model.exposeJoint(None, "modelRoot", "Bone.029"))
            self.weapon.setScale(0.75)
            self.weapon.setPos(0.45, -1.03, -1.17)
            self.weapon.setHpr(9.46, 308.19, 75.78)
        self.gui.load()

    def start(self):
        base.camLens.setNear(0.1)

        self.shooterTrav = CollisionTraverser('ToonFPS.shooterTrav')
        ray = CollisionRay()
        rayNode = CollisionNode('ToonFPS.rayNode')
        rayNode.addSolid(ray)
        rayNode.setCollideMask(BitMask32(0))
        rayNode.setFromCollideMask(CIGlobals.WallBitmask | CIGlobals.FloorBitmask)
        self.shooterRay = ray
        self.shooterRayNode = base.camera.attachNewNode(rayNode)
        self.shooterHandler = CollisionHandlerQueue()
        self.shooterTrav.addCollider(self.shooterRayNode, self.shooterHandler)

        self.firstPerson.start()
        self.v_model_root.reparentTo(base.camera)
        self.v_model.reparentTo(self.v_model_root)
        if self.weaponName == "pistol":
            self.v_model_root.setZ(-1.8)
            self.v_model_root.setY(0.3)
            self.v_model_root.setX(-0.1)
            self.v_model_root.setH(2)
        elif self.weaponName == "sniper":
            self.v_model_root.setPos(-0.42, -0.81, -1.7)
            self.v_model_root.setHpr(359, 352.87, 0.00)
        elif self.weaponName == "shotgun":
            self.v_model_root.setPos(-0.42, -0.81, -1.7)
            self.v_model_root.setHpr(359, 352.87, 0.00)
        self.gui.start()
        self.firstPerson.disableMouse()
        self.aliveFSM.request('draw')

    def reallyStart(self):
        self.firstPerson.reallyStart()
        base.localAvatar.startTrackAnimToSpeed()
        #taskMgr.add(self.movementTask, "toonBattleMovement")

    def end(self):
        if self.aliveFSM.getCurrentState().getName() != 'off':
            self.aliveFSM.request('off')
        if self.firstPerson:
            self.firstPerson.enableMouse()
            self.firstPerson.end()
        taskMgr.remove("toonBattleMovement")
        if self.mg.fsm.getCurrentState().getName() != "play":
            self.fsm.request('off')

    def reallyEnd(self):
        try:
            self.ToonFPS_reallyEnded
            return
        except:
            self.ToonFPS_reallyEnded = 1
        if self.shooterRayNode:
            self.shooterRayNode.removeNode()
            self.shooterRayNode = None
        self.shooterRay = None
        self.shooterTrav = None
        self.shooterHandler = None
        if self.firstPerson:
            self.firstPerson.reallyEnd()
        if self.v_model_root:
            self.v_model_root.reparentTo(hidden)
        if self.v_model:
            self.v_model.reparentTo(hidden)
            self.v_model.setPosHpr(0, 0, 0, 0, 0, 0)
        if self.gui:
            self.gui.end()
        base.camLens.setNear(1.0)

    def cleanup(self):
        try:
            self.ToonFPS_cleanedUp
            return
        except:
            self.ToonFPS_cleanedUp = 1
        taskMgr.remove("lookAtKiller")
        taskMgr.remove("toonBattleMovement")
        if self.firstPerson:
            self.firstPerson.cleanup()
            self.firstPerson = None
        self.draw = None
        self.shoot = None
        self.reload = None
        self.empty = None
        self.ammo = None
        try:
            self.aliveFSM.requestFinalState()
            self.fsm.requestFinalState()
        except:
            self.notify.warning('Redundant call to enter the final state.')
        self.fsm = None
        self.aliveFSM = None
        self.player_node = None
        self.min_camerap = None
        self.max_camerap = None
        self.hp = None
        self.max_hp = None
        if self.v_model:
            self.v_model.cleanup()
            self.v_model = None
        if self.weapon:
            self.weapon.removeNode()
            self.weapon = None
        if self.weapon:
            self.v_model_root.removeNode()
            self.v_model_root = None
        if self.gui:
            self.gui.cleanup()

    def damageTaken(self, amount, avId):
        if self.hp <= 0.0:
            killer = self.mg.cr.doId2do.get(avId, None)
            self.fsm.request('dead', [killer])
        self.gui.adjustHpMeter()

    def enterDraw(self):
        self.draw.play()
        if self.weaponName == "pistol":
            self.track = ActorInterval(self.v_model, 'pdraw', playRate = 1.6, name = 'drawTrack')
        elif self.weaponName == "shotgun":
            self.v_model.pose('sidle', 15)
            self.track = LerpQuatInterval(self.v_model, duration = 0.5, quat = (0, 0, 0),
                startHpr = (70, -50, 0), blendType = 'easeOut', name = 'drawTrack')
        elif self.weaponName == "sniper":
            self.v_model.pose('sidle', 15)
            self.track = LerpQuatInterval(self.v_model, duration = 0.5, quat = (0, 0, 0),
                startHpr = (70, -50, 0), blendType = 'easeOut', name = 'drawTrack')
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.aliveFSM.request, ['idle'])
        self.track.start()

    def exitDraw(self):
        #self.draw.stop()
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None

    def enterIdle(self):
        if self.weaponName == "pistol":
            self.v_model.loop('pidle')
        elif self.weaponName == "shotgun":
            self.track = Sequence(LerpQuatInterval(self.v_model, duration = 2.0, quat = (0, 1, 0), startHpr = (0, 0, 0), blendType = 'easeInOut'),
                LerpQuatInterval(self.v_model, duration = 2.0, quat = (0, 0, 0), startHpr = (0, 1, 0), blendType = 'easeInOut'))
            self.track.loop()
        elif self.weaponName == "sniper":
            self.track = Sequence(LerpQuatInterval(self.v_model, duration = 2.0, quat = (0, 1, 0), startHpr = (0, 0, 0), blendType = 'easeInOut'),
                LerpQuatInterval(self.v_model, duration = 2.0, quat = (0, 0, 0), startHpr = (0, 1, 0), blendType = 'easeInOut'))
            self.track.loop()
        self.accept('mouse1', self.requestShoot)
        if self.ammo <= 0:
            self.gui.notifyNoAmmo()
        if self.ammo < 14:
            self.accept('r', self.aliveFSM.request, ['reload'])

    def requestShoot(self):
        if self.mg.fsm.getCurrentState().getName() != "play":
            return
        if self.ammo > 0:
            self.aliveFSM.request('shoot')
        else:
            self.empty.play()

    def exitIdle(self):
        self.v_model.stop()
        if self.track:
            self.track.finish()
            self.track = None
        self.ignore('mouse1')
        self.ignore('r')

    def enterShoot(self):
        self.shoot.play()
        if self.weaponName == "pistol":
            self.track = ActorInterval(self.v_model, 'pshoot', playRate = 2, name = 'shootTrack')
        elif self.weaponName == "shotgun":
            self.track = Parallel(
                Sequence(
                    LerpQuatInterval(self.v_model, duration = 0.05, quat = (0, 3, 0), startHpr = (0, 0, 0)),
                    LerpQuatInterval(self.v_model, duration = 0.1, quat = (0, 0, 0), startHpr = (0, 3, 0))
                ),
                Sequence(
                    LerpPosInterval(self.v_model, duration = 0.05, pos = (0, -0.3, 0), startPos = (0, 0, 0)),
                    LerpPosInterval(self.v_model, duration = 0.1, pos = (0, 0, 0), startPos = (0, -0.3, 0)),
                    Wait(0.1)
                ),
            )
        elif self.weaponName == "sniper":
            self.track = Parallel(
                Sequence(
                    LerpQuatInterval(self.v_model, duration = 0.05, quat = (0, 3, 0), startHpr = (0, 0, 0)),
                    LerpQuatInterval(self.v_model, duration = 0.1, quat = (0, 0, 0), startHpr = (0, 3, 0))
                ),
                Sequence(
                    LerpPosInterval(self.v_model, duration = 0.05, pos = (0, -0.3, 0), startPos = (0, 0, 0)),
                    LerpPosInterval(self.v_model, duration = 0.1, pos = (0, 0, 0), startPos = (0, -0.3, 0)),
                    Wait(0.1)
                ),
            )
        self.track.setDoneEvent('shootTrack')
        self.acceptOnce(self.track.getDoneEvent(), self.aliveFSM.request, ['idle'])
        self.track.start()
        self.ammo -= 1
        self.gui.adjustAmmoGui()
        self.mg.makeSmokeEffect(self.weapon.find('**/joint_nozzle').getPos(render))
        self.traverse()

    def traverse(self):
        mpos = base.mouseWatcherNode.getMouse()
        self.shooterRay.setFromLens(base.camNode, mpos.getX(), mpos.getY())

        self.shooterTrav.traverse(render)

    def calcDamage(self, avatar):
        dmgData = self.WeaponName2DamageData[self.weaponName]
        maxDamage = dmgData[0]
        minDistance = dmgData[1]
        maxDistance = dmgData[2]
        factor = dmgData[3]
        distance = base.localAvatar.getDistance(avatar)
        if distance < minDistance:
            distance = minDistance
        elif distance > maxDistance:
            distance = maxDistance
        damage = maxDamage - ((distance - minDistance) * factor)
        return damage

    def exitShoot(self):
        #self.shoot.stop()
        self.ignore('shootTrack')
        if self.track:
            self.track.finish()
            self.track = None

    def enterReload(self):
        self.gui.deleteNoAmmoLabel()
        if self.weaponName == "pistol":
            self.track = Parallel(Sequence(Wait(0.3), Func(self.reload.play), Func(self.resetAmmo)),
                ActorInterval(self.v_model, 'preload', playRate = 1.5), name = 'reloadTrack')
        elif self.weaponName == "shotgun":
            self.track = Sequence(Func(self.draw.play), LerpQuatInterval(self.v_model, duration = 0.5,
                quat = (70, -50, 0), startHpr = (0, 0, 0), blendType = 'easeIn'),
                SoundInterval(self.cockBack),
                SoundInterval(self.cockFwd),
                Func(self.resetAmmo),
                Func(self.draw.play),
                LerpQuatInterval(self.v_model, duration = 0.5, quat = (0, 0, 0), startHpr = (70, -50, 0),
                    blendType = 'easeOut'), name = 'reloadTrack')
        elif self.weaponName == "sniper":
            self.track = Sequence(Func(self.draw.play), LerpQuatInterval(self.v_model, duration = 0.5,
                quat = (70, -50, 0), startHpr = (0, 0, 0), blendType = 'easeIn'),
                SoundInterval(self.cockBack),
                SoundInterval(self.cockFwd),
                Func(self.resetAmmo),
                Func(self.draw.play),
                LerpQuatInterval(self.v_model, duration = 0.5, quat = (0, 0, 0), startHpr = (70, -50, 0),
                    blendType = 'easeOut'), name = 'reloadTrack')
        self.track.setDoneEvent('reloadTrack')
        self.acceptOnce(self.track.getDoneEvent(), self.aliveFSM.request, ['idle'])
        self.track.start()

    def exitReload(self):
        #self.reload.stop()
        self.ignore('reloadTrack')
        if self.track:
            self.track.finish()
            self.track = None

    def resetAmmo(self):
        if self.weaponName == "pistol":
            self.ammo = 14
        elif self.weaponName == "shotgun":
            self.ammo = 7
        elif self.weaponName == "sniper":
            self.ammo = 7
        self.gui.resetAmmo()

    def resetHp(self):
        self.hp = self.max_hp
        self.gui.adjustHpMeter()

    def enterOff(self):
        pass

    def exitOff(self):
        pass
예제 #11
0
class Toon(Avatar.Avatar, ToonHead):
    notify = DirectNotifyGlobal.directNotify.newCategory('Toon')

    def __init__(self):
        Avatar.Avatar.__init__(self)
        ToonHead.__init__(self)
        try:
            self.Toon_initialized
            return
        except:
            self.Toon_initialized = 1

        self.avatarType = 'toon'
        self.soundChatBubble = base.loadSfx(
            'phase_3/audio/sfx/GUI_balloon_popup.ogg')
        self.swimRunSfx = base.loadSfx(
            'phase_4/audio/sfx/AV_footstep_runloop_water.ogg')
        self.swimRunLooping = False
        self.animFSM = ClassicFSM('Toon', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral)
        ], 'off', 'off')

        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                self.notify.warning(
                    'animFSM in flux, state=%s, not requesting off' %
                    self.animFSM.getCurrentState().getName())
        else:
            self.notify.warning('animFSM has been deleted')
        if self.effectTrack != None:
            self.effectTrack.finish()
            self.effectTrack = None
        if self.emoteTrack != None:
            self.emoteTrack.finish()
            self.emoteTrack = None
        if self.stunTrack != None:
            self.stunTrack.finish()
            self.stunTrack = None
        if self.wake:
            self.wake.stop()
            self.wake.destroy()
            self.wake = None
        self.cleanupPieModel()
        return

    def delete(self):
        try:
            self.Toon_deleted
        except:
            self.Toon_deleted = 1
            self.stopAnimations()
            self.rightHands = None
            self.rightHand = None
            self.leftHands = None
            self.leftHand = None
            self.headParts = None
            self.torsoParts = None
            self.hipsParts = None
            self.legsParts = None
            del self.animFSM
            for bookActor in self.__bookActors:
                bookActor.cleanup()

            del self.__bookActors
            for holeActor in self.__holeActors:
                holeActor.cleanup()

            del self.__holeActors
            self.soundTeleport = None
            self.motion.delete()
            self.motion = None

            self.removeHeadMeter()
            self.removeGMIcon()
            self.removePartyHat()
            Avatar.Avatar.delete(self)
            ToonHead.delete(self)

    def updateToonDNA(self, newDNA, fForce=0):
        self.newDNA = newDNA
        self.style.gender = newDNA.getGender()
        oldDNA = self.style
        if fForce or newDNA.head != oldDNA.head:
            self.swapToonHead(newDNA.head)
        if fForce or newDNA.torso != oldDNA.torso:
            self.swapToonTorso(newDNA.torso, genClothes=0)
            self.loop('neutral')
        if fForce or newDNA.legs != oldDNA.legs:
            self.swapToonLegs(newDNA.legs)
        self.swapToonColor(newDNA)
        self.__swapToonClothes(newDNA)

    def parentToonParts(self):
        if self.hasLOD():
            for lodName in self.getLODNames():
                if base.config.GetBool('want-new-anims', 1):
                    if not self.getPart('torso',
                                        lodName).find('**/def_head').isEmpty():
                        self.attach('head', 'torso', 'def_head', lodName)
                    else:
                        self.attach('head', 'torso', 'joint_head', lodName)
                else:
                    self.attach('head', 'torso', 'joint_head', lodName)
                self.attach('torso', 'legs', 'joint_hips', lodName)

        else:
            self.attach('head', 'torso', 'joint_head')
            self.attach('torso', 'legs', 'joint_hips')

    def unparentToonParts(self):
        if self.hasLOD():
            for lodName in self.getLODNames():
                self.getPart('head', lodName).reparentTo(self.getLOD(lodName))
                self.getPart('torso', lodName).reparentTo(self.getLOD(lodName))
                self.getPart('legs', lodName).reparentTo(self.getLOD(lodName))

        else:
            self.getPart('head').reparentTo(self.getGeomNode())
            self.getPart('torso').reparentTo(self.getGeomNode())
            self.getPart('legs').reparentTo(self.getGeomNode())

    def generateToon(self):
        self.setDNAString()
        self.generateToonLegs()
        self.generateToonHead()
        self.generateToonTorso()
        self.generateToonColor()
        self.parentToonParts()
        #self.rescaleToon()
        #self.resetHeight()
        self.setupToonNodes()

    def setupToonNodes(self):
        rightHand = NodePath('rightHand')
        self.rightHand = None
        self.rightHands = []
        leftHand = NodePath('leftHand')
        self.leftHands = []
        self.leftHand = None
        for lodName in self.getLODNames():
            hand = self.getPart('torso', lodName).find('**/joint_Rhold')
            if base.config.GetBool('want-new-anims', 1):
                if not self.getPart(
                        'torso',
                        lodName).find('**/def_joint_right_hold').isEmpty():
                    hand = self.getPart(
                        'torso', lodName).find('**/def_joint_right_hold')
            else:
                hand = self.getPart('torso', lodName).find('**/joint_Rhold')
            self.rightHands.append(hand)
            rightHand = rightHand.instanceTo(hand)
            if base.config.GetBool('want-new-anims', 1):
                if not self.getPart(
                        'torso',
                        lodName).find('**/def_joint_left_hold').isEmpty():
                    hand = self.getPart('torso',
                                        lodName).find('**/def_joint_left_hold')
            else:
                hand = self.getPart('torso', lodName).find('**/joint_Lhold')
            self.leftHands.append(hand)
            leftHand = leftHand.instanceTo(hand)
            if self.rightHand == None:
                self.rightHand = rightHand
            if self.leftHand == None:
                self.leftHand = leftHand

        self.headParts = self.findAllMatches('**/__Actor_head')
        self.legsParts = self.findAllMatches('**/__Actor_legs')
        self.hipsParts = self.legsParts.findAllMatches('**/joint_hips')
        self.torsoParts = self.hipsParts.findAllMatches('**/__Actor_torso')
        return

    def initializeBodyCollisions(self, collIdStr):
        Avatar.Avatar.initializeBodyCollisions(self, collIdStr)
        if not self.ghostMode:
            self.collNode.setCollideMask(self.collNode.getIntoCollideMask()
                                         | BitmaskGlobals.PieBitmask)

    def generateToonLegs(self, copy=1):
        global Preloaded
        legStyle = self.newDNA.legs
        filePrefix = LegDict.get(legStyle)
        if filePrefix is None:
            self.notify.error('unknown leg style: %s' % legStyle)
        print(Preloaded[filePrefix + '-1000'])
        # self.loadModel(Preloaded[filePrefix+'-1000'], 'legs', '1000', True)
        self.loadModel(Preloaded[filePrefix + '-1000'])
        if not copy:
            self.showPart('legs', '1000')
        self.loadAnims(LegsAnimDict[legStyle], 'legs', '1000')
        self.findAllMatches('**/boots_short').stash()
        self.findAllMatches('**/boots_long').stash()
        self.findAllMatches('**/shoes').stash()
        return

    def swapToonLegs(self, legStyle, copy=1):
        self.unparentToonParts()
        self.removePart('legs', '1000')
        # Bugfix: Until upstream Panda3D includes this, we have to do it here.
        if 'legs' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['legs']
        self.style.legs = legStyle
        self.generateToonLegs(copy)
        self.generateToonColor()
        self.parentToonParts()
        self.rescaleToon()
        self.resetHeight()
        del self.shadowJoint
        self.initializeDropShadow()
        self.initializeNametag3d()

    def generateToonTorso(self, copy=1, genClothes=1):
        global Preloaded
        torsoStyle = self.style.torso
        filePrefix = TorsoDict.get(torsoStyle)
        if filePrefix is None:
            self.notify.error('unknown torso style: %s' % torsoStyle)
        self.loadModel(Preloaded[filePrefix + '-1000'], 'torso', '1000', True)
        if not copy:
            self.showPart('torso', '1000')
        self.loadAnims(TorsoAnimDict[torsoStyle], 'torso', '1000')
        if genClothes == 1 and not len(torsoStyle) == 1:
            self.generateToonClothes()
        return

    def swapToonTorso(self, torsoStyle, copy=1, genClothes=1):
        self.unparentToonParts()
        self.removePart('torso', '1000')
        # Bugfix: Until upstream Panda3D includes this, we have to do it here.
        if 'torso' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['torso']
        self.style.torso = torsoStyle
        self.generateToonTorso(copy, genClothes)
        self.generateToonColor()
        self.parentToonParts()
        self.rescaleToon()
        self.resetHeight()
        self.setupToonNodes()

    def setDNAString(self):
        self.newDNA = ToonDNA.ToonDNA()
        self.setDNA(self.newDNA)

    def setDNA(self, dna):
        self.style = dna
        #self.generateToon()

    def generateToonHead(self, copy=1):
        headHeight = ToonHead.generateToonHead(self, copy, self.style, '1000')
        if self.style.getAnimal() == 'dog':
            self.loadAnims(HeadAnimDict[self.style.head], 'head', '1000')

    def swapToonHead(self, headStyle=-1, copy=1):
        self.stopLookAroundNow()
        self.eyelids.request('open')
        self.unparentToonParts()
        self.removePart('head', '1000')
        # Bugfix: Until upstream Panda3D includes this, we have to do it here.
        if 'head' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['head']
        if headStyle > -1:
            self.style.head = headStyle
        self.generateToonHead(copy)
        self.generateToonColor()
        self.parentToonParts()
        self.rescaleToon()
        self.resetHeight()
        self.eyelids.request('open')
        self.startLookAround()

    def generateToonColor(self):
        ToonHead.generateToonColor(self, self.style)
        armColor = self.style.getArmColor()
        gloveColor = self.style.getGloveColor()
        legColor = self.style.getLegColor()
        for lodName in self.getLODNames():
            torso = self.getPart('torso', lodName)
            if len(self.style.torso) == 1:
                parts = torso.findAllMatches('**/torso*')
                parts.setColor(*armColor)
            for pieceName in ('arms', 'neck'):
                piece = torso.find('**/' + pieceName)
                piece.setColor(*armColor)

            hands = torso.find('**/hands')
            hands.setColor(*gloveColor)
            legs = self.getPart('legs', lodName)
            for pieceName in ('legs', 'feet'):
                piece = legs.find('**/%s;+s' % pieceName)
                piece.setColor(*legColor)

        if self.cheesyEffect == ToontownGlobals.CEGreenToon:
            self.reapplyCheesyEffect()

    def swapToonColor(self, dna):
        self.setStyle(dna)
        self.generateToonColor()

    def __swapToonClothes(self, dna):
        self.setStyle(dna)
        self.generateToonClothes(fromNet=1)

    def generateToonClothes(self, fromNet=0):
        swappedTorso = 0
        if self.hasLOD():
            if self.style.getGender() == 'f' and fromNet == 0:
                try:
                    bottomPair = ToonDNA.GirlBottoms[self.style.botTex]
                except:
                    bottomPair = ToonDNA.GirlBottoms[0]

                if len(self.style.torso) < 2:
                    self.sendLogSuspiciousEvent(
                        'nakedToonDNA %s was requested' % self.style.torso)
                    return 0
                elif self.style.torso[1] == 's' and bottomPair[
                        1] == ToonDNA.SKIRT:
                    self.swapToonTorso(self.style.torso[0] + 'd', genClothes=0)
                    swappedTorso = 1
                elif self.style.torso[1] == 'd' and bottomPair[
                        1] == ToonDNA.SHORTS:
                    self.swapToonTorso(self.style.torso[0] + 's', genClothes=0)
                    swappedTorso = 1
            try:
                texName = ToonDNA.Shirts[self.style.topTex]
            except:
                texName = ToonDNA.Shirts[0]

            shirtTex = loader.loadTexture(texName, okMissing=True)
            if shirtTex is None:
                self.sendLogSuspiciousEvent('failed to load texture %s' %
                                            texName)
                shirtTex = loader.loadTexture(ToonDNA.Shirts[0])
            shirtTex.setMinfilter(Texture.FTLinearMipmapLinear)
            shirtTex.setMagfilter(Texture.FTLinear)
            try:
                shirtColor = ToonDNA.ClothesColors[self.style.topTexColor]
            except:
                shirtColor = ToonDNA.ClothesColors[0]

            try:
                texName = ToonDNA.Sleeves[self.style.sleeveTex]
            except:
                texName = ToonDNA.Sleeves[0]

            sleeveTex = loader.loadTexture(texName, okMissing=True)
            if sleeveTex is None:
                self.sendLogSuspiciousEvent('failed to load texture %s' %
                                            texName)
                sleeveTex = loader.loadTexture(ToonDNA.Sleeves[0])
            sleeveTex.setMinfilter(Texture.FTLinearMipmapLinear)
            sleeveTex.setMagfilter(Texture.FTLinear)
            try:
                sleeveColor = ToonDNA.ClothesColors[self.style.sleeveTexColor]
            except:
                sleeveColor = ToonDNA.ClothesColors[0]

            if self.style.getGender() == 'm':
                try:
                    texName = ToonDNA.BoyShorts[self.style.botTex]
                except:
                    texName = ToonDNA.BoyShorts[0]

            else:
                try:
                    texName = ToonDNA.GirlBottoms[self.style.botTex][0]
                except:
                    texName = ToonDNA.GirlBottoms[0][0]

            bottomTex = loader.loadTexture(texName, okMissing=True)
            if bottomTex is None:
                self.sendLogSuspiciousEvent('failed to load texture %s' %
                                            texName)
                if self.style.getGender() == 'm':
                    bottomTex = loader.loadTexture(ToonDNA.BoyShorts[0])
                else:
                    bottomTex = loader.loadTexture(ToonDNA.GirlBottoms[0][0])
            bottomTex.setMinfilter(Texture.FTLinearMipmapLinear)
            bottomTex.setMagfilter(Texture.FTLinear)
            try:
                bottomColor = ToonDNA.ClothesColors[self.style.botTexColor]
            except:
                bottomColor = ToonDNA.ClothesColors[0]

            darkBottomColor = bottomColor * 0.5
            darkBottomColor.setW(1.0)
            for lodName in self.getLODNames():
                thisPart = self.getPart('torso', lodName)
                top = thisPart.find('**/torso-top')
                top.setTexture(shirtTex, 1)
                top.setColor(shirtColor)
                sleeves = thisPart.find('**/sleeves')
                sleeves.setTexture(sleeveTex, 1)
                sleeves.setColor(sleeveColor)
                bottoms = thisPart.findAllMatches('**/torso-bot')
                for bottomNum in xrange(0, bottoms.getNumPaths()):
                    bottom = bottoms.getPath(bottomNum)
                    bottom.setTexture(bottomTex, 1)
                    bottom.setColor(bottomColor)

                caps = thisPart.findAllMatches('**/torso-bot-cap')
                caps.setColor(darkBottomColor)

        return swappedTorso

    def getDialogueArray(self):
        animalType = self.style.getType()
        if animalType == 'dog':
            dialogueArray = DogDialogueArray
        elif animalType == 'cat':
            dialogueArray = CatDialogueArray
        elif animalType == 'horse':
            dialogueArray = HorseDialogueArray
        elif animalType == 'mouse':
            dialogueArray = MouseDialogueArray
        elif animalType == 'rabbit':
            dialogueArray = RabbitDialogueArray
        elif animalType == 'duck':
            dialogueArray = DuckDialogueArray
        elif animalType == 'monkey':
            dialogueArray = MonkeyDialogueArray
        elif animalType == 'bear':
            dialogueArray = BearDialogueArray
        elif animalType == 'pig':
            dialogueArray = PigDialogueArray
        else:
            dialogueArray = None
        return dialogueArray

    def findSomethingToLookAt(self):
        if self.randGen.random() < 0.1 or not hasattr(self, 'cr'):
            x = self.randGen.choice((-0.8, -0.5, 0, 0.5, 0.8))
            y = self.randGen.choice((-0.5, 0, 0.5, 0.8))
            self.lerpLookAt(Point3(x, 1.5, y), blink=1)
            return
        nodePathList = []
        for id, obj in self.cr.doId2do.items():
            if hasattr(obj, 'getStareAtNodeAndOffset') and obj != self:
                node, offset = obj.getStareAtNodeAndOffset()
                if node.getY(self) > 0.0:
                    nodePathList.append((node, offset))

        if nodePathList:
            nodePathList.sort(lambda x, y: cmp(x[0].getDistance(self), y[0].
                                               getDistance(self)))
            if len(nodePathList) >= 2:
                if self.randGen.random() < 0.9:
                    chosenNodePath = nodePathList[0]
                else:
                    chosenNodePath = nodePathList[1]
            else:
                chosenNodePath = nodePathList[0]
            self.lerpLookAt(chosenNodePath[0].getPos(self), blink=1)
        else:
            ToonHead.findSomethingToLookAt(self)

    def setupPickTrigger(self):
        Avatar.Avatar.setupPickTrigger(self)
        torso = self.getPart('torso', '1000')
        if torso == None:
            return 0
        self.pickTriggerNp.reparentTo(torso)
        size = self.style.getTorsoSize()
        if size == 'short':
            self.pickTriggerNp.setPosHprScale(0, 0, 0.5, 0, 0, 0, 1.5, 1.5, 2)
        elif size == 'medium':
            self.pickTriggerNp.setPosHprScale(0, 0, 0.5, 0, 0, 0, 1, 1, 2)
        else:
            self.pickTriggerNp.setPosHprScale(0, 0, 1, 0, 0, 0, 1, 1, 2)
        return 1

    def enterNeutral(self,
                     animMultiplier=1,
                     ts=0,
                     callback=None,
                     extraArgs=[]):
        anim = 'neutral'
        self.pose(anim, int(self.getNumFrames(anim) * self.randGen.random()))
        self.loop(anim, restart=0)
        self.setPlayRate(animMultiplier, anim)
        self.playingAnim = anim
        self.setActiveShadow(1)

    def exitNeutral(self):
        self.stop()

    def enterOff(self, animMultiplier=1, ts=0, callback=None):
        self.playingAnim = None
        return

    def exitOff(self):
        pass

    def __returnToLastAnim(self, task):
        if self.playingAnim:
            self.loop(self.playingAnim)
        elif self.hp > 0:
            self.loop('neutral')
        else:
            self.loop('sad-neutral')
        return Task.done

    def getPieces(self, *pieces):
        results = []
        for lodName in self.getLODNames():
            for partName, pieceNames in pieces:
                part = self.getPart(partName, lodName)
                if part:
                    if type(pieceNames) == types.StringType:
                        pieceNames = (pieceNames, )
                    for pieceName in pieceNames:
                        npc = part.findAllMatches('**/%s;+s' % pieceName)
                        for i in xrange(npc.getNumPaths()):
                            results.append(npc[i])

        return results

    def __doHeadScale(self, scale, lerpTime):
        if scale == None:
            scale = ToontownGlobals.toonHeadScales[self.style.getAnimal()]
        track = Parallel()
        for hi in xrange(self.headParts.getNumPaths()):
            head = self.headParts[hi]
            track.append(
                LerpScaleInterval(head, lerpTime, scale,
                                  blendType='easeInOut'))

        return track

    def __doLegsScale(self, scale, lerpTime):
        if scale == None:
            scale = 1
            invScale = 1
        else:
            invScale = 1.0 / scale
        track = Parallel()
        for li in xrange(self.legsParts.getNumPaths()):
            legs = self.legsParts[li]
            torso = self.torsoParts[li]
            track.append(
                LerpScaleInterval(legs, lerpTime, scale,
                                  blendType='easeInOut'))
            track.append(
                LerpScaleInterval(torso,
                                  lerpTime,
                                  invScale,
                                  blendType='easeInOut'))

        return track

    def __doToonScale(self, scale, lerpTime):
        if scale == None:
            scale = 1
        node = self.getGeomNode().getChild(0)
        track = Sequence(
            Parallel(
                LerpHprInterval(node,
                                lerpTime,
                                Vec3(0.0, 0.0, 0.0),
                                blendType='easeInOut'),
                LerpScaleInterval(node, lerpTime, scale,
                                  blendType='easeInOut')),
            Func(self.resetHeight))
        return track

    def doToonColorScale(self, scale, lerpTime, keepDefault=0):
        if keepDefault:
            self.defaultColorScale = scale
        if scale == None:
            scale = VBase4(1, 1, 1, 1)
        node = self.getGeomNode()
        caps = self.getPieces(('torso', 'torso-bot-cap'))
        track = Sequence()
        track.append(Func(node.setTransparency, 1))
        if scale[3] != 1:
            for cap in caps:
                track.append(HideInterval(cap))

        track.append(
            LerpColorScaleInterval(node,
                                   lerpTime,
                                   scale,
                                   blendType='easeInOut'))
        if scale[3] == 1:
            track.append(Func(node.clearTransparency))
            for cap in caps:
                track.append(ShowInterval(cap))

        elif scale[3] == 0:
            track.append(Func(node.clearTransparency))
        return track

    def __colorToonSkin(self, color, lerpTime):
        track = Sequence()
        colorTrack = Parallel()
        torsoPieces = self.getPieces(('torso', ('arms', 'neck')))
        legPieces = self.getPieces(('legs', ('legs', 'feet')))
        headPieces = self.getPieces(('head', '*head*'))
        if color == None:
            armColor = self.style.getArmColor()
            legColor = self.style.getLegColor()
            headColor = self.style.getHeadColor()
        else:
            armColor = color
            legColor = color
            headColor = color
        for piece in torsoPieces:
            colorTrack.append(Func(piece.setColor, *armColor))

        for piece in legPieces:
            colorTrack.append(Func(piece.setColor, *legColor))

        for piece in headPieces:
            if 'hatNode' not in str(piece) and 'glassesNode' not in str(piece):
                colorTrack.append(Func(piece.setColor, *headColor))

        track.append(colorTrack)
        return track

    def __colorToonEars(self, color, colorScale, lerpTime):
        track = Sequence()
        earPieces = self.getPieces(('head', '*ear*'))
        if len(earPieces) == 0:
            return track
        colorTrack = Parallel()
        if earPieces[0].hasColor():
            if color == None:
                headColor = self.style.getHeadColor()
            else:
                headColor = color
            for piece in earPieces:
                colorTrack.append(Func(piece.setColor, *headColor))

        else:
            if colorScale == None:
                colorScale = VBase4(1, 1, 1, 1)
            for piece in earPieces:
                colorTrack.append(Func(piece.setColorScale, *colorScale))

        track.append(colorTrack)
        return track

    def __colorScaleToonMuzzle(self, scale, lerpTime):
        track = Sequence()
        colorTrack = Parallel()
        muzzlePieces = self.getPieces(('head', '*muzzle*'))
        if scale == None:
            scale = VBase4(1, 1, 1, 1)
        for piece in muzzlePieces:
            colorTrack.append(Func(piece.setColorScale, scale))

        track.append(colorTrack)
        return track

    def __colorToonGloves(self, color, lerpTime):
        track = Sequence()
        colorTrack = Parallel()
        glovePieces = self.getPieces(('torso', '*hands*'))
        if color == None:
            for piece in glovePieces:
                colorTrack.append(Func(piece.clearColor))

        else:
            for piece in glovePieces:
                colorTrack.append(Func(piece.setColor, color))

        track.append(colorTrack)
        return track

    def restoreDefaultColorScale(self):
        node = self.getGeomNode()
        if node:
            if self.defaultColorScale:
                node.setColorScale(self.defaultColorScale)
                if self.defaultColorScale[3] != 1:
                    node.setTransparency(1)
                else:
                    node.clearTransparency()
            else:
                node.clearColorScale()
                node.clearTransparency()

    def __doToonColor(self, color, lerpTime):
        node = self.getGeomNode()
        if color == None:
            return Func(node.clearColor)
        else:
            return Func(node.setColor, color, 1)
        return

    def __doPartsColorScale(self, scale, lerpTime):
        if scale == None:
            scale = VBase4(1, 1, 1, 1)
        node = self.getGeomNode()
        pieces = self.getPieces(('torso', ('arms', 'neck')),
                                ('legs', ('legs', 'feet')),
                                ('head', '+GeomNode'))
        track = Sequence()
        track.append(Func(node.setTransparency, 1))
        for piece in pieces:
            if piece.getName(
            )[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral':
                continue
            track.append(ShowInterval(piece))

        p1 = Parallel()
        for piece in pieces:
            if piece.getName(
            )[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral':
                continue
            p1.append(
                LerpColorScaleInterval(piece,
                                       lerpTime,
                                       scale,
                                       blendType='easeInOut'))

        track.append(p1)
        if scale[3] == 1:
            track.append(Func(node.clearTransparency))
        elif scale[3] == 0:
            track.append(Func(node.clearTransparency))
            for piece in pieces:
                if piece.getName(
                )[:7] == 'muzzle-' and piece.getName()[-8:] != '-neutral':
                    continue
                track.append(HideInterval(piece))

        self.generateHat()
        self.generateGlasses()
        return track

    def putOnSuit(self, suitType, setDisplayName=True, rental=False):
        if self.isDisguised:
            self.takeOffSuit()
        from toontown.suit import Suit
        deptIndex = suitType
        suit = Suit.Suit()
        dna = SuitDNA.SuitDNA()
        if rental == True:
            if SuitDNA.suitDepts[deptIndex] == 's':
                suitType = 'cc'
            elif SuitDNA.suitDepts[deptIndex] == 'm':
                suitType = 'sc'
            elif SuitDNA.suitDepts[deptIndex] == 'l':
                suitType = 'bf'
            elif SuitDNA.suitDepts[deptIndex] == 'c':
                suitType = 'f'
            else:
                self.notify.warning(
                    'Suspicious: Incorrect rental suit department requested')
                suitType = 'cc'
        dna.newSuit(suitType)
        suit.setStyle(dna)
        suit.isDisguised = 1
        suit.generateSuit()
        suit.initializeDropShadow()
        suit.setPos(self.getPos())
        suit.setHpr(self.getHpr())
        for part in suit.getHeadParts():
            part.hide()

        suitHeadNull = suit.find('**/joint_head')
        toonHead = self.getPart('head', '1000')
        Emote.globalEmote.disableAll(self)
        toonGeom = self.getGeomNode()
        toonGeom.hide()
        worldScale = toonHead.getScale(render)
        self.headOrigScale = toonHead.getScale()
        headPosNode = hidden.attachNewNode('headPos')
        toonHead.reparentTo(headPosNode)
        toonHead.setPos(0, 0, 0.2)
        headPosNode.reparentTo(suitHeadNull)
        headPosNode.setScale(render, worldScale)
        suitGeom = suit.getGeomNode()
        suitGeom.reparentTo(self)
        if rental == True:
            suit.makeRentalSuit(SuitDNA.suitDepts[deptIndex])
        self.suit = suit
        self.suitGeom = suitGeom
        self.setHeight(suit.getHeight())
        self.nametag3d.setPos(0, 0, self.height + 1.3)
        self.suit.loop('neutral')
        self.isDisguised = 1
        self.setFont(ToontownGlobals.getSuitFont())
        self.nametag.setSpeechFont(ToontownGlobals.getSuitFont())
        if setDisplayName:
            if hasattr(base, 'idTags') and base.idTags:
                name = self.getAvIdName()
            else:
                name = self.getName()
            suitDept = SuitDNA.suitDepts.index(SuitDNA.getSuitDept(suitType))
            suitName = SuitBattleGlobals.SuitAttributes[suitType]['name']
            self.nametag.setDisplayName(
                TTLocalizer.SuitBaseNameWithLevel % {
                    'name': name,
                    'dept': suitName,
                    'level': self.cogLevels[suitDept] + 1
                })
            self.nametag.setWordwrap(9.0)

    def takeOffSuit(self):
        if not self.isDisguised:
            return
        suitType = self.suit.style.name
        toonHeadNull = self.find('**/1000/**/def_head')
        if not toonHeadNull:
            toonHeadNull = self.find('**/1000/**/joint_head')
        toonHead = self.getPart('head', '1000')
        toonHead.reparentTo(toonHeadNull)
        toonHead.setScale(self.headOrigScale)
        toonHead.setPos(0, 0, 0)
        headPosNode = self.suitGeom.find('**/headPos')
        headPosNode.removeNode()
        self.suitGeom.reparentTo(self.suit)
        self.resetHeight()
        self.nametag3d.setPos(0, 0, self.height + 0.5)
        toonGeom = self.getGeomNode()
        toonGeom.show()
        Emote.globalEmote.releaseAll(self)
        self.isDisguised = 0
        self.setFont(ToontownGlobals.getToonFont())
        self.nametag.setSpeechFont(ToontownGlobals.getToonFont())
        self.nametag.setWordwrap(None)
        if hasattr(base, 'idTags') and base.idTags:
            name = self.getAvIdName()
        else:
            name = self.getName()
        self.setDisplayName(name)
        self.suit.delete()
        del self.suit
        del self.suitGeom

    def makeWaiter(self):
        if not self.isDisguised:
            return
        self.suit.makeWaiter(self.suitGeom)
예제 #12
0
class CrashGUIItem(DirectFrame):
    notify = directNotify.newCategory('CrashGUIItem')
    
    DownZ = -0.25
    UpZ = 0.5
    
    def __init__(self, visItem, visScale, frameX, visX, textX, textZ, textScale, spins = False, font = CIGlobals.getToonFont(),
                 align = TextNode.ACenter):
        DirectFrame.__init__(self, parent = base.a2dTopCenter, pos = (frameX, 0, self.UpZ))
        
        self.frameX = frameX
        
        self.item = visItem
        self.item.flattenLight()
        self.item.setDepthWrite(1)
        self.item.setDepthTest(1)
        self.item.setScale(visScale)
        self.item.setX(visX)
        self.item.reparentTo(self)
        
        self.spinIval = None
        if spins:
            self.spinIval = LerpHprInterval(
                self.item,
                duration = 0.5,
                hpr = Vec3(360, 0, 0),
                startHpr = Vec3(0, 0, 0)
            )
            self.spinIval.loop()
        
        self.text = OnscreenText(parent = self, pos = (textX, textZ, 0),
            scale = textScale, shadow = (0, 0, 0, 1), fg = (0.5, 0.5, 1.0, 1.0), font = font, align = align)
            
        self.fsm = ClassicFSM(
            "ItemFSM",
            [
                State('off', self.enterOff, self.exitOff),
                State('up', self.enterUp, self.exitUp),
                State('up2down', self.enterUp2Down, self.exitUp2Down),
                State('down', self.enterDown, self.exitDown),
                State('down2up', self.enterDown2Up, self.exitDown2Up)
            ],
            "up",
            "off"
        ); self.fsm.enterInitialState()
        
        self.initialiseoptions(CrashGUIItem)
        
    def taskName(self, name):
        return name + "-" + str(id(self))
        
    def enterOff(self):
        pass
        
    def exitOff(self):
        pass
        
    def enterUp(self):
        self.setZ(self.UpZ)
        
    def exitUp(self):
        pass
        
    def enterUp2Down(self):
        self.ival = LerpPosInterval(
            self,
            duration = 0.5,
            pos = (self.frameX, 0, self.DownZ),
            startPos = (self.frameX, 0, self.UpZ),
            name = self.taskName('up2downival'))
        self.ival.setDoneEvent(self.ival.getName())
        self.acceptOnce(self.ival.getDoneEvent(), self.__up2DownDone)
        self.ival.start()
    
    def __up2DownDone(self):
        self.fsm.request('down')
        
    def exitUp2Down(self):
        self.ignore(self.ival.getDoneEvent())
        self.ival.finish()
        del self.ival
        
    def enterDown(self):
        self.setZ(self.DownZ)
        downTime = 3.0
        taskMgr.doMethodLater(downTime, self.__downTask, self.taskName('downTask'))
        
    def __downTask(self, task):
        self.fsm.request('down2up')
        return task.done
        
    def exitDown(self):
        taskMgr.remove(self.taskName('downTask'))
        
    def enterDown2Up(self):
        self.ival = LerpPosInterval(
            self,
            duration = 0.5,
            pos = (self.frameX, 0, self.UpZ),
            startPos = (self.frameX, 0, self.DownZ),
            name = self.taskName('down2upival'))
        self.ival.setDoneEvent(self.ival.getName())
        self.acceptOnce(self.ival.getDoneEvent(), self.__down2UpDone)
        self.ival.start()
    
    def __down2UpDone(self):
        self.fsm.request('up')
        
    def exitDown2Up(self):
        self.ignore(self.ival.getDoneEvent())
        self.ival.finish()
        del self.ival
        
    def setTextText(self, text):
        self.text.setText(text)
        
    def show(self):
        if self.fsm.getCurrentState().getName() in ['up', 'down2up']:
            self.fsm.request('up2down')
        elif self.fsm.getCurrentState().getName() == 'down':
            taskMgr.remove(self.taskName('downTask'))
            self.enterDown()
예제 #13
0
파일: Char.py 프로젝트: coginvasion/src
class Char(Avatar.Avatar):

    def __init__(self):
        try:
            self.Char_initialized
            return
        except:
            self.Char_initialized = 1

        Avatar.Avatar.__init__(self)
        self.avatarType = CIGlobals.CChar
        self.avatarName = None
        self.currentAnim = None
        self.charType = ''
        self.eyes = loader.loadTexture('phase_3/maps/eyes1.jpg', 'phase_3/maps/eyes1_a.rgb')
        self.closedEyes = loader.loadTexture('phase_3/maps/mickey_eyes_closed.jpg', 'phase_3/maps/mickey_eyes_closed_a.rgb')
        self.animFSM = ClassicFSM('Char', [State('off', self.enterOff, self.exitOff),
         State('neutral', self.enterNeutral, self.exitNeutral),
         State('walk', self.enterWalk, self.exitWalk),
         State('run', self.enterRun, self.exitRun)], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()
        Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 3.5, 1)
        return

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                notify.warning('animFSM in flux, state=%s, not requesting off' % self.animFSM.getCurrentState().getName())
        else:
            notify.warning('animFSM has been deleted')

    def disable(self):
        self.stopBlink()
        self.stopAnimations()
        Avatar.Avatar.disable(self)

    def delete(self):
        try:
            self.Char_deleted
        except:
            self.Char_deleted = 1
            del self.animFSM
            Avatar.Avatar.delete(self)

    def setChat(self, chatString):
        if self.charType == CIGlobals.Mickey:
            self.dial = base.audio3d.loadSfx('phase_3/audio/dial/mickey.wav')
        elif self.charType == CIGlobals.Minnie:
            self.dial = base.audio3d.loadSfx('phase_3/audio/dial/minnie.wav')
        elif self.charType == CIGlobals.Goofy:
            self.dial = base.audio3d.loadSfx('phase_6/audio/dial/goofy.wav')
        base.audio3d.attachSoundToObject(self.dial, self)
        self.dial.play()
        Avatar.Avatar.setChat(self, chatString)

    def setName(self, nameString, charName = None):
        self.avatarName = nameString
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType, charName=charName)

    def generateChar(self, charType):
        self.charType = charType
        if charType == CIGlobals.Mickey or charType == CIGlobals.Minnie:
            self.loadModel('phase_3/models/char/' + charType.lower() + '-' + str(CIGlobals.ModelDetail(self.avatarType)) + '.bam')
            self.loadAnims({'neutral': 'phase_3/models/char/' + charType.lower() + '-wait.bam',
             'walk': 'phase_3/models/char/' + charType.lower() + '-walk.bam',
             'run': 'phase_3/models/char/' + charType.lower() + '-run.bam',
             'left-start': 'phase_3.5/models/char/' + charType.lower() + '-left-start.bam',
             'left': 'phase_3.5/models/char/' + charType.lower() + '-left.bam',
             'right-start': 'phase_3.5/models/char/' + charType.lower() + '-right-start.bam',
             'right': 'phase_3.5/models/char/' + charType.lower() + '-right.bam'})
            if charType == CIGlobals.Mickey:
                self.mickeyEye = self.controlJoint(None, 'modelRoot', 'joint_pupilR')
                self.mickeyEye.setY(0.025)
            for bundle in self.getPartBundleDict().values():
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                earNull.clearNetTransforms()

            for bundle in self.getPartBundleDict().values():
                charNodepath = bundle['modelRoot'].partBundleNP
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                ears = charNodepath.find('**/sphere3')
                if ears.isEmpty():
                    ears = charNodepath.find('**/*sphere3')
                ears.clearEffect(CharacterJointEffect.getClassType())
                earRoot = charNodepath.attachNewNode('earRoot')
                earPitch = earRoot.attachNewNode('earPitch')
                earPitch.setP(40.0)
                ears.reparentTo(earPitch)
                earNull.addNetTransform(earRoot.node())
                ears.clearMat()
                ears.node().setPreserveTransform(ModelNode.PTNone)
                ears.setP(-40.0)
                ears.flattenMedium()
                ears.setBillboardAxis()
                self.startBlink()

        elif charType == CIGlobals.Pluto:
            self.loadModel('phase_6/models/char/pluto-1000.bam')
            self.loadAnims({'walk': 'phase_6/models/char/pluto-walk.bam',
             'neutral': 'phase_6/models/char/pluto-neutral.bam',
             'sit': 'phase_6/models/char/pluto-sit.bam',
             'stand': 'phase_6/models/char/pluto-stand.bam'})
        elif charType == CIGlobals.Goofy:
            self.loadModel('phase_6/models/char/TT_G-1500.bam')
            self.loadAnims({'neutral': 'phase_6/models/char/TT_GWait.bam',
             'walk': 'phase_6/models/char/TT_GWalk.bam'})
        else:
            raise StandardError('unknown char %s!' % charType)
        Avatar.Avatar.initShadow(self)
        return

    def initializeLocalCollisions(self, name, radius):
        Avatar.Avatar.initializeLocalCollisions(self, radius, 2, name)

    def startBlink(self):
        randomStart = random.uniform(0.5, 5)
        taskMgr.add(self.blinkTask, 'blinkTask')

    def stopBlink(self):
        taskMgr.remove('blinkTask')
        taskMgr.remove('doBlink')
        taskMgr.remove('openEyes')

    def blinkTask(self, task):
        taskMgr.add(self.doBlink, 'doBlink')
        delay = random.uniform(0.5, 7)
        task.delayTime = delay
        return task.again

    def doBlink(self, task):
        self.closeEyes()
        taskMgr.doMethodLater(0.2, self.openEyes, 'openEyes')
        return task.done

    def closeEyes(self):
        self.find('**/joint_pupilR').hide()
        self.find('**/joint_pupilL').hide()
        if self.charType == CIGlobals.Mickey:
            self.mickeyEye.setY(-0.025)
            self.mickeyEye.hide()
        self.find('**/eyes').setTexture(self.closedEyes, 1)

    def openEyes(self, task):
        self.find('**/joint_pupilR').show()
        self.find('**/joint_pupilL').show()
        if self.charType == CIGlobals.Mickey:
            self.mickeyEye.setY(0.025)
            self.mickeyEye.show()
        self.find('**/eyes').setTexture(self.eyes, 1)
        return task.done

    def enterOff(self):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterNeutral(self):
        self.loop('neutral')

    def exitNeutral(self):
        self.stop()

    def enterWalk(self):
        self.loop('walk')

    def exitWalk(self):
        self.stop()

    def enterRun(self):
        self.loop('run')

    def exitRun(self):
        self.stop()
예제 #14
0
class CogBrain(DirectObject):
    PANIC_SPEED = 0.15
    PANIC_DELAY = 0.5
    RUNAWAY_SPEED = 0.1
    RUNAWAY_SAFE_DISTANCE = 50
    MAX_BOSS_HELPERS = 5
    PANIC_HP_FACTOR = 0.222
    ATTACK_DISTANCE = 40.0
    MAX_ATTACKERS = 3
    Difficulty2MaxAttackThrows = {}
    for level in range(1, 5):
        Difficulty2MaxAttackThrows[level] = 3

    for level in range(5, 10):
        Difficulty2MaxAttackThrows[level] = 4

    for level in range(9, 13):
        Difficulty2MaxAttackThrows[level] = 5

    def __init__(self, suit):
        self.suit = suit
        self.panicHP = self.suit.getMaxHealth() * self.PANIC_HP_FACTOR
        self.fsm = ClassicFSM('CogBrain', [State('off', self.enterOff, self.exitOff),
         State('neutral', self.enterNeutral, self.exitNeutral),
         State('followBoss', self.enterFollowBoss, self.exitFollowBoss),
         State('panic', self.enterPanic, self.exitPanic),
         State('runAway', self.enterRunAway, self.exitRunAway)], 'off', 'off')
        self.fsm.enterInitialState()

    def start(self):
        taskMgr.add(self.__think, self.suit.uniqueName('think'))

    def end(self, andGoOff = 1):
        taskMgr.remove(self.suit.uniqueName('think'))
        if andGoOff:
            self.fsm.request('off')

    def __think(self, task = None):
        if task:
            task.delayTime = 1
        if self.suit.getAttacking():
            if task:
                return task.again
            else:
                return
        _help_priority = 0
        _panic_priority = 0
        _run_priority = 0
        _helper_suits = 0
        boss = None
        for av in self.suit.getManager().suits.values():
            if av.doId != self.suit.doId:
                if av.head in ('vp',):
                    boss = av
                    for suit in self.suit.getManager().suits.values():
                        if suit.doId != self.suit.doId:
                            if suit.brain:
                                if suit.brain.fsm.getCurrentState().getName() == 'followBoss':
                                    _helper_suits += 1

        if _helper_suits == self.MAX_BOSS_HELPERS - 1:
            _help_priority = 2
        elif _helper_suits == self.MAX_BOSS_HELPERS - 2:
            _help_priority = 2.5
        elif _helper_suits == self.MAX_BOSS_HELPERS - 3:
            _help_priority = 3.5
        elif _helper_suits == self.MAX_BOSS_HELPERS - 4:
            _help_priority = 4
        elif _helper_suits == self.MAX_BOSS_HELPERS - 5:
            _help_priority = 4.5
        if boss == None or _helper_suits == self.MAX_BOSS_HELPERS:
            _help_priority = 0
        if self.fsm.getCurrentState().getName() == 'followBoss':
            if self.bossSpotKey != boss.boss.spot:
                self.fsm.request('followBoss', [boss])
                return task.again
        _toons_in_range = 0
        in_range = 15
        for av in self.suit.air.doId2do.values():
            if av.__class__.__name__ == 'DistributedToonAI':
                if av.zoneId == self.suit.zoneId:
                    if self.suit.getDistance(av) <= in_range:
                        _toons_in_range += 1

        if self.fsm.getCurrentState().getName() == 'followBoss':
            _panic_priority = _toons_in_range / 0.85
        else:
            _panic_priority = _toons_in_range / 0.75
        if self.fsm.getCurrentState().getName() == 'panic' and _toons_in_range > 0:
            _run_priority = 5
        if self.suit.getHealth() <= self.panicHP:
            if _panic_priority < 4:
                _panic_priority = 4
        if _run_priority == 5:
            self.fsm.request('runAway', [av])
            del _help_priority
            del _panic_priority
            del _run_priority
            del _helper_suits
            del boss
            del in_range
            del _toons_in_range
            try:
                del av
            except:
                pass

            if task:
                return task.done
            else:
                return
        elif _panic_priority <= 2 and _help_priority <= 2:
            state_num = random.randint(0, 2)
            if state_num == 0 or state_num == 1:
                new_state = 'neutral'
            else:
                new_state = 'followBoss'
            if boss == None or _help_priority == 0:
                if self.fsm.getCurrentState().getName() != 'neutral':
                    self.fsm.request('neutral')
            else:
                new_state = 'neutral'
                if self.fsm.getCurrentState().getName() != new_state:
                    if self.fsm.getCurrentState().getName() == 'followBoss':
                        del _help_priority
                        del _panic_priority
                        del _run_priority
                        del _helper_suits
                        del boss
                        del in_range
                        del _toons_in_range
                        try:
                            del args
                        except:
                            pass

                        try:
                            del new_state
                        except:
                            pass

                        try:
                            del state_num
                        except:
                            pass

                        try:
                            del av
                        except:
                            pass

                        if task:
                            return task.again
                        else:
                            return
                    args = []
                    if new_state == 'followBoss':
                        args = [boss]
                    self.fsm.request(new_state, args)
        elif _panic_priority > _help_priority:
            if self.fsm.getCurrentState().getName() != 'panic':
                self.fsm.request('panic')
                del _help_priority
                del _panic_priority
                del _run_priority
                del _helper_suits
                del boss
                del in_range
                del _toons_in_range
                try:
                    del args
                except:
                    pass

                try:
                    del new_state
                except:
                    pass

                try:
                    del state_num
                except:
                    pass

                try:
                    del av
                except:
                    pass

                if task:
                    return task.done
                else:
                    return
        elif _panic_priority < _help_priority:
            if self.fsm.getCurrentState().getName() != 'followBoss':
                self.fsm.request('followBoss', [boss])
        elif _panic_priority == _help_priority:
            new_state = random.choice(['panic', 'followBoss'])
            if self.fsm.getCurrentState().getName() != new_state:
                args = []
                if new_state == 'followBoss':
                    args = [boss]
                self.fsm.request(new_state, args)
        del _help_priority
        del _panic_priority
        del _run_priority
        del _helper_suits
        del boss
        del in_range
        del _toons_in_range
        try:
            del args
        except:
            pass

        try:
            del new_state
        except:
            pass

        try:
            del state_num
        except:
            pass

        try:
            del av
        except:
            pass

        if task:
            return task.again
        else:
            return

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterNeutral(self):
        self.suit.createPath(fromCurPos=True)
        self.numAttacksThrown = 0
        if not self.suit.isBackup():
            self.neutral_startLookingForToons()

    def neutral_startLookingForToons(self):
        taskMgr.add(self.__lookForToons, self.suit.uniqueName('lookForToon'))

    def neutral_stopLookingForToons(self):
        taskMgr.remove(self.suit.uniqueName('lookForToon'))

    def __lookForToons(self, task):
        if self.suit.isBackup() or not hasattr(self, 'numAttacksThrown'):
            return task.done
        elif self.suit.getAttacking():
            task.delayTime = 1.0
            return task.again
        elif self.numAttacksThrown >= self.Difficulty2MaxAttackThrows[self.suit.getLevel()]:
            self.numAttacksThrown = 0
            if not self.suit.isWalking():
                self.suit.createPath(path_key=self.suit.currentPath, fromCurPos=True)
            task.delayTime = 10
            return task.again
        else:
            closestToonOrTurret = None
            obj2range = {}
            for obj in base.air.doId2do.values():
                if obj.__class__.__name__ in ('DistributedToonAI', 'DistributedPieTurretAI'):
                    if obj.zoneId == self.suit.zoneId:
                        if not obj.isDead():
                            if obj.__class__.__name__ == 'DistributedToonAI':
                                if obj.getNumAttackers() < self.MAX_ATTACKERS:
                                    dist = obj.getDistance(self.suit)
                                    if dist <= self.ATTACK_DISTANCE:
                                        obj2range[obj] = dist
                            else:
                                dist = obj.getDistance(self.suit)
                                if dist <= self.ATTACK_DISTANCE:
                                    obj2range[obj] = dist

            ranges = []
            for distance in obj2range.values():
                ranges.append(distance)

            ranges.sort()
            for obj in obj2range.keys():
                distance = obj2range[obj]
                if distance == ranges[0]:
                    closestToonOrTurret = obj

            if closestToonOrTurret != None and not self.suit.getAttacking():
                if self.suit.head != 'vp':
                    if self.suit.walkTrack:
                        self.ignore(self.suit.walkTrack.getName())
                        self.suit.walkTrack.clearToInitial()
                        self.suit.walkTrack = None
                self.suit.b_setSuitState(3, -1, -1)
                self.suit.b_setAnimState('neutral')
                self.end(0)
                self.suit.headsUp(closestToonOrTurret)
                self.suit.attackToon(closestToonOrTurret)
                self.suit.setAttacking(True)
                if closestToonOrTurret.__class__.__name__ == 'DistributedToonAI':
                    closestToonOrTurret.addNewAttacker(self.suit.doId)
                self.numAttacksThrown += 1
                return task.done
            if self.numAttacksThrown > 0:
                if not self.suit.isWalking():
                    self.suit.createPath(path_key=self.suit.currentPath, fromCurPos=True)
            elif not self.suit.isWalking():
                self.suit.createPath(fromCurPos=True)
            self.numAttacksThrown = 0
            task.delayTime = 3.5
            return task.again

    def exitNeutral(self):
        self.neutral_stopLookingForToons()
        del self.numAttacksThrown

    def enterPanic(self):
        taskMgr.add(self.__panic, self.suit.uniqueName('panic'))

    def __panic(self, task):
        self.suit.createPath(durationFactor=self.PANIC_SPEED, fromCurPos=True)
        if task.time == 2.0:
            self.__think(None)
        task.delayTime = self.PANIC_DELAY
        return task.again

    def exitPanic(self):
        taskMgr.remove(self.suit.uniqueName('panic'))

    def enterFollowBoss(self, boss):
        self.boss = boss
        if boss.boss.spot == None:
            self.bossSpot = boss.getPos(render)
        else:
            self.bossSpot = CIGlobals.SuitSpawnPoints[self.suit.hood][boss.boss.spot]
        if self.suit.currentPath == boss.boss.spot:
            self.suit.createPath(path_key=boss.boss.spot, fromCurPos=True)
        else:
            self.suit.currentPathQueue = SuitPathFinder.find_path(self.suit.hood, self.suit.currentPath, boss.boss.spot)
            self.suit.currentPathQueue.remove(self.suit.currentPathQueue[0])
            self.suit.createPath(fromCurPos=True)
        taskMgr.add(self.__followBoss, self.suit.uniqueName('followBoss'))
        self.bossSpotKey = boss.boss.spot
        return

    def __followBoss(self, task):
        if self.boss not in self.suit.getManager().suits.values():
            self.fsm.request('neutral')
            return task.done
        elif self.suit.getDistance(self.boss) <= 4:
            self.suit.b_setSuitState(3, -1, -1)
            if self.suit.walkTrack:
                self.suit.ignore(self.suit.walkTrack.getDoneEvent())
                self.suit.walkTrack.pause()
                self.suit.walkTrack = None
                self.suit.b_setAnimState('neutral')
                self.suit.setH(self.suit.getH() - 180)
                self.suit.d_setH(self.suit.getH())
            return task.done
        else:
            return task.cont

    def exitFollowBoss(self):
        self.suit.resetPathQueue()
        taskMgr.remove(self.suit.uniqueName('followBoss'))
        del self.boss
        del self.bossSpot
        del self.bossSpotKey

    def enterRunAway(self, toon):
        self.toon = toon
        self.suit.createPath(durationFactor=self.RUNAWAY_SPEED, fromCurPos=True)
        taskMgr.add(self.__runAway, self.suit.uniqueName('runAway'))

    def __runAway(self, task):
        try:
            if self.suit.getDistance(self.toon) >= self.RUNAWAY_SAFE_DISTANCE:
                self.start()
                return task.done
            if self.suit.walkTrack == None or self.suit.walkTrack.isStopped():
                self.suit.createPath(durationFactor=self.RUNAWAY_SPEED, fromCurPos=True)
            return task.cont
        except:
            self.fsm.request('neutral')
            return task.done

        return

    def exitRunAway(self):
        taskMgr.remove(self.suit.uniqueName('runAway'))
        del self.toon
class Human(HumanBase.HumanBase, Biped.Biped):
    notify = DirectNotifyGlobal.directNotify.newCategory("Human")
    prebuiltAnimData = {}

    def __init__(self, other=None):
        Biped.Biped.__init__(self, other, HumanAnimationMixer)
        self.zombie = False
        self.crazyColorSkin = False
        self.crazyColorSkinIndex = 0
        self.flattenPending = None
        self.flattenSuperLowName = None
        self.optimizeLOD = base.config.GetBool("optimize-avatar-lod", 1)
        self.loaded = 0
        self.playingRate = None
        self.shadowFileName = "models/misc/drop_shadow"
        self.setFont(PiratesGlobals.getInterfaceFont())
        self._Human__blinkName = "blink-" + str(self.this)
        self.eyeLids = None
        self.eyeBalls = None
        self.eyeIris = None
        self.reducedAnimList = None
        self.headNode = None
        self.headEffects = NodePath("headEffects")
        self.extraNode = None
        self.scaleNode = None
        self.rootNode = None
        self.floorOffsetZ = 0.0
        self.isGhost = 0
        self.headFudgeHpr = Vec3(0, 0, 0)
        self.randGen = random.Random()
        self.randGen.seed(random.random())
        self.eyeFSM = ClassicFSM(
            "eyeFSM",
            [
                State("off", self.enterEyeFSMOff, self.exitEyeFSMOff, ["open", "closed"]),
                State("open", self.enterEyeFSMOpen, self.exitEyeFSMOpen, ["closed", "off"]),
                State("closed", self.enterEyeFSMClosed, self.exitEyeFSMClosed, ["open", "off"]),
            ],
            "off",
            "off",
        )
        self.eyeFSM.enterInitialState()
        if other != None:
            self.copyHuman(other)

    def removeCopiedNodes(self):
        self.dropShadow = self.find("**/drop_shadow*")
        if not self.dropShadow.isEmpty():
            self.deleteDropShadow()
        else:
            self.dropShadow = None
        billboardNode = self.find("**/billboardNode")
        if not billboardNode.isEmpty():
            billboardNode.removeNode()

        self.getGeomNode().getParent().removeNode()

    def flattenHuman(self):
        self.getWeaponJoints()

    def flattenSuperLow(self):
        name = "flattenSuperLow-%s" % self.this
        self.flattenSuperLowName = name
        model = self.getLOD("500")
        self.accept(name, self._Human__doneFlattenSuperLow)
        taskMgr.remove(name)
        taskMgr.add(self.flattenSuperLowTask, name, extraArgs=[model], taskChain="background")

    def flattenSuperLowTask(self, model):
        model = model.copyTo(NodePath())
        rhn = model.find("**/rightHand")
        lhn = model.find("**/leftHand")
        if lhn:
            lhn.detachNode()

        if rhn:
            rhn.detachNode()

        node = model.node()
        gr = SceneGraphReducer()
        model.node().setAttrib(TransparencyAttrib.make(0), 2000)
        gr.applyAttribs(
            node,
            SceneGraphReducer.TTApplyTextureColor
            | SceneGraphReducer.TTTexMatrix
            | SceneGraphReducer.TTOther
            | SceneGraphReducer.TTCullFace
            | SceneGraphReducer.TTTransform
            | SceneGraphReducer.TTColor
            | SceneGraphReducer.TTColorScale,
        )
        num_removed = gr.flatten(node, -1)
        gr.makeCompatibleState(node)
        gr.collectVertexData(
            node, ~(SceneGraphReducer.CVDFormat | SceneGraphReducer.CVDName | SceneGraphReducer.CVDAnimationType)
        )
        gr.unify(node, 0)
        name = self.flattenSuperLowName
        if name:
            messenger.send(name, [model], taskChain="default")

    def _Human__doneFlattenSuperLow(self, flat):
        self.headNode = flat.find("**/def_head01")
        self.rootNode = flat.find("**/dx_root")
        self.getWeaponJoints()
        orig = self.getLOD("500")
        orig.getChildren().detach()
        self.loadModel(flat, lodName="500", copy=False, autoBindAnims=False)
        self.getWeaponJoints()
        if hasattr(self, "animProp") and self.animProp:
            self.resetAnimProp()

        self.findAllMatches("**/def_head01").detach()
        self.findAllMatches("**/dx_root").detach()
        for lodName in self.getLODNames():
            if lodName == "500":
                self.headNode.reparentTo(self.getLOD(lodName).find("**/+Character"))
                self.rootNode.reparentTo(self.getLOD(lodName).find("**/+Character"))
                continue
            self.headNode.instanceTo(self.getLOD(lodName).find("**/+Character"))
            self.rootNode.instanceTo(self.getLOD(lodName).find("**/+Character"))

        self.headEffects.reparentTo(self.headNode)

    def _Human__doneFlattenHuman(self, models):
        self.flattenPending = None
        self.getWeaponJoints()

    def copyHuman(self, other):
        self.gender = other.gender
        self.loaded = other.loaded
        self.loadAnimatedHead = other.loadAnimatedHead
        self.rootScale = other.rootScale

    def delete(self):

        try:
            pass
        except:
            self.Human_deleted = 1
            taskMgr.remove(self._Human__blinkName)
            name = self.flattenSuperLowName
            if name:
                self.flattenSuperLowName = None
                self.ignore(name)
                taskMgr.remove(name)

            if self.dropShadow and not self.dropShadow.isEmpty():
                self.deleteDropShadow()

            del self.eyeFSM
            self.controlShapes = None
            self.sliderNames = None
            Biped.Biped.delete(self)

    def isDeleted(self):

        try:
            if self.Human_deleted == 1:
                return True
        except:
            return False

    def fixEyes(self):
        self.eyeLids = {}
        self.eyeBalls = {}
        self.eyeIris = {}
        for lodName in self.getLODNames():
            geom = self.getPart("head", lodName)
            self.eyeLids[lodName] = geom.findAllMatches("**/*eyelid*")
            self.eyeBalls[lodName] = geom.findAllMatches("**/eye_ball*")
            self.eyeIris[lodName] = geom.findAllMatches("**/eye_iris*")
            self.eyeLids[lodName].stash()
            self.eyeBalls[lodName].unstash()
            self.eyeIris[lodName].unstash()

    def makeAnimDict(self, gender, animNames):
        self.animTable = []
        for currAnim in animNames:
            anim = animNames.get(currAnim)
            for currAnimName in anim:
                self.animTable.append([currAnimName, currAnimName])

        self.reducedAnimList = self.animTable

    def forceLoadAnimDict(self):
        for anim in self.animDict.keys():
            self.getAnimControls(anim)

    def createAnimDict(self, customList=None):
        filePrefix = "models/char/m"
        genderPrefix = "m"
        if self.style.gender == "f":
            self.type = BodyDefs.femaleFrames[self.style.getBodyShape()]
            filePrefix = "models/char/f"
            genderPrefix = "f"
        else:
            self.type = BodyDefs.maleFrames[self.style.getBodyShape()]
        if self.reducedAnimList is None:
            self.animDict = self.prebuiltAnimData[genderPrefix + self.type]
            return None

        filePrefix += "p"
        animList = self.reducedAnimList
        self.animDict = {}
        for anim in animList:
            animSuffix = ""
            for i in range(0, len(CustomAnimDict[genderPrefix + self.type])):
                if anim[0] == CustomAnimDict[genderPrefix + self.type][i]:
                    animSuffix = "_" + genderPrefix + NewModelDict.get(self.type)
                    break
                    continue

            self.animDict[anim[0]] = filePrefix + "_" + anim[1] + animSuffix

        return filePrefix

    def getIsPaid(self):
        return True

    def setupGhostNodes(self):
        lod = NodePath(self.getLODNode())
        for node in lod.getChildren():
            eyes = node.findAllMatches("**/eye*")
            if eyes:
                eyes.wrtReparentTo(eyes[0].getParent().attachNewNode(ModelNode("eyes")))
                continue

    def loadHuman(self, other):
        other.style = self.style
        other.gender = self.style.gender
        other.model.dna = self.style
        self.createAnimDict()
        if self.style.gender == "f":
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 1
        else:
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 0
        other.zombie = self.zombie
        other.crazyColorSkin = self.crazyColorSkin
        other.setCrazyColorSkinIndex(self.getCrazyColorSkinIndex())
        yieldThread("anim dict")
        other.isPaid = self.getIsPaid()
        other.showLOD(2000)
        yieldThread("showLOD")
        base.loadingScreen.tick()
        if other.zombie:
            other.showZombie()

        if hasattr(self, "motionFSM"):
            self.motionFSM.setAvatar(self)

        base.loadingScreen.tick()
        yieldThread("zombie")
        other.applyBodyShaper()
        base.loadingScreen.tick()
        yieldThread("body shaper")
        base.loadingScreen.tick()
        other.applyHeadShaper()
        yieldThread("head shaper")
        base.loadingScreen.tick()
        if self.zombie == 2:
            other.model.eyeBalls.unstash()
            other.model.irises.stash()
        elif self.zombie:
            other.model.eyeBalls.stash()
            other.model.irises.stash()
        else:
            other.model.eyeBalls.unstash()
            other.model.irises.unstash()
        base.loadingScreen.tick()
        self.copyActor(other)
        base.loadingScreen.tick()
        self.floorOffsetZ = other.rootNode.getZ()
        yieldThread("copyActor")
        self.copyHuman(other)
        if self.isGhost:
            self.setupGhostNodes()

        gnodes = self.getLOD("500").findAllMatches("**/+GeomNode")
        for node in gnodes:
            node.setTextureOff(other.model.tattooStage)

        base.loadingScreen.tick()
        self.flattenSuperLow()
        self.rootNode = self.getLOD("500").find("**/dx_root")
        self.headNode = self.getLOD("500").find("**/def_head01")
        lodNames = self.getLODNames()
        self.scaleNode = self.controlJoint(None, "legs", "def_scale_jt", lodNames[0])
        if len(lodNames) > 1:
            for i in range(1, len(lodNames)):
                self.controlJoint(self.scaleNode, "legs", "def_scale_jt", lodNames[i])

        self.setGlobalScale(self.calcBodyScale())
        yieldThread("copyHuman")
        base.loadingScreen.tick()
        self.loadAnimsOnAllLODs(self.animDict, "modelRoot")
        base.loadingScreen.tick()
        yieldThread("loadAnims")
        other.zombie = 0
        other.crazyColorSkin = 0
        other.setCrazyColorSkinIndex(0)
        other.showNormal()
        yieldThread("show normal")
        self.initializeNametag3d()
        self.initializeDropShadow()
        self.setName(self.getName())
        yieldThread("misc nodes")
        base.loadingScreen.tick()
        self.loaded = 1

    def setGlobalScale(self, scale):
        self.scaleNode.setScale(scale)
        self.rootScale = scale
        self.scaleNode.setZ(-(self.floorOffsetZ * (1 - scale)))

    def initializeMiscNodes(self):
        self.initializeNametag3d()
        self.initializeDropShadow()

    def undoControlJoints(self):
        self.getGeomNode().getParent().findAllMatches("def_*").detach()
        self.getGeomNode().getParent().findAllMatches("trs_*").detach()
        self.findAllMatches("def_*").detach()
        self.findAllMatches("trs_*").detach()

    def cleanupHuman(self, gender="m"):
        self.eyeFSM.request("off")
        self.undoControlJoints()
        self.removeCopiedNodes()
        self.eyeLids = {}
        self.eyeBalls = {}
        self.eyeIris = {}
        self.flush()
        self.loaded = 0
        self.master = 0

    def getCrazyColorSkinIndex(self):
        return self.crazyColorSkinIndex

    def setCrazyColorSkinIndex(self, index):
        if len(HumanDNA.crazySkinColors) > index:
            self.crazyColorSkinIndex = index
        else:
            self.notify.warning(
                "(Human)index: %d is out of bounds for crazyColorSkin: %d" % (index, len(HumanDNA.crazySkinColors))
            )

    def generateHuman(self, gender, others, useFaceTex=False):
        parent = self.getParent()
        self.detachNode()
        if gender == "f":
            other = others[1]
        else:
            other = others[0]
        if self.loaded:
            self.cleanupHuman()

        other.useFaceTex = useFaceTex
        self.loadHuman(other)
        if self.isLocal():
            self.renderReflection = True

        self.setRenderReflection()
        self.resetEffectParent()
        self.enableMixing()
        self.reparentTo(parent)

    def getShadowJoint(self):
        return self.nametagNodePath

    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle("modelRoot", lodName)
            joint = bundle.findChild("name_tag")
            if joint:
                joints.append(joint)
                continue

        return joints

    def _Human__blinkOpenEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() == "closed":
            self.eyeFSM.request("open")

        r = self.randGen.random()
        if r < 0.10000000000000001:
            t = 0.20000000000000001
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self._Human__blinkCloseEyes, self._Human__blinkName)
        return Task.done

    def _Human__blinkCloseEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() != "open":
            taskMgr.doMethodLater(4.0, self._Human__blinkCloseEyes, self._Human__blinkName)
        else:
            self.eyeFSM.request("closed")
            taskMgr.doMethodLater(0.125, self._Human__blinkOpenEyes, self._Human__blinkName)
        return Task.done

    def startBlink(self):
        taskMgr.remove(self._Human__blinkName)
        if self.eyeLids:
            self.openEyes()

        taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1, self._Human__blinkCloseEyes, self._Human__blinkName)

    def stopBlink(self):
        taskMgr.remove(self._Human__blinkName)
        if self.eyeLids:
            self.eyeFSM.request("open")

    def closeEyes(self):
        self.eyeFSM.request("closed")

    def openEyes(self):
        self.eyeFSM.request("open")

    def enterEyeFSMOff(self):
        pass

    def exitEyeFSMOff(self):
        pass

    def enterEyeFSMOpen(self):
        for lodName in self.getLODNames():
            self.eyeLids[lodName].hide()
            self.eyeBalls[lodName].show()
            self.eyeIris[lodName].show()

    def exitEyeFSMOpen(self):
        pass

    def enterEyeFSMClosed(self):
        return None
        for lodName in self.getLODNames():
            self.eyeLids[lodName].show()
            self.eyeBalls[lodName].hide()
            self.eyeIris[lodName].hide()

    def exitEyeFSMClosed(self):
        pass

    def getGlobalScale(self):
        return self.rootScale

    def calcBodyScale(self):
        idx = 0
        if self.gender == "f":
            idx = 1

        mappedValue = (0.80000000000000004 + (1 + self.style.getBodyHeight()) * 0.20000000000000001) * BodyScales[idx][
            self.style.getBodyShape()
        ]
        return mappedValue

    def setupAnimDicts(cls):
        for t in BodyDefs.maleFrames:
            cls.storeAnimDict("models/char/mp", "m", t)

        for t in BodyDefs.femaleFrames:
            cls.storeAnimDict("models/char/fp", "f", t)

    setupAnimDicts = classmethod(setupAnimDicts)

    def storeAnimDict(cls, prefix, gender, type):
        qualifier = gender + type
        animList = AnimListDict[type]
        cls.prebuiltAnimData[qualifier] = {}
        for anim in animList:
            if anim[0] == "intro":
                continue

            animSuffix = ""
            for i in range(0, len(CustomAnimDict[qualifier])):
                if anim[0] == CustomAnimDict[qualifier][i]:
                    animSuffix = "_" + gender + NewModelDict.get(type)
                    break
                    continue

            cls.prebuiltAnimData[qualifier][anim[0]] = prefix + "_" + anim[1] + animSuffix

    storeAnimDict = classmethod(storeAnimDict)
예제 #16
0
class DynamicHuman(HumanBase.HumanBase, Biped.Biped):
    notify = DirectNotifyGlobal.directNotify.newCategory('Human')

    def __init__(self, other = None):
        Biped.Biped.__init__(self, other, HumanAnimationMixer)
        self.model = None
        self.zombie = False
        self.crazyColorSkin = False
        self.crazyColorSkinIndex = 0
        self.flattenPending = None
        if __dev__:
            self.optimizeLOD = base.config.GetBool('optimize-avatar-lod', 1)
        else:
            self.optimizeLOD = 0
        self.master = 0
        self.loaded = 0
        self.playingRate = None
        self.shadowFileName = 'models/misc/drop_shadow'
        self.setFont(PiratesGlobals.getInterfaceFont())
        self._DynamicHuman__blinkName = 'blink-' + str(self.this)
        self.eyeLids = None
        self.eyeBalls = None
        self.eyeIris = None
        self.reducedAnimList = None
        self.headNode = None
        self.extraNode = None
        self.scaleNode = None
        self.rootNode = None
        self.floorOffsetZ = 0.0
        self.headFudgeHpr = Vec3(0, 0, 0)
        self.randGen = random.Random()
        self.randGen.seed(random.random())
        self.eyeFSM = ClassicFSM('eyeFSM', [
            State('off', self.enterEyeFSMOff, self.exitEyeFSMOff, [
                'open',
                'closed']),
            State('open', self.enterEyeFSMOpen, self.exitEyeFSMOpen, [
                'closed',
                'off']),
            State('closed', self.enterEyeFSMClosed, self.exitEyeFSMClosed, [
                'open',
                'off'])], 'off', 'off')
        self.eyeFSM.enterInitialState()
        self.isPaid = True
        if other != None:
            self.copyHuman(other)



    def removeCopiedNodes(self):
        self.dropShadow = self.find('**/drop_shadow*')
        if not self.dropShadow.isEmpty():
            self.deleteDropShadow()



    def flattenHuman(self):
        self.deleteNametag3d()
        self.getWeaponJoints()


    def _DynamicHuman__doneFlattenHuman(self, models):
        self.flattenPending = None
        self.getWeaponJoints()


    def copyHuman(self, other):
        self.gender = other.gender
        self.loaded = other.loaded
        self.type = other.type
        self.loadAnimatedHead = other.loadAnimatedHead
        self.flattenHuman()
        self.model = None


    def delete(self):
        self.Human_deleted = 1
        taskMgr.remove(self._DynamicHuman__blinkName)
        if self.dropShadow and not self.dropShadow.isEmpty():
            self.deleteDropShadow()

        del self.eyeFSM
        self.controlShapes = None
        self.sliderNames = None
        if self.model:
            self.model.delete()
            del self.model

        Biped.Biped.delete(self)



    def isDeleted(self):

        try:
            if self.Human_deleted == 1:
                return True
        except:
            return False



    def setupExtraNodes(self):
        idx = 0
        if self.gender == 'f':
            idx = 1

        jointName = 'def_head01'
        jointNameExtra = 'def_extra_jt'
        jointNameScale = 'def_scale_jt'
        lods = self.getLODNames()
        self.headNode = self.controlJoint(None, 'legs', jointName, lods[0])
        self.extraNode = self.controlJoint(None, 'legs', jointNameExtra, lods[0])
        self.scaleNode = self.controlJoint(None, 'legs', jointNameScale, lods[0])
        self.rootNode = self.getLOD('2000').find('**/dx_root')
        self.floorOffsetZ = self.rootNode.getZ()
        for lod in lods[1:]:
            self.controlJoint(self.headNode, 'legs', jointName, lod)
            self.controlJoint(self.extraNode, 'legs', jointNameExtra, lod)
            self.controlJoint(self.scaleNode, 'legs', jointNameScale, lod)
            exposedHeadJoint = self.getLOD(lod).find('**/def_head01')
            if not exposedHeadJoint.isEmpty():
                exposedHeadJoint.removeNode()
                continue

        self.headNode.setScale(HeadScales[idx][self.style.getBodyShape()])
        self.setGlobalScale(self.calcBodyScale())


    def undoExtraNodes(self):
        jointNameExtra = 'def_extra_jt'
        jointNameScale = 'def_scale_jt'
        joints = self.findAllMatches('**/*' + jointNameExtra)
        if not joints.isEmpty():
            joints.detach()
            joints.clear()

        if self.headNode:
            self.headNode.removeNode()
            self.headNode = None
            self.extraNode.removeNode()
            self.extraNode = None

        joints = self.findAllMatches('**/*' + jointNameScale)
        if not joints.isEmpty():
            joints.detach()
            joints.clear()

        if self.scaleNode:
            self.scaleNode.removeNode()
            self.scaleNode = None
            self.rootNode = None



    def fixEyes(self):
        self.eyeLids = { }
        self.eyeBalls = { }
        self.eyeIris = { }
        for lodName in self.getLODNames():
            geom = self.getPart('head', lodName)
            self.eyeLids[lodName] = geom.findAllMatches('**/*eyelid*')
            self.eyeBalls[lodName] = geom.findAllMatches('**/eye_ball*')
            self.eyeIris[lodName] = geom.findAllMatches('**/eye_iris*')
            self.eyeLids[lodName].stash()
            self.eyeBalls[lodName].unstash()
            self.eyeIris[lodName].unstash()



    def generateFaceTexture(self):
        faceTextureIdx = self.style.head.texture
        if self.gender == 'f':
            face_textures = PirateFemale.face_textures
        else:
            face_textures = PirateMale.face_textures
        tex_name = self.getTrySafe(face_textures, faceTextureIdx)
        if tex_name != None:
            tex = self.model.faceTextures.findTexture(tex_name)
            if tex == None:
                return None

        else:
            return None
        for lodName in self.getLODNames():
            self.findAllMatches('**/body_master_face').setTexture(tex, 1)



    def generateSkinColor(self):
        skinColor = self.style.getSkinColor()
        self.model.faces[0].setColorScale(skinColor)
        if self.model.newAvatars:
            self.model.currentBody.setColorScale(skinColor)
        else:
            numPaths = self.model.body.getNumPaths()
            if self.zombie:
                self.model.body.setColorScale(Vec4(1, 1, 1, 1))
            else:
                self.model.body.setColorScale(skinColor)


    def generateSkinTexture(self):
        bodyTextureIdx = self.style.body.skin
        if self.zombie:
            if self.gender == 'f':
                bodyTextureIdx = PirateFemale.ZOMB_BODY_TEXTURE
            else:
                bodyTextureIdx = PirateMale.ZOMB_BODY_TEXTURE

        if self.gender == 'f':
            body_textures = PirateFemale.body_textures[self.style.body.shape]
        else:
            body_textures = PirateMale.body_textures[self.style.body.shape]
        tex_name = self.getTrySafe(body_textures, bodyTextureIdx)
        if tex_name != None:
            tex = self.model.bodyTextures.findTexture(tex_name)
        else:
            return None
        for parts in self.model.bodys:
            parts.setTexture(tex, 1)



    def generateHairColor(self, colorName = None, colorModel = None):
        self.model.setHairBaseColor()


    def getTrySafe(self, list, idx):

        try:
            if type(idx) == str:
                lookup = idx.split('_cut')[0]
            else:
                lookup = idx
            return list[lookup]
        except:
            return None



    def generateEyesTexture(self):
        eyesTextureIdx = self.style.head.eyes.color
        if self.gender == 'f':
            eye_iris_textures = PirateFemale.eye_iris_textures
        else:
            eye_iris_textures = PirateMale.eye_iris_textures
        tex_name = self.getTrySafe(eye_iris_textures, eyesTextureIdx)
        if tex_name != None:
            tex = self.eyeIrisTextures.findTexture(tex_name)
        else:
            return None
        self.model.irises.setTexture(tex, 1)


    def generateHatColor(self):
        style = self.style
        if self.zombie:
            style = self.model.dnaZomb

        hatColor = style.lookupHatColor()
        geom = self.getGeomNode()
        geom.findAllMatches('**/hat_band*').setColorScale(hatColor)


    def generateClothesColor(self):
        style = self.style
        if self.zombie:
            style = self.model.dnaZomb

        clothesTopColor = style.lookupClothesTopColor()
        clothesBotColor = style.lookupClothesBotColor()
        geom = self.getGeomNode()
        geom.findAllMatches('**/clothing_layer1_shirt*').setColorScale(clothesTopColor[0])
        geom.findAllMatches('**/clothing_layer2_vest*').setColorScale(clothesTopColor[1])
        geom.findAllMatches('**/clothing_layer3_coat*').setColorScale(clothesTopColor[2])
        geom.findAllMatches('**/clothing_layer1_pant*').setColorScale(clothesBotColor[0])
        geom.findAllMatches('**/clothing_layer2_belt*').setColorScale(clothesBotColor[1])
        geom.findAllMatches('**/clothing_layer1_shoe*').setColorScale(clothesBotColor[2])


    def generateColor(self):
        self.generateSkinColor()
        self.generateHairColor()
        self.generateHatColor()


    def makeAnimDict(self, gender, animNames):
        self.animDict = []
        for currAnim in animNames:
            anim = animNames.get(currAnim)
            for currAnimName in anim:
                self.animTable.append([
                    currAnimName,
                    currAnimName])


        self.reducedAnimList = self.animDict


    def forceLoadAnimDict(self):
        for anim in self.animDict:
            self.getAnimControls(anim[0])



    def createAnimDict(self, customList = None):
        if self.gender == 'f':
            filePrefix = 'models/char/f'
            genderPrefix = 'f'
        else:
            filePrefix = 'models/char/m'
            genderPrefix = 'm'
        filePrefix += 'p'
        animList = self.reducedAnimList
        if animList is None:
            animList = AnimListDict[self.type]

        AnimDict.clear()
        for anim in animList:
            animSuffix = ''
            for customAnim in CustomAnimDict[genderPrefix + self.type]:
                if anim[0] == customAnim:
                    animSuffix = '_' + genderPrefix + NewModelDict.get(self.type)
                    break
                    continue

            AnimDict[anim[0]] = filePrefix + '_' + anim[1] + animSuffix

        if self.reducedAnimList is None:
            AnimDict.pop('intro')

        return filePrefix


    def generateBody(self, copy = 1):
        filePrefix = self.createAnimDict()
        lodString = '2000'
        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '2000', copy)
        self.loadAnims(AnimDict, 'modelRoot', 'all')
        if loader.loadModel(filePrefix + '_' + '1000', allowInstance = True) != None:
            lodString = '1000'

        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '1000', copy)
        if loader.loadModel(filePrefix + '_' + '500', allowInstance = True) != None:
            lodString = '500'

        self.loadModel(filePrefix + '_' + lodString, 'modelRoot', '500', copy)
        self.makeSubpart('head', [
            'zz_head01'], [])
        self.makeSubpart('torso', [
            'zz_spine01'], [
            'zz_head01'])
        self.makeSubpart('legs', [
            'dx_root'], [
            'zz_spine01'])
        self.setSubpartsComplete(True)
        self.getWeaponJoints()
        self.eyeIrisTextures = loader.loadModel('models/misc/eye_iris.bam')


    def refreshBody(self):
        if self.style.getGender() == 'f':
            gender = 1
            cjs = FemaleBodyShapeControlJoints
            matrix = FemaleBodyShapeControlJointMatrix
        else:
            gender = 0
            cjs = MaleBodyShapeControlJoints
            matrix = MaleBodyShapeControlJointMatrix
        type = self.style.getBodyShape()
        filePrefix = self.createAnimDict()
        self.loadAnims(AnimDict, 'modelRoot', 'all')
        for jointName in cjs:
            joint = self.find('**/*' + jointName)
            vector = matrix[jointName][type]
            if jointName.find('def') != -1:
                joint.setScale(vector)
                continue
            joint.setPos(vector)

        self.headNode.setScale(HeadScales[gender][self.style.getBodyShape()])
        self.setGlobalScale(self.calcBodyScale())
        self.createAnimDict()
        self.stop(self.getCurrentAnim())
        self.loop(self.getCurrentAnim())


    def setLODs(self):
        self.setLODNode()
        avatarDetail = base.config.GetString('avatar-detail', 'high')
        if avatarDetail == 'high':
            dist = [
                0,
                20,
                80,
                280]
        elif avatarDetail == 'med':
            dist = [
                0,
                10,
                40,
                280]
        elif avatarDetail == 'low':
            dist = [
                0,
                5,
                20,
                280]
        else:
            raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
        self.addLOD(2000, dist[1], dist[0])
        self.addLOD(1000, dist[2], dist[1])
        self.addLOD(500, dist[3], dist[2])
        if self.optimizeLOD:
            lowLOD = self.getLOD('500')
            lowLOD.setTextureOff(1000)
            lowLOD.setTransparency(0, 1000)

        self.getLODNode().setCenter(Point3(0, 0, 5))


    def showLOD(self, lodName):
        if not self.model.loaded:
            self.model.setupHead(lodName)
            self.model.setupBody(lodName)
            self.model.setupClothing(lodName)
            if self.master:
                self.model.setupSelectionChoices('NPC')

            self.model.loaded = 1

        self.model.setFromDNA()
        self.generateEyesTexture()
        if self.optimizeLOD:
            self.optimizeLowLOD()

        self.generateColor()


    def loadHuman(self, gender = 'm', other = None):
        if other:
            pirate = other
            pirate.style = self.style
        else:
            pirate = self
        pirate.gender = gender
        if self.loaded:
            return None

        if pirate.gender == 'f':
            pirate.type = BodyDefs.femaleFrames[pirate.style.getBodyShape()]
            controlShapes = PirateFemale.ControlShapes
            sliderNames = PirateFemale.SliderNames
        else:
            pirate.type = BodyDefs.maleFrames[pirate.style.getBodyShape()]
            controlShapes = PirateMale.ControlShapes
            sliderNames = PirateMale.SliderNames
        if not pirate.loaded:
            pirate.setLODs()
            pirate.loadAnimatedHead = True
            pirate.generateBody()
            if pirate.gender == 'f':
                pirate.model = PirateFemale.PirateFemale(pirate, pirate.style)
                self.pirateFemale = PirateFemale.PirateFemale(pirate, pirate.style)
            else:
                pirate.model = PirateMale.PirateMale(pirate, pirate.style)
                self.pirateMale = PirateMale.PirateMale(pirate, pirate.style)
            if base.config.GetBool('debug-dynamic-human', 0):
                pirate.model.newAvatars = True
            else:
                pirate.model.newAvatars = False
            pirate.faceAwayFromViewer()
            pirate.fixEyes()
        else:
            pirate.model.dna = pirate.style
            pirate.reducedAnimList = self.reducedAnimList
            pirate.createAnimDict()
            pirate.loadAnims(AnimDict, 'modelRoot', 'all')
        self.lods = pirate.getLODNames()
        if pirate.gender == 'f':
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 1
        else:
            self.headFudgeHpr = Vec3(0, 0, 0)
            idx = 0
        pirate.zombie = self.zombie
        pirate.showLOD(2000)
        pirate.loaded = 1
        self.model = pirate.model
        if pirate.zombie:
            pirate.showZombie()
        else:
            pirate.showNormal()
        if hasattr(self, 'motionFSM'):
            self.motionFSM.setAvatar(self)

        self.controlShapes = controlShapes
        self.sliderNames = sliderNames
        if other:
            self.copyActor(other)
            self.fixEyes()
            self.copyHuman(other)
            self.undoExtraNodes()

        self.setupExtraNodes()
        self.applyBodyShaper()
        self.applyHeadShaper()
        if other:
            pirate.zombie = 0
            pirate.showNormal()
            pirate.unloadAnims(AnimDict, None, None)
            pirate.removeAnimControlDict()
            pirate.reducedAnimList = None

        self.initializeMiscNodes()
        self.startBlink()


    def initializeMiscNodes(self):
        self.initializeNametag3d()
        self.initializeDropShadow()
        if self.getLOD('2000') == None:
            return None

        exposedHeadJoint = self.getLOD('2000').find('**/def_head01')
        if not exposedHeadJoint.isEmpty():
            idx = 0
            if self.gender == 'f':
                idx = 1

            exposedHeadJoint.setScale(1)
            self.headNode.reparentTo(exposedHeadJoint)
            self.headNode.setScale(HeadScales[idx][self.style.getBodyShape()])



    def undoControlJoints(self):
        self.getGeomNode().getParent().findAllMatches('def_*').detach()
        self.getGeomNode().getParent().findAllMatches('trs_*').detach()
        self.findAllMatches('def_*').detach()
        self.findAllMatches('trs_*').detach()


    def cleanupHuman(self, gender = 'm'):
        self.eyeFSM.request('off')
        self.undoExtraNodes()
        self.undoControlJoints()
        self.eyeLids = { }
        self.eyeBalls = { }
        self.eyeIris = { }
        self.flush()
        self.loaded = 0
        self.master = 0


    def generateHuman(self, gender = 'm', others = None):
        other = None
        if others:
            if gender == 'f':
                other = others[1]
            else:
                other = others[0]

        if other and not (other.master) and other.loaded:
            other.cleanupHuman()
        elif self.loaded:
            self.cleanupHuman()

        self.loadHuman(self.style.gender, other)
        if self.isLocal():
            self.renderReflection = True

        self.setRenderReflection()
        self.disableMixing()
        self.enableMixing()

    generateHuman = quickProfile('loadHuman')(generateHuman)

    def getShadowJoint(self):
        return self


    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('legs', lodName)
            joint = bundle.findChild('name_tag')
            if joint:
                joints.append(joint)
                continue

        return joints


    def _DynamicHuman__blinkOpenEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() == 'closed':
            self.eyeFSM.request('open')

        r = self.randGen.random()
        if r < 0.10000000000000001:
            t = 0.20000000000000001
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self._DynamicHuman__blinkCloseEyes, self._DynamicHuman__blinkName)
        return Task.done


    def _DynamicHuman__blinkCloseEyes(self, task):
        if self.eyeFSM.getCurrentState().getName() != 'open':
            taskMgr.doMethodLater(4.0, self._DynamicHuman__blinkCloseEyes, self._DynamicHuman__blinkName)
        else:
            self.eyeFSM.request('closed')
            taskMgr.doMethodLater(0.125, self._DynamicHuman__blinkOpenEyes, self._DynamicHuman__blinkName)
        return Task.done


    def startBlink(self):
        taskMgr.remove(self._DynamicHuman__blinkName)
        if self.eyeLids:
            self.openEyes()

        taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1, self._DynamicHuman__blinkCloseEyes, self._DynamicHuman__blinkName)


    def stopBlink(self):
        taskMgr.remove(self._DynamicHuman__blinkName)
        if self.eyeLids:
            self.eyeFSM.request('open')



    def closeEyes(self):
        self.eyeFSM.request('closed')


    def openEyes(self):
        self.eyeFSM.request('open')


    def enterEyeFSMOff(self):
        pass


    def exitEyeFSMOff(self):
        pass


    def enterEyeFSMOpen(self):
        for lodName in self.getLODNames():
            if not self.eyeLids[lodName].isEmpty():
                self.eyeLids[lodName].hide()
                self.eyeBalls[lodName].show()
                self.eyeIris[lodName].show()
                continue



    def exitEyeFSMOpen(self):
        pass


    def enterEyeFSMClosed(self):
        return None
        for lodName in self.getLODNames():
            if not self.eyeLids[lodName].isEmpty():
                self.eyeLids[lodName].show()
                self.eyeBalls[lodName].hide()
                self.eyeIris[lodName].hide()
                continue



    def exitEyeFSMClosed(self):
        pass


    def setControlValue(self, r, name):
        if self.style.getGender() == 'f':
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
        else:
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
        shapes = self.controlShapes
        ctl = shapes[name]
        slider = ctl[0]
        if r < 0.0:
            if len(ctl) > 1:
                slider = ctl[1]


        for i in range(0, len(slider)):
            jointName = slider[i][0]
            jointCtls = self.findAllMatches(jointName)
            posI = matrixI[jointName][0]
            hprI = matrixI[jointName][1]
            sclI = matrixI[jointName][2]
            posF = VBase3(posI[0], posI[1], posI[2])
            hprF = VBase3(hprI[0], hprI[1], hprI[2])
            sclF = VBase3(sclI[0], sclI[1], sclI[2])
            self.notify.debug('scv: %s initial %s' % (jointName, posI))
            dr = slider[i][4] * r
            ctl[0][i][5] = dr
            posDelta = VBase3(0, 0, 0)
            hprDelta = VBase3(0, 0, 0)
            sclDelta = VBase3(0, 0, 0)
            for sliderIdx in xrange(0, len(matrixF[jointName])):
                sliderName = matrixF[jointName][sliderIdx]
                jointSet = shapes[sliderName][0]
                for jointIdx in xrange(0, len(jointSet)):
                    if jointSet[jointIdx][0] == jointName:
                        if jointSet[jointIdx][1] == TX:
                            posDelta.setX(posDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == TY:
                            posDelta.setY(posDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == TZ:
                            posDelta.setZ(posDelta.getZ() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RX:
                            hprDelta.setX(hprDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RY:
                            hprDelta.setY(hprDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == RZ:
                            hprDelta.setZ(hprDelta.getZ() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SX:
                            if r < 0.0:
                                sclDelta.setX(sclDelta.getX() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setX(sclDelta.getX() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SY:
                            if r < 0.0:
                                sclDelta.setY(sclDelta.getY() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setY(sclDelta.getY() + jointSet[jointIdx][5])
                        elif jointSet[jointIdx][1] == SZ:
                            if r < 0.0:
                                sclDelta.setZ(sclDelta.getZ() + jointSet[jointIdx][5] / jointSet[jointIdx][2])
                            else:
                                sclDelta.setZ(sclDelta.getZ() + jointSet[jointIdx][5])
                        else:
                            self.notify.warning('scv:wrong element = %s' % jointSet[jointIdx][1])
                    jointSet[jointIdx][1] == TX


            self.notify.debug('scv: %s composite posDelta = %s' % (jointName, posDelta))
            posF.setX(posI[0] + posDelta[0])
            posF.setY(posI[1] + posDelta[1])
            posF.setZ(posI[2] + posDelta[2])
            self.notify.debug('scv: %s final posDelta%s' % (jointName, posF))
            self.notify.debug('scv: %s composite hprDelta = %s' % (jointName, hprDelta))
            hprF.setX(hprI[0] + hprDelta[0])
            hprF.setY(hprI[1] + hprDelta[1])
            hprF.setZ(hprI[2] + hprDelta[2])
            self.notify.debug('scv: %s final hprDelta%s' % (jointName, hprF))
            self.notify.debug('scv: %s composite sclDelta = %s' % (jointName, sclDelta))
            sclF.setX(sclI[0] + sclDelta[0])
            sclF.setY(sclI[1] + sclDelta[1])
            sclF.setZ(sclI[2] + sclDelta[2])
            self.notify.debug('scv: %s final sclDelta%s' % (jointName, sclF))
            for j in range(0, jointCtls.getNumPaths()):
                jointCtl = jointCtls[j]
                jointCtl.setPosHprScale(posF, hprF, sclF)




    def applyBodyShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleBodyShapeControlJoints
            matrix = FemaleBodyShapeControlJointMatrix
        else:
            cjs = MaleBodyShapeControlJoints
            matrix = MaleBodyShapeControlJointMatrix
        type = self.style.getBodyShape()
        for jointName in cjs:
            for lodName in self.getLODNames():
                if lodName == '2000':
                    joint = self.controlJoint(None, 'legs', jointName, lodName)
                    continue
                joint = self.controlJoint(joint, 'legs', jointName, lodName)

            joint = self.find('**/*' + jointName)
            vector = matrix[jointName][type]
            if jointName.find('def') != -1:
                joint.setScale(vector)
                continue
            joint.setPos(vector)



    def undoBodyShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleBodyShapeControlJoints
        else:
            cjs = MaleBodyShapeControlJoints


    def applyHeadShaper(self):
        self.createControlJoints()
        self.initHeadControlShapes()
        self.setHeadControlShapeValues()


    def undoHeadShaper(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
        else:
            cjs = MaleHeadShapeControlJoints


    def createControlJoints(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
        else:
            cjs = MaleHeadShapeControlJoints
        for jointName in cjs:
            for lodName in self.getLODNames():
                if lodName == '2000':
                    joint = self.controlJoint(None, 'legs', jointName, lodName)
                    continue
                if lodName == '1000':
                    continue
                    continue
                if lodName == '500':
                    continue
                    continue




    def initHeadControlShapes(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
        else:
            cjs = MaleHeadShapeControlJoints
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
        if len(matrixF['initialized']) > 0:
            return None

        initializedMatrixI = len(matrixI['initialized'])
        initializedMatrixF = len(matrixF['initialized'])
        for jointName in cjs:
            transform = TransformState.makeMat(self.getJointTransform('legs', jointName, '2000'))
            pos = Vec3(transform.getPos())
            hpr = Vec3(transform.getHpr())
            scale = Vec3(transform.getScale())
            matrixI[jointName].append(pos)
            matrixI[jointName].append(hpr)
            matrixI[jointName].append(scale)

        matrixI['initialized'].append('initialized')
        shapes = self.controlShapes
        names = self.sliderNames
        for i in xrange(0, len(shapes)):
            slider = shapes[names[i]]
            for k in xrange(0, len(slider[0])):
                slider[0][k][4] = slider[0][k][2]
                if len(slider) > 1:
                    slider[1][k][4] = slider[1][k][2]
                    continue


        for i in xrange(0, len(shapes)):
            slider = shapes[names[i]]
            for k in xrange(0, len(slider[0])):
                jointCtl = slider[0][k]
                jointName = jointCtl[0]
                matrixF[jointName].append(names[i])
                pos = matrixI[jointName][0]
                hpr = matrixI[jointName][1]
                scl = matrixI[jointName][2]

                if jointCtl[1] < 3:
                    posDelta = jointCtl[4] - pos[jointCtl[1]]
                    jointCtl[4] = posDelta
                    if len(slider) > 1:
                        jointCtl = slider[1][k]
                        jointCtl[4] = posDelta

                if len(slider) > 1:
                    if jointCtl[1] > 2 and jointCtl[1] < 6:
                        hprDelta = jointCtl[4] - hpr[jointCtl[1] - 3]
                        jointCtl[4] = hprDelta
                        if len(slider) > 1:
                            jointCtl = slider[1][k]
                            jointCtl[4] = hprDelta

                #if len(slider) > 1:
                    #sclDelta = jointCtl[4] - scl[jointCtl[1] - 6]
                    #jointCtl[4] = sclDelta

                #if len(slider) > 1:
                #    jointCtl = slider[1][k]
                #    jointCtl[4] = sclDelta


        matrixF['initialized'].append('initialized')


    def setHeadControlShapeValues_old(self):
        value = self.style.getHeadSize()
        mappedValue = 0.90000000000000002 + (1 + value) * 0.10000000000000001
        self.extraNode.setScale(2 - mappedValue, mappedValue, 1)
        self.setControlValue(self.style.getHeadWidth(), 'headWidth')
        self.setControlValue(self.style.getHeadHeight(), 'headHeight')
        self.setControlValue(self.style.getHeadRoundness(), 'headRoundness')
        self.setControlValue(self.style.getJawWidth(), 'jawWidth')
        self.setControlValue(self.style.getJawAngle(), 'jawChinAngle')
        self.setControlValue(self.style.getJawChinSize(), 'jawChinSize')
        self.setControlValue(self.style.getJawLength(), 'jawLength')
        self.setControlValue(self.style.getMouthWidth(), 'mouthWidth')
        self.setControlValue(self.style.getMouthLipThickness(), 'mouthLipThickness')
        self.setControlValue(self.style.getCheekFat(), 'cheekFat')
        self.setControlValue(self.style.getBrowProtruding(), 'browProtruding')
        self.setControlValue(self.style.getEyeCorner(), 'eyeCorner')
        self.setControlValue(self.style.getEyeOpeningSize(), 'eyeOpeningSize')
        self.setControlValue(self.style.getEyeBulge(), 'eyeSpacing')
        self.setControlValue(self.style.getNoseBridgeWidth(), 'noseBridgeWidth')
        self.setControlValue(self.style.getNoseNostrilWidth(), 'noseNostrilWidth')
        self.setControlValue(self.style.getNoseLength(), 'noseLength')
        self.setControlValue(self.style.getNoseBump(), 'noseBump')
        self.setControlValue(self.style.getNoseNostrilHeight(), 'noseNostrilHeight')
        self.setControlValue(self.style.getNoseNostrilAngle(), 'noseNostrilAngle')
        self.setControlValue(self.style.getNoseBridgeBroke(), 'noseBridgeBroke')
        self.setControlValue(self.style.getNoseNostrilBroke(), 'noseNostrilBroke')
        self.setControlValue(self.style.getEarScale(), 'earScale')
        self.setControlValue(self.style.getEarFlapAngle(), 'earFlap')
        self.setControlValue(self.style.getEarPosition(), 'earPosition')


    def getGlobalScale(self):
        return self.scaleNode.getScale()


    def setGlobalScale(self, scale):
        self.scaleNode.setScale(scale)
        self.scaleNode.setZ(-(self.floorOffsetZ * (1 - scale)))


    def calcBodyScale(self):
        idx = 0
        if self.gender == 'f':
            idx = 1

        mappedValue = (0.80000000000000004 + (1 + self.style.getBodyHeight()) * 0.20000000000000001) * BodyScales[idx][self.style.getBodyShape()]
        return mappedValue


    def showZombie(self):
        self.model.irises.stash()
        self.model.faces[0].stash()
        self.model.faceZomb.unstash()
        self.generateSkinTexture()


    def showNormal(self):
        self.model.irises.unstash()
        self.model.faces[0].unstash()
        self.model.faceZomb.stash()
        self.generateSkinTexture()


    def takeAwayTexture(self, geoms, omitFace = False):
        emptyRenderState = RenderState.makeEmpty()
        eyeIrisColor = VBase4(0, 0, 0, 1)
        for i in range(0, geoms.getNumPaths()):
            element = geoms[i]
            if 'eye_iris' in element.getName():
                element.setColorScale(eyeIrisColor)
            elif omitFace and 'master_face' in element.getName():
                continue

            element.setTextureOff()
            geom = element.node()
            for j in range(0, geom.getNumGeoms()):
                geom.setGeomState(j, emptyRenderState)




    def optimizeMedLOD(self):
        medLOD = self.getLOD('1000')
        geoms = medLOD.findAllMatches('**/teeth*')
        geoms.stash()
        self.medSkinGone = False
        geoms = medLOD.find('**/body_forearm*')
        if geoms.isEmpty():
            self.medSkinGone = True
            geoms = medLOD.findAllMatches('**/body_*')
            self.takeAwayTexture(geoms, True)

        geoms = medLOD.findAllMatches('**/hair_*')
        self.takeAwayTexture(geoms)
        if self.gender != 'f':
            geoms = medLOD.findAllMatches('**/beard_*')
            self.takeAwayTexture(geoms)
            geoms = medLOD.findAllMatches('**/mustache_*')
            self.takeAwayTexture(geoms)

        geoms = medLOD.findAllMatches('**/eye_*')
        self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/clothing_layer2_belt_*')
        self.takeAwayTexture(geoms)
        geoms = medLOD.findAllMatches('**/clothing_layer1_shoe_*')
        self.takeAwayTexture(geoms)


    def optimizeLowLOD(self):
        lowLOD = self.getLOD('500')
        geoms = lowLOD.findAllMatches('**/teeth*')
        geoms.stash()
        geoms = lowLOD.findAllMatches('**/+GeomNode')
        self.takeAwayTexture(geoms)


    def setHeadControlShapeValues(self):
        value = self.style.getHeadSize()
        mappedValue = 0.90000000000000002 + (1 + value) * 0.10000000000000001
        self.extraNode.setScale(2 - mappedValue, mappedValue, 1)
        self.setControlValue_new(self.style.getHeadWidth(), 'headWidth')
        self.setControlValue_new(self.style.getHeadHeight(), 'headHeight')
        self.setControlValue_new(self.style.getHeadRoundness(), 'headRoundness')
        self.setControlValue_new(self.style.getJawWidth(), 'jawWidth')
        self.setControlValue_new(self.style.getJawAngle(), 'jawChinAngle')
        self.setControlValue_new(self.style.getJawChinSize(), 'jawChinSize')
        self.setControlValue_new(self.style.getJawLength(), 'jawLength')
        self.setControlValue_new(self.style.getMouthWidth(), 'mouthWidth')
        self.setControlValue_new(self.style.getMouthLipThickness(), 'mouthLipThickness')
        self.setControlValue_new(self.style.getCheekFat(), 'cheekFat')
        self.setControlValue_new(self.style.getBrowProtruding(), 'browProtruding')
        self.setControlValue_new(self.style.getEyeCorner(), 'eyeCorner')
        self.setControlValue_new(self.style.getEyeOpeningSize(), 'eyeOpeningSize')
        self.setControlValue_new(self.style.getEyeBulge(), 'eyeSpacing')
        self.setControlValue_new(self.style.getNoseBridgeWidth(), 'noseBridgeWidth')
        self.setControlValue_new(self.style.getNoseNostrilWidth(), 'noseNostrilWidth')
        self.setControlValue_new(self.style.getNoseLength(), 'noseLength')
        self.setControlValue_new(self.style.getNoseBump(), 'noseBump')
        self.setControlValue_new(self.style.getNoseNostrilHeight(), 'noseNostrilHeight')
        self.setControlValue_new(self.style.getNoseNostrilAngle(), 'noseNostrilAngle')
        self.setControlValue_new(self.style.getNoseBridgeBroke(), 'noseBridgeBroke')
        self.setControlValue_new(self.style.getNoseNostrilBroke(), 'noseNostrilBroke')
        self.setControlValue_new(self.style.getEarScale(), 'earScale')
        self.setControlValue_new(self.style.getEarFlapAngle(), 'earFlap')
        self.setControlValue_new(self.style.getEarPosition(), 'earPosition')
        self.postProcess_setHeadControlShapeValues()


    def setControlValue_new(self, r, name):
        ctl = self.controlShapes[name]
        zeroindex = ctl[0]
        sliders = zeroindex
        if r < 0.0:
            if len(ctl) > 1:
                sliders = ctl[1]


        for i in range(0, len(sliders)):
            zeroindex[i][5] = sliders[i][4] * r



    def postProcess_setHeadControlShapeValues(self):
        if self.style.getGender() == 'f':
            cjs = FemaleHeadShapeControlJoints
            matrixF = FemaleHeadShapeControlJointMatrix
            matrixI = FemaleHeadShapeInitialControlJointMatrix
        else:
            cjs = MaleHeadShapeControlJoints
            matrixF = MaleHeadShapeControlJointMatrix
            matrixI = MaleHeadShapeInitialControlJointMatrix
        posDelta = VBase3()
        hprDelta = VBase3()
        sclDelta = VBase3()
        fdict2 = {
            0: posDelta.addX,
            1: posDelta.addY,
            2: posDelta.addZ,
            3: hprDelta.addX,
            4: hprDelta.addY,
            5: hprDelta.addZ,
            6: sclDelta.addX,
            7: sclDelta.addY,
            8: sclDelta.addZ }
        for jointName in cjs:
            posDelta.assign(matrixI[jointName][0])
            hprDelta.assign(matrixI[jointName][1])
            sclDelta.assign(matrixI[jointName][2])
            for sliderIdx in xrange(0, len(matrixF[jointName])):
                sliderName = matrixF[jointName][sliderIdx]
                jointSet = self.controlShapes[sliderName][0]
                for sliderJoint in jointSet:
                    if sliderJoint[0] == jointName:
                        fdict2[sliderJoint[1]](sliderJoint[5])
                        continue


            self.find(jointName).setPosHprScale(posDelta, hprDelta, sclDelta)
class InventoryGui(DirectObject):
    directNotify = DirectNotify().newCategory('InventoryGui')
    HiddenPos = (0.2, 0, 0)
    VisiblePos = (-0.1725, 0, 0)
    SwitchTime = 0.3
    AutoShowTime = 1.5
    DELETED = False

    def __init__(self):
        DirectObject.__init__(self)
        self.backpack = base.localAvatar.backpack
        if not self.backpack:
            return
        self.backpack.loadoutGUI = self
        self.oneSlotPos = [(0, 0, 0)]
        self.twoSlotsPos = [(0, 0, 0.3), (0, 0, -0.2)]
        self.threeSlotsPos = [(0, 0, 0.5), (0, 0, 0), (0, 0, -0.5)]
        self.fourSlotPos = [(0, 0, 0.5), (0, 0, 0.15), (0, 0, -0.2),
                            (0, 0, -0.55)]
        self.availableSlot = 0
        self.slots = []
        self.activeSlot = None
        self.defaultSlots = 3
        self.prevSlot = None
        self.ammoLabel = None
        self.inventoryFrame = DirectFrame(parent=base.a2dRightCenter,
                                          pos=(-0.1725, 0, 0))
        self.visibilityBtn = DirectButton(text='',
                                          relief=None,
                                          text_bg=(1, 1, 1, 0),
                                          parent=base.a2dRightCenter,
                                          pos=(-0.1725, 0, 0),
                                          frameSize=(-0.2, 0.2, -0.725, 0.7),
                                          clickSound=None,
                                          rolloverSound=None)
        self.visibilityBtn.bind(DGG.WITHIN, self.__handleVisEnter)
        self.visibilityBtn.bind(DGG.WITHOUT, self.__handleVisExit)
        self.visibilityBtn.setBin('background', 10)
        self.visibilityBtn = None
        self.visibilityBtnStatus = 0
        self.switchSound = True
        self.switchSoundSfx = base.loadSfx(
            'phase_3/audio/sfx/GUI_balloon_popup.ogg')
        self.visibilityFSM = ClassicFSM('InventoryGui-VisibilityFSM', [
            State('off', self.enterOff, self.exitOff),
            State('hidden', self.enterHidden, self.exitHidden),
            State('hidden2visible', self.enterHidden2Visible,
                  self.exitHidden2Visible),
            State('visible', self.enterVisible, self.exitVisible),
            State('visible2hidden', self.enterVisible2Hidden,
                  self.exitVisible2Hidden)
        ], 'off', 'off')
        self.visibilityFSM.enterInitialState()
        self.visibilityFSM.request('hidden')
        return

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterHidden(self):
        self.inventoryFrame.setPos(InventoryGui.HiddenPos)
        self.inventoryFrame.hide()

    def exitHidden(self):
        pass

    def enterVisible(self, autoShow=False):
        self.inventoryFrame.setPos(InventoryGui.VisiblePos)
        self.inventoryFrame.show()
        if self.visibilityBtnStatus == 0:
            if autoShow is False:
                self.visibilityFSM.request('visible2hidden')

    def exitVisible(self):
        pass

    def enterHidden2Visible(self, autoShow=False):
        self.inventoryFrame.show()
        self.moveIval = LerpPosInterval(self.inventoryFrame,
                                        duration=InventoryGui.SwitchTime,
                                        pos=InventoryGui.VisiblePos,
                                        startPos=InventoryGui.HiddenPos)
        self.moveIval.setDoneEvent('hidden2visible')
        self.acceptOnce('hidden2visible', self.visibilityFSM.request,
                        ['visible', [autoShow]])
        self.moveIval.start()

    def exitHidden2Visible(self):
        self.ignore('hidden2visible')
        self.moveIval.finish()
        del self.moveIval

    def enterVisible2Hidden(self):
        self.moveIval = LerpPosInterval(self.inventoryFrame,
                                        duration=InventoryGui.SwitchTime,
                                        pos=InventoryGui.HiddenPos,
                                        startPos=InventoryGui.VisiblePos)
        self.moveIval.setDoneEvent('visible2hidden')
        self.acceptOnce('visible2hidden', self.visibilityFSM.request,
                        ['hidden'])
        self.moveIval.start()

    def exitVisible2Hidden(self):
        self.ignore('visible2hidden')
        self.moveIval.finish()
        del self.moveIval

    def click_setWeapon(self, slot, cmd):
        self.setWeapon(slot, playSound=False)

    def setWeapon(self, slot, playSound=True, showUpIfHidden=False):
        if isinstance(slot, str):
            for iSlot in self.slots:
                if iSlot.getGag():
                    if iSlot.getGag().getID() == slot:
                        slot = iSlot

        if self.activeSlot and slot != self.activeSlot:
            self.activeSlot.setOutlineImage('idle')
            self.prevSlot = self.activeSlot
        if slot.getGag() and self.backpack.getSupply(slot.getGag().getID(
        )) > 0 and not slot.getGag().getState() == GagState.RECHARGING:
            if self.activeSlot != slot:
                gagId = slot.getGag().getID()
                base.localAvatar.needsToSwitchToGag = gagId
                if base.localAvatar.gagsTimedOut == False:
                    base.localAvatar.b_equip(gagId)
                    base.localAvatar.enableGagKeys()
                slot.setOutlineImage('selected')
                self.activeSlot = slot
            else:
                if self.activeSlot == slot and slot.getGag().getState() in [
                        GagState.LOADED, GagState.RECHARGING
                ]:
                    base.localAvatar.needsToSwitchToGag = 'unequip'
                    if base.localAvatar.gagsTimedOut == False:
                        base.localAvatar.b_unEquip()
                        base.localAvatar.enableGagKeys()
                    self.activeSlot = None
            self.update()
            if self.switchSound and playSound:
                base.playSfx(self.switchSoundSfx)
        if showUpIfHidden:
            base.taskMgr.remove('showUpIfHidden')
            self.__autoVisEnter()
            base.taskMgr.doMethodLater(InventoryGui.AutoShowTime,
                                       self.__autoVisExitTask,
                                       'showUpIfHidden')
        return

    def __autoVisExitTask(self, task):
        if self.visibilityBtnStatus == 0:
            self.__handleVisExit(None, updateBtnStatus=False)
        return task.done

    def __autoVisEnter(self):
        self.__handleVisEnter(None, True, False)
        return

    def __handleVisEnter(self, foo, autoShow=False, updateBtnStatus=True):
        if updateBtnStatus:
            self.visibilityBtnStatus = 1
        if self.visibilityFSM.getCurrentState().getName() == 'hidden':
            self.visibilityFSM.request('hidden2visible', [autoShow])
        else:
            if self.visibilityFSM.getCurrentState().getName(
            ) == 'visible2hidden':
                self.visibilityFSM.request('visible')

    def __handleVisExit(self, foo, updateBtnStatus=True):
        if updateBtnStatus:
            self.visibilityBtnStatus = 0
        base.taskMgr.remove('showUpIfHidden')
        if self.visibilityFSM.getCurrentState().getName() == 'visible':
            self.visibilityFSM.request('visible2hidden')

    def createGui(self):
        self.deleteGui()
        posGroup = self.threeSlotsPos
        if self.defaultSlots == 4:
            posGroup = self.fourSlotPos
        for slot in range(len(posGroup) + 1):
            if slot == 3:
                posGroup = self.fourSlotPos
            slotObj = Slot(self, slot + 1, posGroup[slot], self.inventoryFrame)
            self.slots.append(slotObj)
            if slot == 3:
                slotObj.hide()

        self.ammoLabel = DirectLabel(text='Ammo: 0',
                                     text_fg=(1, 1, 1, 1),
                                     relief=None,
                                     text_shadow=(0, 0, 0, 1),
                                     text_scale=0.08,
                                     pos=(0.2, 0, 0.35),
                                     parent=base.a2dBottomLeft)
        self.ammoLabel.hide()
        self.enableWeaponSwitch()
        self.resetScroll()
        self.update()
        return

    def deleteGui(self):
        self.disableWeaponSwitch()
        for slot in self.slots:
            slot.destroy()

        self.slots = []
        if self.ammoLabel:
            self.ammoLabel.destroy()
            self.ammoLabel = None
        self.DELETED = True
        return

    def resetScroll(self):
        nextGag = 0
        prevGag = -1
        curGag = -1
        if self.prevSlot:
            prevGag = self.slots.index(self.prevSlot)
        if self.activeSlot:
            curGag = self.slots.index(self.activeSlot)
        if curGag == len(self.slots) - 1:
            nextGag = 0
            prevGag = curGag - 1
        else:
            if curGag == 0:
                nextGag = 1
                prevGag = len(self.slots) - 1
            else:
                if curGag == -1:
                    prevGag = len(self.slots) - 1
                else:
                    nextGag = curGag + 1
                    prevGag = curGag - 1
        self.accept('wheel_down',
                    self.setWeapon,
                    extraArgs=[self.slots[nextGag], True, True])
        self.accept('wheel_up',
                    self.setWeapon,
                    extraArgs=[self.slots[prevGag], True, True])

    def update(self):
        if not self.backpack:
            return
        for element in [self.ammoLabel, self.inventoryFrame]:
            if not element:
                return

        updateSlots = list(self.slots)
        for slot in self.slots:
            gag = slot.getGag()
            if not gag:
                updateSlots.remove(slot)
                slot.hide()
                continue
            supply = self.backpack.getSupply(gag.getID())
            index = self.slots.index(slot)
            if not gag and len(self.backpack.getGags()) - 1 >= index:
                gag = self.backpack.getGagByIndex(index)
                slot.setGag(gag)
                if self.backpack.getSupply(gag.getID(
                )) > 0 and not gag.getState() == GagState.RECHARGING:
                    slot.setOutlineImage('idle')
                else:
                    slot.setOutlineImage('no_ammo')
            elif slot == self.activeSlot:
                self.ammoLabel['text_fg'] = (1, 1, 1, 1)
                if supply > 0 and not gag.getState() == GagState.RECHARGING:
                    slot.setOutlineImage('selected')
                else:
                    if supply <= 0:
                        self.ammoLabel['text_fg'] = (0.9, 0, 0, 1)
                    slot.setOutlineImage('no_ammo')
                    self.activeSlot = None
                self.ammoLabel.show()
                self.ammoLabel['text'] = 'Ammo: %s' % self.backpack.getSupply(
                    slot.getGag().getID())
            elif self.backpack.getSupply(slot.getGag().getID(
            )) > 0 and not gag.getState() == GagState.RECHARGING:
                slot.setOutlineImage('idle')
            else:
                slot.setOutlineImage('no_ammo')

        numSlots = len(updateSlots)
        posGroup = {
            1: self.oneSlotPos,
            2: self.twoSlotsPos,
            3: self.threeSlotsPos,
            4: self.fourSlotPos
        }.get(numSlots)
        for i in xrange(len(updateSlots)):
            updateSlots[i].setPos(posGroup[i])
            updateSlots[i].show()

        if self.activeSlot == None:
            self.ammoLabel.hide()
            self.ammoLabel['text'] = 'Ammo: 0'
        self.resetScroll()
        return

    def setBackpack(self, backpack):
        self.backpack = backpack

    def updateLoadout(self):
        if self.backpack:
            loadout = self.backpack.getLoadout()
            if len(loadout) <= 3:
                self.reseatSlots()
            else:
                if len(loadout) == 4:
                    self.reseatSlots(slots=4)
            for i in range(len(self.slots)):
                slot = self.slots[i]
                if i < len(loadout):
                    slot.setGag(loadout[i])
                else:
                    slot.setGag(None)

            self.update()
        return

    def reseatSlots(self, slots=3):
        for slot in range(len(self.slots) - 1):
            if slots == 4:
                self.slots[slot].setPos(self.fourSlotPos[slot])
            else:
                self.slots[slot].setPos(self.threeSlotsPos[slot])

    def enableWeaponSwitch(self):
        for index in range(len(self.slots)):
            self.accept(str(index + 1),
                        self.setWeapon,
                        extraArgs=[self.slots[index], True, True])

    def disableWeaponSwitch(self):
        for key in ['1', '2', '3', '4', 'wheel_down', 'wheel_up']:
            self.ignore(key)

    def getSlots(self):
        return self.slots

    def getActiveSlot(self):
        return self.activeSlot

    def isDeleted(self):
        return self.DELETED
예제 #18
0
class DistributedDisneyCharAI(DistributedAvatarAI, DistributedSmoothNodeAI):
    notify = directNotify.newCategory('DistributedDisneyCharAI')

    def __init__(self, air, charId):
        DistributedAvatarAI.__init__(self, air)
        DistributedSmoothNodeAI.__init__(self, air)
        self.fsm = ClassicFSM('DDCharAI',
                              [State('off', self.enterOff, self.exitOff),
                               State('neutral', self.enterNeutral, self.exitNeutral),
                               State('walking', self.enterWalking, self.exitWalking)],
                              'off', 'off')
        self.fsm.enterInitialState()
        self.charId = charId
        self.avatars = []
        self.inConvo = False
        self.chatsThisConvo = 0
        self.toonOfInterest = 0
        self.saidGoodbye = False
        self.currentPointLetter = None
        self.lastPointLetter = None
        self.talkEnabled = True

    def requestStateData(self):
        if self.charId == SAILOR_DONALD:
            return

        avId = self.air.getAvatarIdFromSender()
        if self.fsm.getCurrentState().getName() == 'neutral':
            self.sendUpdateToAvatarId(avId, 'doNeutral', [self.currentPointLetter])
        elif self.fsm.getCurrentState().getName() == 'walking':
            self.sendUpdateToAvatarId(avId, 'doWalking', [self.currentPointLetter, self.lastPointLetter, self.walkingTimestamp])

    def getCharId(self):
        return self.charId

    def chooseChat(self, chatType):
        if chatType == 'greet':
            sharedOrMine = random.choice([SHARED_GREETINGS, CHAR_GREETINGS])

        elif chatType == 'comment':
            num = random.randint(1, 100)
            if num in SHARED_COMMENT_CHANCE:
                sharedOrMine = SHARED_COMMENTS
            elif num in UNIQUE_COMMENT_CHANCE:
                sharedOrMine = CHAR_COMMENTS

        elif chatType == 'bye':
            sharedOrMine = random.choice([SHARED_GOODBYES, CHAR_GOODBYES])

        if sharedOrMine < CHAR_GREETINGS:
            # It's shared
            chatList = CHATTER[sharedOrMine]
        elif sharedOrMine >= CHAR_GREETINGS:
            # It's unique
            chatList = CHATTER[sharedOrMine][self.charId]

        chat = random.choice(chatList)
        index = chatList.index(chat)
        return [sharedOrMine, index]

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterNeutral(self, pickRandomPoint = False):
        if self.talkEnabled:
            taskMgr.doMethodLater(random.uniform(*CHECK_FOR_PEEPS_RANGE), self.__neutralTask, self.uniqueName('neutralTask'))
        if self.charId != SAILOR_DONALD:
            self.startLonelyTask()
            if pickRandomPoint:
                self.currentPointLetter = random.choice(WALK_POINTS[self.charId].keys())
            self.sendUpdate('doNeutral', [self.currentPointLetter])

    def startLonelyTask(self):
        self.lonelyThreshold = random.randint(*TIMES_LONELY_RANGE)
        self.timesLonely = 0
        taskMgr.doMethodLater(random.uniform(*LONELY_TIME_RANGE), self.__lonelyTask, self.uniqueName('lonelyTask'))

    def __lonelyTask(self, task):
        # Nobody came over to talk to me.
        if self.isLonely() and not self.inConvo:
            self.timesLonely += 1
            if self.timesLonely >= self.lonelyThreshold:
                # Let's walk somewhere else -- maybe we'll find people there.
                self.fsm.request('walking')
                return task.done

        task.delayTime = random.uniform(*TIMES_LONELY_RANGE)
        return task.again

    def __neutralTask(self, task):
        if self.saidGoodbye:
            # Alright, let's go walking.
            self.fsm.request('walking')
            return task.done

        # Wait for peoples to walk over to me!
        if self.wantsToChat() and not self.inConvo:
            # Aye! We got someone!
            taskMgr.remove(self.uniqueName('lonelyTask'))
            # Let's pick a random person out of my huge circle of friends to talk to.
            peep = random.choice(self.avatars)
            self.toonOfInterest = peep
            # Let's greet them!
            chatType, index = self.chooseChat('greet')
            self.chatsThisConvo = 1
            # Alright, tell all of the DisneyChar clients to talk to this person!
            self.sendUpdate('talk2Toon', [chatType, index, peep])
            # Don't mess with me, i'm in a conversation!
            self.inConvo = True
            task.delayTime = random.uniform(*TALK_AGAIN_RANGE)
            return task.again
        elif self.inConvo:
            # Seems like they're still interested in me. Let's keep talking.
            if not self.toonOfInterest in self.avatars:
                # Wait, no they're not.
                if self.wantsToChat():
                    # Luckily I have more friends to talk to!
                    self.toonOfInterest = random.choice(self.avatars)
                elif self.isLonely():
                    # Wow, all my friends left me.
                    self.startLonelyTask()
                    self.inConvo = False
                    task.delayTime = random.uniform(*CHECK_FOR_PEEPS_RANGE)
                    return task.again

            if self.chatsThisConvo >= CHAT_THRESHOLD:
                # Ugh, i'm bored. Say goodbye and go for a walk.
                chatType, index = self.chooseChat('bye')
                self.sendUpdate('talk2Toon', [chatType, index, self.toonOfInterest])
                self.saidGoodbye = True
                task.delayTime = 3.0
                return task.again

            self.toonOfInterest = random.choice(self.avatars)

            # Say a random comment.
            chatType, index = self.chooseChat('comment')
            self.sendUpdate('talk2Toon', [chatType, index, self.toonOfInterest])
            self.chatsThisConvo += 1
            task.delayTime = random.uniform(*TALK_AGAIN_RANGE)
            return task.again

        task.delayTime = random.uniform(*CHECK_FOR_PEEPS_RANGE)
        return task.again

    def exitNeutral(self):
        taskMgr.remove(self.uniqueName('lonelyTask'))
        taskMgr.remove(self.uniqueName('neutralTask'))
        self.chatsThisConvo = 0
        self.toonOfInterest = 0
        self.saidGoodbye = False
        self.inConvo = False
        del self.lonelyThreshold
        del self.timesLonely

    def enterWalking(self):
        if self.currentPointLetter is None:
            # We haven't walked anywhere yet.
            # Choose any random point.
            point = random.choice(WALK_POINTS[self.charId].items())
            pointLetter = point[0]
            timeUntilNeutral = 1.0
        else:
            pointLetter = self.choosePoint()
            point = WALK_POINTS[self.charId][pointLetter][0]
            lastPoint = WALK_POINTS[self.charId][self.currentPointLetter][0]

            timeUntilNeutral = (point.getXy() - lastPoint.getXy()).length() * 0.2

            if self.charId == PLUTO:
                timeUntilNeutral += PLUTO_STANDUP_TIME
            elif self.charId == SLEEP_DONALD:
                timeUntilNeutral += SLEEP_DONALD_N2W_TIME

        taskMgr.doMethodLater(timeUntilNeutral, self.__walkingTask, self.uniqueName('walkingTask'))
        self.walkingTimestamp = globalClockDelta.getRealNetworkTime()
        self.sendUpdate('doWalking', [pointLetter, self.currentPointLetter, self.walkingTimestamp])
        self.lastPointLetter = self.currentPointLetter
        self.currentPointLetter = pointLetter

    def __walkingTask(self, task):
        self.fsm.request('neutral')
        return task.done

    def exitWalking(self):
        taskMgr.remove(self.uniqueName('walkingTask'))

    def choosePoint(self):
        accessiblePointIndices = WALK_POINTS[self.charId][self.currentPointLetter][1]
        return random.choice(accessiblePointIndices)

    def isLonely(self):
        return len(self.avatars) == 0

    def wantsToChat(self):
        return (self.fsm.getCurrentState().getName() == 'neutral' and len(self.avatars) > 0)

    def avatarEnter(self):
        avId = self.air.getAvatarIdFromSender()
        if not avId in self.avatars:
            toon = self.air.doId2do.get(avId)
            if toon:
                self.acceptOnce(toon.getDeleteEvent(), self.avatarExit)
                self.acceptOnce(toon.getZoneChangeEvent(), self.avatarExit, [avId])
                self.avatars.append(avId)

    def avatarExit(self, avId = None, foo = None, foo2 = None):
        print "avatar Exit"
        if avId == None:
            avId = self.air.getAvatarIdFromSender()
        if avId in self.avatars:
            toon = self.air.doId2do.get(avId)
            if toon:
                self.ignore(toon.getZoneChangeEvent())
                self.ignore(toon.getDeleteEvent())
            self.avatars.remove(avId)

    def announceGenerate(self):
        DistributedAvatarAI.announceGenerate(self)
        data = CHAR_DATA[self.charId]
        self.talkEnabled = data[4]
        self.fsm.request('neutral', [True])
예제 #19
0
class PlayGame(StateData):
    notify = directNotify.newCategory('PlayGame')

    Hood2HoodClass = {ZoneUtil.ToontownCentral: TTHood.TTHood,
                ZoneUtil.MinigameArea: MGHood.MGHood,
                ZoneUtil.TheBrrrgh: BRHood.BRHood,
                ZoneUtil.DonaldsDreamland: DLHood.DLHood,
                ZoneUtil.MinniesMelodyland: MLHood.MLHood,
                ZoneUtil.DaisyGardens: DGHood.DGHood,
                ZoneUtil.DonaldsDock: DDHood.DDHood,
                ZoneUtil.BattleTTC: CTCHood.CTCHood,
                ZoneUtil.CogTropolis: CTHood.CTHood}
    Hood2HoodState = {ZoneUtil.ToontownCentral: 'TTHood',
                ZoneUtil.MinigameArea: 'MGHood',
                ZoneUtil.TheBrrrgh: 'BRHood',
                ZoneUtil.DonaldsDreamland: 'DLHood',
                ZoneUtil.MinniesMelodyland: 'MLHood',
                ZoneUtil.DaisyGardens: 'DGHood',
                ZoneUtil.DonaldsDock: 'DDHood',
                ZoneUtil.BattleTTC: 'CTCHood',
                ZoneUtil.CogTropolis: 'CTHood'}

    def __init__(self, parentFSM, doneEvent):
        StateData.__init__(self, "playGameDone")
        self.doneEvent = doneEvent
        self.fsm = ClassicFSM('World', [State('off', self.enterOff, self.exitOff, ['quietZone']),
                State('quietZone', self.enterQuietZone, self.exitQuietZone, ['TTHood',
                    'BRHood', 'DLHood', 'MLHood', 'DGHood', 'DDHood', 'MGHood', 'CTCHood', 'CTHood']),
                State('TTHood', self.enterTTHood, self.exitTTHood, ['quietZone']),
                State('BRHood', self.enterBRHood, self.exitBRHood, ['quietZone']),
                State('DLHood', self.enterDLHood, self.exitDLHood, ['quietZone']),
                State('MLHood', self.enterMLHood, self.exitMLHood, ['quietZone']),
                State('DGHood', self.enterDGHood, self.exitDGHood, ['quietZone']),
                State('DDHood', self.enterDDHood, self.exitDDHood, ['quietZone']),
                State('MGHood', self.enterMGHood, self.exitMGHood, ['quietZone']),
                State('CTCHood', self.enterCTCHood, self.exitCTCHood, ['quietZone']),
                State('CTHood', self.enterCTHood, self.exitCTHood, ['quietZone'])],
                'off', 'off')
        self.fsm.enterInitialState()

        self.parentFSM = parentFSM
        self.parentFSM.getStateNamed('playGame').addChild(self.fsm)

        self.hoodDoneEvent = 'hoodDone'
        self.hood = None
        self.quietZoneDoneEvent = uniqueName('quietZoneDone')
        self.quietZoneStateData = None
        self.place = None
        self.lastHood = None
        self.suitManager = None

    def enter(self, hoodId, zoneId, avId):
        StateData.enter(self)
        whereName = ZoneUtil.getWhereName(zoneId)
        loaderName = ZoneUtil.getLoaderName(zoneId)
        self.fsm.request('quietZone', [{'zoneId': zoneId,
            'hoodId': hoodId,
            'where': whereName,
            'how': 'teleportIn',
            'avId': avId,
            'shardId': None,
            'loader': loaderName}])

    def exit(self):
        StateData.exit(self)

    def getCurrentWorldName(self):
        return self.fsm.getCurrentState().getName()

    def enterOff(self):
        pass

    def exitOff(self):
        pass
        
    def enterCTHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitCTHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.CogTropolis

    def enterCTCHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitCTCHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.ToontownCentral

    def enterDDHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitDDHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.DonaldsDock

    def enterDGHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitDGHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.DaisyGardens

    def enterMLHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitMLHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.MinniesMelodyland

    def enterDLHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitDLHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.DonaldsDreamland

    def enterBRHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitBRHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.TheBrrrgh

    def enterTTHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitTTHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.ToontownCentral

    def enterMGHood(self, requestStatus):
        self.accept(self.hoodDoneEvent, self.handleHoodDone)
        self.hood.enter(requestStatus)

    def exitMGHood(self):
        self.ignore(self.hoodDoneEvent)
        self.hood.exit()
        self.hood.unload()
        self.hood = None
        self.lastHood = ZoneUtil.MinigameArea

    def handleHoodDone(self):
        doneStatus = self.hood.getDoneStatus()
        if doneStatus['zoneId'] is None:
            self.doneStatus = doneStatus
            messenger.send(self.doneEvent)
        else:
            self.fsm.request('quietZone', [doneStatus])

    def loadDNAStore(self):
        if hasattr(self, 'dnaStore'):
            self.dnaStore.reset_nodes()
            self.dnaStore.reset_hood_nodes()
            self.dnaStore.reset_place_nodes()
            self.dnaStore.reset_hood()
            self.dnaStore.reset_fonts()
            self.dnaStore.reset_DNA_vis_groups()
            self.dnaStore.reset_materials()
            self.dnaStore.reset_block_numbers()
            self.dnaStore.reset_block_zones()
            self.dnaStore.reset_suit_points()
            del self.dnaStore

        self.dnaStore = DNAStorage()
        loadDNAFile(self.dnaStore, 'phase_4/dna/storage.pdna')
        self.dnaStore.storeFont('humanist', CIGlobals.getToonFont())
        self.dnaStore.storeFont('mickey', CIGlobals.getMickeyFont())
        self.dnaStore.storeFont('suit', CIGlobals.getSuitFont())
        loadDNAFile(self.dnaStore, 'phase_3.5/dna/storage_interior.pdna')

    def enterQuietZone(self, requestStatus):
        self.acceptOnce(self.quietZoneDoneEvent, self.handleQuietZoneDone, [requestStatus])
        self.acceptOnce('enteredQuietZone', self.handleEnteredQuietZone, [requestStatus])
        self.quietZoneStateData = QuietZoneState(self.quietZoneDoneEvent, 0)
        self.quietZoneStateData.load()
        self.quietZoneStateData.enter(requestStatus)

    def handleEnteredQuietZone(self, requestStatus):
        hoodId = requestStatus['hoodId']
        if self.Hood2HoodClass.has_key(hoodId):
            hoodClass = self.Hood2HoodClass[hoodId]
            base.transitions.noTransitions()
            loader.beginBulkLoad('hood', hoodId, 100)
            self.loadDNAStore()
            self.hood = hoodClass(self.fsm, self.hoodDoneEvent, self.dnaStore, hoodId)
            self.hood.load()

            hoodState = self.Hood2HoodState[hoodId]
            self.fsm.request(hoodState, [requestStatus], exitCurrent = 0)
        self.quietZoneStateData.fsm.request('waitForSetZoneResponse')

    def handleQuietZoneDone(self, requestStatus):
        if self.hood:
            self.hood.enterTheLoader(requestStatus)
            self.hood.loader.enterThePlace(requestStatus)
            loader.endBulkLoad('hood')
        self.exitQuietZone()

    def exitQuietZone(self):
        if self.quietZoneStateData:
            self.ignore('enteredQuietZone')
            self.ignore(self.quietZoneDoneEvent)
            self.quietZoneStateData.exit()
            self.quietZoneStateData.unload()
            self.quietZoneStateData = None

    def setPlace(self, place):
        self.place = place

    def getPlace(self):
        return self.place
class CogBrain(DirectObject):
    PANIC_SPEED = 0.15
    PANIC_DELAY = 0.5
    RUNAWAY_SPEED = 0.1
    RUNAWAY_SAFE_DISTANCE = 50
    MAX_BOSS_HELPERS = 5
    PANIC_HP_FACTOR = .222
    ATTACK_DISTANCE = 40.0
    MAX_ATTACKERS = 3
    Difficulty2MaxAttackThrows = {}
    for level in range(1, 4 + 1):
        Difficulty2MaxAttackThrows[level] = 3
    for level in range(5, 9 + 1):
        Difficulty2MaxAttackThrows[level] = 4
    for level in range(9, 12 + 1):
        Difficulty2MaxAttackThrows[level] = 5

    def __init__(self, suit):
        self.suit = suit
        self.panicHP = self.suit.getMaxHealth() * self.PANIC_HP_FACTOR
        self.fsm = ClassicFSM(
            'CogBrain',
            [
                State('off', self.enterOff, self.exitOff),
                State('neutral', self.enterNeutral, self.exitNeutral),
                State('followBoss', self.enterFollowBoss, self.exitFollowBoss),
                State('panic', self.enterPanic, self.exitPanic),
                State('runAway', self.enterRunAway, self.exitRunAway)
            ],
            'off', 'off'
        )
        self.fsm.enterInitialState()

    def start(self):
        taskMgr.add(self.__think, self.suit.uniqueName('think'))

    def end(self, andGoOff = 1):
        taskMgr.remove(self.suit.uniqueName('think'))
        if andGoOff:
            self.fsm.request('off')

    def __think(self, task = None):
        if task:
            task.delayTime = 1
        # Let's do some thinking!

        if self.suit.getAttacking():
            # Don't think when I attack.
            if task:
                return task.again
            else:
                return

        _help_priority = 0
        _panic_priority = 0
        _run_priority = 0

        # PRIORITY SCALE:
        # 5 or > = Top Priority
        # 4 = Very Important
        # 3 = Quite Important
        # 2 = Not really important
        # 1 = Not important

        # First things first, is there a boss I can help?
        _helper_suits = 0
        boss = None
        for av in self.suit.getManager().suits.values():
            if av.doId != self.suit.doId:
                if av.head in ["vp"]:
                    # Looks like there is! How many suits are already helping that boss?
                    boss = av
                    for suit in self.suit.getManager().suits.values():
                        if suit.doId != self.suit.doId:
                            if suit.brain:
                                if suit.brain.fsm.getCurrentState().getName() == "followBoss":
                                    # Alright, there's someone helping...
                                    _helper_suits += 1
        if _helper_suits == self.MAX_BOSS_HELPERS - 1:
            _help_priority = 2
        elif _helper_suits == self.MAX_BOSS_HELPERS - 2:
            _help_priority = 2.5
        elif _helper_suits == self.MAX_BOSS_HELPERS - 3:
            _help_priority = 3.5
        elif _helper_suits == self.MAX_BOSS_HELPERS - 4:
            _help_priority = 4
        elif _helper_suits == self.MAX_BOSS_HELPERS - 5:
            _help_priority = 4.5
        if boss == None or _helper_suits == self.MAX_BOSS_HELPERS:
            _help_priority = 0

        if self.fsm.getCurrentState().getName() == "followBoss":
            if self.bossSpotKey != boss.boss.spot:
                self.fsm.request('followBoss', [boss])
                return task.again

        # Let me check how many toons are near me...
        _toons_in_range = 0
        # What would I call: in range?
        in_range = 15 # 15 units near me
        for av in self.suit.air.doId2do.values():
            if av.__class__.__name__ == "DistributedToonAI":
                if av.zoneId == self.suit.zoneId:
                    if self.suit.getDistance(av) <= in_range:
                        # Yikes, someone's near me!
                        _toons_in_range += 1
        if self.fsm.getCurrentState().getName() == "followBoss":
            # I need to be tougher when defending my boss!
            _panic_priority = _toons_in_range / .85
        else:
            _panic_priority = _toons_in_range / .75

        if self.fsm.getCurrentState().getName() == "panic" and _toons_in_range > 0:
            # Wait, I'm panicking and toons are near me, RUN!
            _run_priority = 5

        # Next, how are we doing?
        if self.suit.getHealth() <= self.panicHP:
            # Oh no, I'm close to dying!
            # This is pretty important!

            # Don't make me less panicky if I'm panicked
            # more about something else.
            if _panic_priority < 4:
                _panic_priority = 4

        # Alright, let's decide what I should do.
        if _run_priority == 5:
            # 5 or 0 is the only priority running away can have.
            # If my running away priority is 5, I better do it!
            self.fsm.request('runAway', [av])
            del _help_priority
            del _panic_priority
            del _run_priority
            del _helper_suits
            del boss
            del in_range
            del _toons_in_range
            try:
                del av
            except:
                pass
            if task:
                return task.done
            else:
                return
        elif _panic_priority <= 2 and _help_priority <= 2:
            # Eh, nothing is really that important.
            # Let me just choose to help the boss or chill out.
            state_num = random.randint(0, 2)
            if state_num == 0 or state_num == 1:
                new_state = "neutral"
            else:
                new_state = "followBoss"
            if boss == None or _help_priority == 0:
                # There's no boss, just chill.
                if self.fsm.getCurrentState().getName() != "neutral":
                    self.fsm.request('neutral')
            else:
                new_state = "neutral"
                if self.fsm.getCurrentState().getName() != new_state:
                    if self.fsm.getCurrentState().getName() == "followBoss":
                        del _help_priority
                        del _panic_priority
                        del _run_priority
                        del _helper_suits
                        del boss
                        del in_range
                        del _toons_in_range
                        try:
                            del args
                        except:
                            pass
                        try:
                            del new_state
                        except:
                            pass
                        try:
                            del state_num
                        except:
                            pass
                        try:
                            del av
                        except:
                            pass

                        if task:
                            return task.again
                        else:
                            return
                    args = []
                    if new_state == 'followBoss':
                        args = [boss]
                    self.fsm.request(new_state, args)
        elif _panic_priority > _help_priority:
            # Who cares about my boss! Panic!!!
            if self.fsm.getCurrentState().getName() != "panic":
                self.fsm.request('panic')
                del _help_priority
                del _panic_priority
                del _run_priority
                del _helper_suits
                del boss
                del in_range
                del _toons_in_range
                try:
                    del args
                except:
                    pass
                try:
                    del new_state
                except:
                    pass
                try:
                    del state_num
                except:
                    pass
                try:
                    del av
                except:
                    pass

                if task:
                    return task.done
                else:
                    return
        elif _panic_priority < _help_priority:
            # The boss needs help! Help him!
            if self.fsm.getCurrentState().getName() != "followBoss":
                self.fsm.request('followBoss', [boss])
        elif _panic_priority == _help_priority:
            new_state = random.choice(['panic', 'followBoss'])
            if self.fsm.getCurrentState().getName() != new_state:
                args = []
                if new_state == "followBoss":
                    args = [boss]
                self.fsm.request(new_state, args)

        del _help_priority
        del _panic_priority
        del _run_priority
        del _helper_suits
        del boss
        del in_range
        del _toons_in_range
        try:
            del args
        except:
            pass
        try:
            del new_state
        except:
            pass
        try:
            del state_num
        except:
            pass
        try:
            del av
        except:
            pass

        if task:
            return task.again

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterNeutral(self):
        # Okay, just strollin' along, looking for some Toons to attack.
        self.suit.createPath(fromCurPos = True)
        self.numAttacksThrown = 0
        if not self.suit.isBackup():
            self.neutral_startLookingForToons()

    def neutral_startLookingForToons(self):
        taskMgr.add(self.__lookForToons, self.suit.uniqueName('lookForToon'))

    def neutral_stopLookingForToons(self):
        taskMgr.remove(self.suit.uniqueName('lookForToon'))

    def __lookForToons(self, task):
        # This is so we wont't have to do a bunch of calculations
        # when we won't even be attacking if we already are.
        if self.suit.isBackup() or not hasattr(self, 'numAttacksThrown'):
            return task.done
        if self.suit.getAttacking():
            task.delayTime = 1.0
            return task.again
        if self.numAttacksThrown >= self.Difficulty2MaxAttackThrows[self.suit.getLevel()]:
            self.numAttacksThrown = 0
            if not self.suit.isWalking():
                self.suit.createPath(path_key = self.suit.currentPath, fromCurPos = True)
            task.delayTime = 10
            return task.again
        closestToonOrTurret = None
        obj2range = {}
        for obj in base.air.doId2do.values():
            if obj.__class__.__name__ in ["DistributedToonAI", "DistributedPieTurretAI"]:
                if obj.zoneId == self.suit.zoneId:
                    if not obj.isDead():
                        if obj.__class__.__name__ == "DistributedToonAI":
                            # This helps with the stun lock problem.
                            if obj.getNumAttackers() < self.MAX_ATTACKERS:
                                dist = obj.getDistance(self.suit)
                                if dist <= self.ATTACK_DISTANCE:
                                    obj2range[obj] = dist
                        else:
                            dist = obj.getDistance(self.suit)
                            if dist <= self.ATTACK_DISTANCE:
                                obj2range[obj] = dist
        ranges = []
        for distance in obj2range.values():
            ranges.append(distance)
        ranges.sort()
        for obj in obj2range.keys():
            distance = obj2range[obj]
            if distance == ranges[0]:
                closestToonOrTurret = obj
        if closestToonOrTurret != None and not self.suit.getAttacking():
            if self.suit.head != "vp":
                if self.suit.walkTrack:
                    self.ignore(self.suit.walkTrack.getName())
                    self.suit.walkTrack.clearToInitial()
                    self.suit.walkTrack = None
            self.suit.b_setSuitState(3, -1, -1)
            self.suit.b_setAnimState("neutral")
            self.end(0)
            self.suit.headsUp(closestToonOrTurret)
            self.suit.attackToon(closestToonOrTurret)
            self.suit.setAttacking(True)
            if closestToonOrTurret.__class__.__name__ == "DistributedToonAI":
                closestToonOrTurret.addNewAttacker(self.suit.doId)
            self.numAttacksThrown += 1
            return task.done
        else:
            if self.numAttacksThrown > 0:
                if not self.suit.isWalking():
                    self.suit.createPath(path_key = self.suit.currentPath, fromCurPos = True)
            else:
                if not self.suit.isWalking():
                    self.suit.createPath(fromCurPos = True)
            self.numAttacksThrown = 0
        task.delayTime = 3.5
        return task.again

    def exitNeutral(self):
        self.neutral_stopLookingForToons()
        del self.numAttacksThrown

    def enterPanic(self):
        # Ahh, what's going on?! What do I do!? Help!
        taskMgr.add(self.__panic, self.suit.uniqueName('panic'))

    def __panic(self, task):
        self.suit.createPath(durationFactor = self.PANIC_SPEED, fromCurPos = True)
        if task.time == 2.0:
            self.__think(None)
        task.delayTime = self.PANIC_DELAY
        return task.again

    def exitPanic(self):
        taskMgr.remove(self.suit.uniqueName('panic'))

    def enterFollowBoss(self, boss):
        # I'll follow this boss, he can protect me, or maybe I can heal him up.
        self.boss = boss
        # Let me find out where this boss is...
        if boss.boss.spot == None:
            self.bossSpot = boss.getPos(render)
        else:
            self.bossSpot = CIGlobals.SuitSpawnPoints[self.suit.hood][boss.boss.spot]
        # Let me find my way there...
        if self.suit.currentPath == boss.boss.spot:
            # Wait a minute, we're already here! Walk straight to the boss.
            self.suit.createPath(path_key = boss.boss.spot, fromCurPos = True)
        else:
            self.suit.currentPathQueue = SuitPathFinder.find_path(self.suit.hood, self.suit.currentPath, boss.boss.spot)
            self.suit.currentPathQueue.remove(self.suit.currentPathQueue[0])
            # Okay, let's start walking the path!
            self.suit.createPath(fromCurPos = True)
        # I want to keep a little bit of a distance from the boss...
        taskMgr.add(self.__followBoss, self.suit.uniqueName('followBoss'))
        self.bossSpotKey = boss.boss.spot

    def __followBoss(self, task):
        if not self.boss in self.suit.getManager().suits.values():
            self.fsm.request('neutral')
            return task.done
        if self.suit.getDistance(self.boss) <= 4:
            # Okay, stop here!
            self.suit.b_setSuitState(3, -1, -1)
            if self.suit.walkTrack:
                self.suit.ignore(self.suit.walkTrack.getDoneEvent())
                self.suit.walkTrack.pause()
                self.suit.walkTrack = None
                self.suit.b_setAnimState("neutral")
                # I want to protect this boss, so I need to stand in front of him.
                self.suit.setH(self.suit.getH() - 180)
                self.suit.d_setH(self.suit.getH())
            return task.done
        return task.cont

    def exitFollowBoss(self):
        self.suit.resetPathQueue()
        taskMgr.remove(self.suit.uniqueName('followBoss'))
        del self.boss
        del self.bossSpot
        del self.bossSpotKey

    def enterRunAway(self, toon):
        # Get me away from this guy!
        self.toon = toon
        # Now, run!
        self.suit.createPath(durationFactor = self.RUNAWAY_SPEED, fromCurPos = True)
        # I'll only run until I'm a safe distance from them.
        taskMgr.add(self.__runAway, self.suit.uniqueName('runAway'))

    def __runAway(self, task):
        try:
            if self.suit.getDistance(self.toon) >= self.RUNAWAY_SAFE_DISTANCE:
                # Okay, we're safe!
                self.start()
                return task.done
            elif self.suit.walkTrack == None or self.suit.walkTrack.isStopped():
                # He's still on us, keep running!
                self.suit.createPath(durationFactor = self.RUNAWAY_SPEED, fromCurPos = True)
            return task.cont
        except:
            self.fsm.request('neutral')
            return task.done

    def exitRunAway(self):
        taskMgr.remove(self.suit.uniqueName('runAway'))
        del self.toon
예제 #21
0
class Toon(Avatar.Avatar, ToonHead, ToonDNA.ToonDNA):
    notify = directNotify.newCategory("Toon")

    def __init__(self, cr, mat=0):
        self.cr = cr
        try:
            self.Toon_initialized
            return
        except:
            self.Toon_initialized = 1
        Avatar.Avatar.__init__(self, mat)
        ToonDNA.ToonDNA.__init__(self)
        ToonHead.__init__(self, cr)
        self.collsSetup = False
        self.forwardSpeed = 0.0
        self.rotateSpeed = 0.0
        self.strafeSpeed = 0.0
        self.avatarType = CIGlobals.Toon
        self.track = None
        self.standWalkRunReverse = None
        self.playingAnim = None
        self.playingRate = None
        self.tag = None
        self.money = 0
        self.lookAtTrack = None
        self.portal1 = None
        self.portal2 = None
        self.spineA = NodePath()
        self.tokenIcon = None
        self.tokenIconIval = None
        self.fallSfx = base.audio3d.loadSfx(
            "phase_4/audio/sfx/MG_cannon_hit_dirt.ogg")
        base.audio3d.attachSoundToObject(self.fallSfx, self)
        self.eyes = loader.loadTexture("phase_3/maps/eyes.jpg",
                                       "phase_3/maps/eyes_a.rgb")
        self.myTaskId = random.uniform(0, 1231231232132131231232)
        self.closedEyes = loader.loadTexture("phase_3/maps/eyesClosed.jpg",
                                             "phase_3/maps/eyesClosed_a.rgb")
        self.soundChatBubble = loader.loadSfx(
            "phase_3/audio/sfx/GUI_balloon_popup.ogg")
        self.shadowCaster = None
        self.accessories = []
        self.chatSoundDict = {}
        self.backpack = None
        self.forceRunSpeed = False
        self.animFSM = ClassicFSM('Toon', [
            State('off', self.enterOff, self.exitOff),
            State('neutral', self.enterNeutral, self.exitNeutral),
            State('swim', self.enterSwim, self.exitSwim),
            State('walk', self.enterWalk, self.exitWalk),
            State('run', self.enterRun, self.exitRun),
            State('bow', self.enterBow, self.exitBow),
            State('openBook', self.enterOpenBook, self.exitOpenBook),
            State('readBook', self.enterReadBook, self.exitReadBook),
            State('closeBook', self.enterCloseBook, self.exitCloseBook),
            State('teleportOut', self.enterTeleportOut, self.exitTeleportOut),
            State('teleportIn', self.enterTeleportIn, self.exitTeleportIn),
            State('died', self.enterDied, self.exitDied),
            State('fallFWD', self.enterFallFWD, self.exitFallFWD),
            State('fallBCK', self.enterFallBCK, self.exitFallBCK),
            State('jump', self.enterJump, self.exitJump),
            State('leap', self.enterLeap, self.exitLeap),
            State('laugh', self.enterLaugh, self.exitLaugh),
            State('happy', self.enterHappyJump, self.exitHappyJump),
            State('shrug', self.enterShrug, self.exitShrug),
            State('hdance', self.enterHDance, self.exitHDance),
            State('wave', self.enterWave, self.exitWave),
            State('scientistEmcee', self.enterScientistEmcee,
                  self.exitScientistEmcee),
            State('scientistWork', self.enterScientistWork,
                  self.exitScientistWork),
            State('scientistGame', self.enterScientistGame,
                  self.exitScientistGame),
            State('scientistJealous', self.enterScientistJealous,
                  self.exitScientistJealous),
            State('cringe', self.enterCringe, self.exitCringe),
            State('conked', self.enterConked, self.exitConked),
            State('win', self.enterWin, self.exitWin),
            State('walkBack', self.enterWalkBack, self.exitWalkBack),
            State('deadNeutral', self.enterDeadNeutral, self.exitDeadNeutral),
            State('deadWalk', self.enterDeadWalk, self.exitDeadWalk),
            State('squish', self.enterSquish, self.exitSquish),
            State('Happy', self.enterHappy, self.exitHappy),
            State('Sad', self.enterSad, self.exitSad),
            State('Swim', self.enterSwim, self.exitSwim)
        ], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()

        if not hasattr(self, 'uniqueName'):
            self.uniqueName = types.MethodType(uniqueName, self)

        self.activities = {
            ACT_DIE: Die(self),
            ACT_VICTORY_DANCE: VictoryDance(self),
            ACT_TOON_BOW: Bow(self),
            ACT_JUMP: Jump(self)
        }

    def setActivity(self, act, timestamp=0):
        Avatar.Avatar.setActivity(self, act, timestamp)
        if act == ACT_NONE:
            self.animFSM.request("Happy")

    def getUpperBodySubpart(self):
        if self.getAnimal() == "dog":
            return ["torso-top", "head"]

        return ["torso-top"]

    def getLowerBodySubpart(self):
        return ["legs", "torso-pants"]

    def getRightHandNode(self):
        return self.find("**/def_joint_right_hold")

    def getLeftHandNode(self):
        return self.find("**/def_joint_left_hold")

    def getHeadNode(self):
        return self.getPart('head')

    def getEyePoint(self):
        # middle of the head
        return Point3(0, 0, self.getHeight() - (self.getHeadHeight() / 2.0))

    def setForceRunSpeed(self, flag):
        self.forceRunSpeed = flag

    def resetTorsoRotation(self):
        if not self.isEmpty():
            spine = self.find("**/def_spineB")
            if not spine.isEmpty():
                spine.setH(0)
                spine.detachNode()
                self.getPart("legs").setH(0)
                self.releaseJoint("torso", "def_spineB")

    def showAvId(self):
        pass

    def showName(self):
        pass

    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('legs', lodName)
            joint = bundle.findChild('joint_nameTag')
            if joint:
                joints.append(joint)

        return joints

    def enterHappy(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = None
        self.standWalkRunReverse = (('neutral', 1.0), ('walk', 1.0),
                                    ('run', 1.0), ('walk', -1.0),
                                    ('strafe', 1.0), ('strafe', -1.0))
        self.setSpeed(self.forwardSpeed, self.rotateSpeed)

    def exitHappy(self):
        self.standWalkRunReverse = None
        self.stop()

    def enterSad(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'sad'
        self.standWalkRunReverse = (('dneutral', 1.0), ('dwalk', 1.2),
                                    ('dwalk', 1.2), ('dwalk', -1.0))
        self.setSpeed(0, 0)

    def exitSad(self):
        self.standWalkRunReverse = None
        self.stop()
        #if hasattr(self, 'doId'):
        #    if hasattr(base, 'localAvatar'):
        #        if base.localAvatar.doId == self.doId:
        #            self.controlManager.enableAvatarJump()

    def setSpeed(self, forwardSpeed, rotateSpeed, strafeSpeed=0.0):
        if self.forceRunSpeed:
            forwardSpeed = CIGlobals.RunCutOff
        self.forwardSpeed = forwardSpeed
        self.rotateSpeed = rotateSpeed
        self.strafeSpeed = strafeSpeed
        action = None
        if self.standWalkRunReverse != None:

            rotateCutOff = CIGlobals.RotateCutOff if not self.isLocalAvatar(
            ) else CIGlobals.WalkCutOff

            if strafeSpeed < CIGlobals.StrafeCutOff and strafeSpeed > -CIGlobals.StrafeCutOff:
                self.resetTorsoRotation()

            if forwardSpeed >= CIGlobals.RunCutOff:
                action = CIGlobals.RUN_INDEX
            elif forwardSpeed > CIGlobals.WalkCutOff:
                action = CIGlobals.WALK_INDEX
            elif forwardSpeed < -CIGlobals.WalkCutOff:
                action = CIGlobals.REVERSE_INDEX
            elif abs(rotateSpeed) > rotateCutOff:
                action = CIGlobals.WALK_INDEX
            elif abs(strafeSpeed) > CIGlobals.RunCutOff:
                action = CIGlobals.RUN_INDEX
            elif abs(strafeSpeed) > CIGlobals.WalkCutOff:
                action = CIGlobals.WALK_INDEX
            else:
                action = CIGlobals.STAND_INDEX

            if abs(strafeSpeed) > CIGlobals.WalkCutOff:
                spine = self.find("**/def_spineB")

                if spine.isEmpty():
                    spine = self.controlJoint(None, "torso", "def_spineB")

                movementVec = Vec3(strafeSpeed, forwardSpeed, 0)
                movementVec.normalize()
                movementAngle = rad2Deg(
                    math.atan2(movementVec[1], movementVec[0])) - 90.0

                if action == CIGlobals.REVERSE_INDEX:
                    movementAngle -= 180

                spine.setH(-movementAngle)
                self.getPart('legs').setH(movementAngle)

            anim, rate = self.standWalkRunReverse[action]
            if anim != self.playingAnim or rate != self.playingRate or self.forcedTorsoAnim != self.lastForcedTorsoAnim:
                self.playingAnim = anim
                self.playingRate = rate
                self.lastForcedTorsoAnim = self.forcedTorsoAnim

                if self.forcedTorsoAnim is None:
                    self.loop(anim)
                else:

                    # Whatever happens to the legs should also happen on the pants.
                    self.loop(anim, partName='torso-pants')
                    self.loop(anim, partName='legs')
                self.setPlayRate(rate, anim)
        return action

    def enterSquish(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'squish'
        sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.ogg')
        lerpTime = 0.1
        node = self.getGeomNode().getChild(0)
        origScale = node.getScale()
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('getSquished')
        else:
            name = 'getSquished'
        self.track = Sequence(LerpScaleInterval(node,
                                                lerpTime,
                                                VBase3(2, 2, 0.025),
                                                blendType='easeInOut'),
                              Wait(1.0),
                              Parallel(
                                  Sequence(
                                      Wait(0.4),
                                      LerpScaleInterval(node,
                                                        lerpTime,
                                                        VBase3(1.4, 1.4, 1.4),
                                                        blendType='easeInOut'),
                                      LerpScaleInterval(node,
                                                        lerpTime / 2.0,
                                                        VBase3(0.8, 0.8, 0.8),
                                                        blendType='easeInOut'),
                                      LerpScaleInterval(
                                          node,
                                          lerpTime / 3.0,
                                          origScale,
                                          blendType='easeInOut')),
                                  ActorInterval(self, 'happy', startTime=0.2),
                                  SoundInterval(sound)),
                              name=name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.squishDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.start(ts)

    def squishDone(self, callback=None, extraArgs=[]):
        self.__doCallback(callback, extraArgs)

    def exitSquish(self):
        if self.track:
            self.ignore(self.track.getName())
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track.finish()
            self.track = None
        self.playingAnim = 'neutral'

    def enterDeadNeutral(self, ts=0, callback=None, extraArgs=[]):
        self.loop('dneutral')

    def exitDeadNeutral(self):
        self.stop()

    def enterDeadWalk(self, ts=0, callback=None, extraArgs=[]):
        self.loop('dwalk')

    def exitDeadWalk(self):
        self.stop()

    def setBackpack(self, pack):
        self.backpack = pack

    def getGhost(self):
        return 0

    def updateChatSoundDict(self):
        self.chatSoundDict['exclaim'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('exclaim'))
        self.chatSoundDict['question'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('question'))
        self.chatSoundDict['short'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('short'))
        self.chatSoundDict['medium'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('med'))
        self.chatSoundDict['long'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('long'))
        self.chatSoundDict['howl'] = base.audio3d.loadSfx(
            self.getToonAnimalNoise('howl'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['exclaim'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['question'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['short'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['medium'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['long'],
                                         self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['howl'],
                                         self.getPart('head'))

    def __actAsGone(self):
        if self.nametag3d:
            self.nametag3d.hide()
        if self.getShadow():
            self.getShadow().hide()
        if self.tokenIcon:
            self.tokenIcon.hide()
        #self.stashBodyCollisions()

    def __restoreHide(self):
        if self.tokenIcon:
            self.tokenIcon.show()
        if self.getShadow():
            self.getShadow().show()
        if self.nametag3d:
            self.nametag3d.show()
        if self.getGeomNode():
            self.getGeomNode().setTransparency(False)
            self.getGeomNode().setAlphaScale(1.0)
            self.getGeomNode().show()
        #self.unstashBodyCollisions()

    def handleGhost(self, flag):
        alpha = 1.0 if not flag else 0.25
        local = self == base.localAvatar
        if flag:
            if self.getAccessLevel() >= base.localAvatar.getAccessLevel():
                # Other staff members at this access level or higher should
                # be able to see this avatar still.
                alpha = 0.25
                #self.stashBodyCollisions()
            elif not local:
                self.getGeomNode().setTransparency(True)
                self.getGeomNode().setColorScale(1.0, 1.0, 1.0, 0.0)
                self.__actAsGone()
        else:
            self.__restoreHide()
        if local:
            self.getGeomNode().setTransparency(flag)
            self.getGeomNode().setColorScale(1.0, 1.0, 1.0, alpha)

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                self.notify.warning(
                    "animFSM in flux, state=%s, not requesting off" %
                    self.animFSM.getCurrentState().getName())
        else:
            self.notify.warning("animFSM has been deleted")
        if self.track != None:
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        return

    def disable(self):
        try:
            self.Toon_disabled
        except:
            self.Toon_disabled = 1
            self.ignoreAll()
            self.backpack = None
            self.collsSetup = False
            self.stopAnimations()
            self.removeAdminToken()
            ToonHead.delete(self)
            self.deleteCurrentToon()
            self.chatSoundDict = {}
            Avatar.Avatar.disable(self)

    def delete(self):
        try:
            self.Toon_deleted
        except:
            self.Toon_deleted = 1
            del self.animFSM
            self.forwardSpeed = None
            self.chatSoundDict = None
            self.rotateSpeed = None
            self.avatarType = None
            self.track = None
            self.standWalkRunReverse = None
            self.currentAnim = None
            self.toon_head = None
            self.forcedTorsoAnim = None
            self.lastForcedTorsoAnim = None
            self.toon_torso = None
            self.toon_legs = None
            self.gender = None
            self.headtype = None
            self.head = None
            self.legtype = None
            self.torsotype = None
            self.hr = None
            self.hg = None
            self.hb = None
            self.tr = None
            self.tg = None
            self.tb = None
            self.lr = None
            self.lg = None
            self.lb = None
            self.shir = None
            self.shig = None
            self.shib = None
            self.shor = None
            self.shog = None
            self.shob = None
            self.shirt = None
            self.sleeve = None
            self.short = None
            self.tag = None
            self.money = None
            self.lookAtTrack = None
            self.portal1 = None
            self.portal2 = None
            self.backpack = None
            self.fallSfx = None
            self.eyes = None
            self.myTaskId = None
            self.closedEyes = None
            self.soundChatBubble = None
            self.lastAction = None
            self.lastState = None
            self.playingAnim = None
            self.playingRate = None
            self.accessories = None
            Avatar.Avatar.delete(self)
        return

    def initCollisions(self):
        self.collNodePath.setCollideMask(BitMask32(0))
        self.collNodePath.node().setFromCollideMask(CIGlobals.WallBitmask)

        pusher = CollisionHandlerPusher()
        pusher.setInPattern("%in")
        pusher.addCollider(self.collNodePath, self)
        base.cTrav.addCollider(self.collNodePath, pusher)

    def deleteCurrentToon(self):
        if self.shadowCaster:
            self.shadowCaster.clear()
            self.shadowCaster = None

        for accessory in self.accessories:
            accessory.removeNode()
        self.accessories = []

        self.pupils = []

        if 'head' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['head']
        if 'torso' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['torso']
        if 'legs' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['legs']

        self.deleteShadow()
        self.removePart('head')
        self.removePart('torso')
        self.removePart('legs')

        self.clearPythonData()
        self.flush()

    def setAdminToken(self, tokenInstance):

        if tokenInstance:
            matPath = tokenInstance.getMaterialPath()
            self.tokenIcon = loader.loadModel(
                "phase_3/models/props/staffIcon.bam")
            self.tokenIcon.reparentTo(self)
            self.tokenIcon.setScale(0.75)
            self.tokenIcon.setShaderAuto()

            # Let's update the material.
            self.tokenIcon.setBSPMaterial(matPath, 1)

            # Let's position the icon above the nametag.
            x, y, z = self.nametag3d.getPos()
            self.tokenIcon.setPos(Vec3(x, y, z + self.tokenIcon.getSz()))

            r, g, b, _ = tokenInstance.getColor()

            # Let's add the glow.
            glow = loader.loadModel(
                'phase_4/models/minigames/particleGlow.bam')
            glow.reparentTo(self.tokenIcon)
            glow.setScale(2.50)
            glow.setColorScale((r, g, b, 0.50), 1)
            glow.setBSPMaterial('phase_4/maps/particleGlow.mat', 1)
            glow.setDepthWrite(False, 1)
            glow.setShaderAuto()
            glow.setTwoSided(1)

            self.tokenIconIval = Sequence(
                LerpHprInterval(self.tokenIcon,
                                duration=3.0,
                                hpr=Vec3(360, 0, 0),
                                startHpr=Vec3(0, 0, 0)))
            self.tokenIconIval.loop()
        else:
            self.removeAdminToken()

    def removeAdminToken(self):
        if self.tokenIcon != None and self.tokenIconIval != None:
            self.tokenIconIval.finish()
            self.tokenIcon.removeNode()
            self.tokenIconIval = None
            self.tokenIcon = None

    def playChatSfx(self, chatString):
        if not self.getGhost() or self.doId == base.localAvatar.doId:
            if "ooo" in chatString.lower():
                sfx = self.chatSoundDict['howl']
            elif "!" in chatString.lower():
                sfx = self.chatSoundDict['exclaim']
            elif "?" in chatString.lower():
                sfx = self.chatSoundDict['question']
            elif len(chatString) <= 9:
                sfx = self.chatSoundDict['short']
            elif 10 <= len(chatString) <= 19:
                sfx = self.chatSoundDict['medium']
            elif len(chatString) >= 20:
                sfx = self.chatSoundDict['long']
            base.playSfx(sfx, node=self)

    def chatStompComplete(self, chatString):
        if not self.thoughtInProg and CIGlobals.getSettingsMgr().getSetting(
                "chs").getValue():
            self.playChatSfx(chatString)

    def setName(self, nameString):
        Avatar.Avatar.setName(self, nameString)

    def setDNAStrand(self, dnaStrand, makeTag=1):
        ToonDNA.ToonDNA.setDNAStrand(self, dnaStrand)
        self.deleteCurrentToon()
        self.generateToon(makeTag)

    def generateMask(self):
        # No accessories yet.

        if self.shirt == self.maleTopDNA2maleTop['135'][
                0] or self.shirt == self.maleTopDNA2maleTop['136'][0]:
            # This toon is wearing the tsa suit, give them some sweet shades.
            name = 'tsaGlasses'
            glasses = loader.loadModel(
                AccessoryGlobals.AccessoryName2Model[name])
            glassesNode = self.getPart('head').attachNewNode('glassesNode')
            glasses.reparentTo(glassesNode)
            data = AccessoryGlobals.MaskTransExtended[name].get(self.animal)
            if not data:
                data = AccessoryGlobals.MaskTrans.get(self.animal)
                posHprScale = AccessoryGlobals.MaskTrans[self.animal][
                    self.headLength]
            else:
                posHprScale = AccessoryGlobals.MaskTransExtended[name][
                    self.animal].get(self.headLength)
                if not posHprScale:
                    posHprScale = AccessoryGlobals.MaskTrans[self.animal][
                        self.headLength]

            glasses.setPos(posHprScale[0])
            glasses.setHpr(posHprScale[1])
            glasses.setScale(posHprScale[2])

            self.accessories.append(glassesNode)

    def generateToon(self, makeTag=1):
        self.generateLegs()
        self.generateTorso()
        self.generateHead()
        self.setToonColor()
        self.setClothes()
        self.setGloves()
        self.parentToonParts()
        self.rescaleToon()
        self.generateMask()

        # Make torso subparts so we can play a run animation on the pants but another animation on the spine and arms.
        if self.gender == 'boy':
            self.makeSubpart("torso-pants", [
                "def_left_pant_bottom", "def_left_pant_top",
                "def_right_pant_bottom", "def_right_pant_top"
            ],
                             parent="torso")
        elif self.gender == 'girl':
            if self.torso == 'dgs_skirt':
                self.makeSubpart("torso-pants", [
                    "def_left_skirt_backA", "def_left_skirt_frontA",
                    "def_left_skirt_topA", "def_right_skirt_backA",
                    "def_right_skirt_frontA", "def_right_skirt_topA"
                ],
                                 parent="torso")
            elif self.torso == 'dgl_skirt':
                self.makeSubpart("torso-pants", [
                    "def_left_skirt_bottomA", "def_left_skirt_topA",
                    "def_right_hip"
                ],
                                 parent="torso")
            else:
                self.makeSubpart("torso-pants", [
                    "def_left_skirt_bottomA", "def_left_skirt_topA",
                    "def_right_skirt_bottomA", "def_right_skirt_topA"
                ],
                                 parent="torso")
        self.makeSubpart("torso-top", ["def_spineB"], parent="torso")

        Avatar.Avatar.initShadow(self)

        self.updateChatSoundDict()
        self.setBlend(frameBlend=True)

        bodyMat = CIGlobals.getCharacterMaterial(shininess=5,
                                                 specular=(0.5, 0.5, 0.5, 1))
        self.setMaterial(bodyMat, 1)

        if not hasattr(base, 'localAvatar') or base.localAvatar != self:
            self.setupPhysics(1.0, self.getHeight())

        # We can safely optimize the scene graph and combine nodes since we're done manipulating
        # the separate pieces. After this point, the separate pieces of the toon are no
        # longer manipulatable, such as arms, sleeves, shirt, etc. If this needs to be done,
        # the toon will have to be regenerated.

        # Don't do it in Make-A-Toon though, as we have to be constantly modifying the pieces.
        if not self.mat:
            self.optimize()

        if makeTag:
            self.setupNameTag()
        if self.cr.isShowingPlayerIds:
            self.showAvId()

        self.loop('neutral')

    def optimize(self):
        self.getPart('legs').flattenStrong()
        self.postFlatten()

    def attachTNT(self):
        self.pies.attachTNT()
        self.holdTNTAnim()

    def detachTNT(self):
        self.pies.detachTNT()
        self.animFSM.request(self.animFSM.getCurrentState().getName())

    def holdTNTAnim(self):
        self.pose("toss", 22, partName="torso")

    def parentToonParts(self):
        self.attach('head', 'torso', 'def_head')
        self.attach('torso', 'legs', 'joint_hips')

    def unparentToonParts(self):
        self.getPart('head').reparentTo(self.getGeomNode())
        self.getPart('torso').reparentTo(self.getGeomNode())
        self.getPart('legs').reparentTo(self.getGeomNode())

    def getHeadHeight(self):
        animal = self.getAnimal()
        headScale = ToonGlobals.HeadScales[animal][2]
        headHeight = ToonGlobals.HeadHeightDict[self.head] * headScale
        return headHeight

    def rescaleToon(self):
        if not self.getHead():
            return

        animal = self.getAnimal()
        bodyScale = ToonGlobals.BodyScales[animal]
        headScale = ToonGlobals.HeadScales[animal][2]
        shoulderHeight = ToonGlobals.LegHeightDict[
            self.getLegs()] * bodyScale + ToonGlobals.TorsoHeightDict[
                self.getTorso()] * bodyScale
        height = shoulderHeight + ToonGlobals.HeadHeightDict[
            self.getHead()] * headScale
        bodyScale = ToonGlobals.BodyScales[animal]
        self.setAvatarScale(bodyScale)
        self.getPart('head').setScale(headScale)
        self.setHeight(height)

    def setGloves(self):
        color = self.getGloveColor()
        gloves = self.find('**/hands')
        gloves.setColor(color)

    def setClothes(self):
        shirt, shirtcolor = self.getShirtStyle()
        short, shortcolor = self.getShortStyle()
        sleeve, sleevecolor = self.getSleeveStyle()
        torsot = self.findAllMatches('**/torso-top')
        torsob = self.findAllMatches('**/torso-bot')
        sleeves = self.findAllMatches('**/sleeves')
        torsot.setBSPMaterial(shirt, 1)
        torsob.setBSPMaterial(short, 1)
        sleeves.setBSPMaterial(sleeve, 1)
        torsot.setColor(shirtcolor)
        sleeves.setColor(sleevecolor)
        torsob.setColor(shortcolor)

    def generateLegs(self):
        ToonGlobals.generateBodyPart(self, 'legs', self.getLegs(), 3, 'shorts')
        self.find('**/boots_long').stash()
        self.find('**/boots_short').stash()
        self.find('**/shoes').stash()

    def generateTorso(self):
        ToonGlobals.generateBodyPart(self, 'torso', self.getTorso(), 3, '')

    def generateHead(self, pat=0):
        gender = self.getGender()
        head = self.getAnimal()
        headtype = self.getHead()
        ToonHead.generateHead(self, gender, head, headtype)

    def setToonColor(self):
        self.setHeadColor()
        self.setTorsoColor()
        self.setLegColor()

    def setLegColor(self):
        legcolor = self.getLegColor()
        self.findAllMatches('**/legs').setColor(legcolor)
        self.findAllMatches('**/feet').setColor(legcolor)

    def setTorsoColor(self):
        torsocolor = self.getTorsoColor()
        self.findAllMatches('**/arms').setColor(torsocolor)
        self.findAllMatches('**/neck').setColor(torsocolor)
        self.findAllMatches('**/hands').setColor(1, 1, 1, 1)

    def enterOff(self, ts=0, callback=None, extraArgs=[]):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterWin(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'win'
        self.sfx = base.audio3d.loadSfx("phase_3.5/audio/sfx/ENC_Win.ogg")
        self.sfx.setLoop(True)
        base.audio3d.attachSoundToObject(self.sfx, self)
        base.playSfx(self.sfx, node=self, looping=1)
        self.loop("win")

    def exitWin(self):
        self.stop()
        self.sfx.stop()
        del self.sfx
        self.playingAnim = 'neutral'

    def enterShrug(self, ts=0, callback=None, extraArgs=[]):
        self.play("shrug")

    def exitShrug(self):
        self.exitGeneral()

    def enterHDance(self, ts=0, callback=None, extraArgs=[]):
        self.play("hdance")

    def exitHDance(self):
        self.exitGeneral()

    def enterScientistWork(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scwork")

    def exitScientistWork(self):
        self.exitGeneral()

    def enterScientistEmcee(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scemcee")

    def exitScientistEmcee(self):
        self.exitGeneral()

    def enterScientistGame(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scgame")

    def exitScientistGame(self):
        self.exitGeneral()

    def enterScientistJealous(self, ts=0, callback=None, extraArgs=[]):
        self.loop("scjealous")

    def exitScientistJealous(self):
        self.exitGeneral()

    def enterWave(self, ts=0, callback=None, extraArgs=[]):
        self.play("wave")

    def exitWave(self):
        self.exitGeneral()

    def enterLaugh(self, ts=0, callback=None, extraArgs=[]):
        self.setPlayRate(5.0, "neutral")
        self.loop("neutral")

    def exitLaugh(self):
        self.setPlayRate(1.0, "neutral")
        self.stop()

    def enterNeutral(self, ts=0, callback=None, extraArgs=[]):
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop("neutral", partName="legs")
            return
        self.loop("neutral")
        self.playingAnim = 'neutral'

    def exitNeutral(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def exitGeneral(self):
        self.stop()

    def enterRun(self, ts=0, callback=None, extraArgs=[]):
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('run', partName='legs')
            return
        self.loop("run")

    def exitRun(self):
        self.exitGeneral()

    def enterWalk(self, ts=0, callback=None, extraArgs=[]):
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('walk', partName='legs')
            return
        self.loop('walk')

    def exitWalk(self):
        self.exitGeneral()

    def enterWalkBack(self, ts=0, callback=None, extraArgs=[]):
        self.setPlayRate(-1.0, "walk")
        self.enterWalk()

    def exitWalkBack(self):
        self.exitWalk()
        self.setPlayRate(1.0, "walk")

    def enterOpenBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book1 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book1.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self,
                                   "book",
                                   startFrame=CIGlobals.OpenBookFromFrame,
                                   endFrame=CIGlobals.OpenBookToFrame,
                                   name=self.uniqueName('enterOpenBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback,
                        [callback, extraArgs])
        self.track.start(ts)
        self.book1.play("chan",
                        fromFrame=CIGlobals.OpenBookFromFrame,
                        toFrame=CIGlobals.OpenBookToFrame)

    def exitOpenBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book1:
            self.book1.cleanup()
            self.book1 = None
        self.playingAnim = 'neutral'

    def enterReadBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book2 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book2.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))

        self.pingpong("book",
                      fromFrame=CIGlobals.ReadBookFromFrame,
                      toFrame=CIGlobals.ReadBookToFrame)
        self.book2.pingpong("chan",
                            fromFrame=CIGlobals.ReadBookFromFrame,
                            toFrame=CIGlobals.ReadBookToFrame)

    def exitReadBook(self):
        if self.book2:
            self.book2.cleanup()
            self.book2 = None
        self.playingAnim = 'neutral'

    def enterCloseBook(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'book'
        self.book3 = Actor("phase_3.5/models/props/book-mod.bam",
                           {"chan": "phase_3.5/models/props/book-chan.bam"})
        self.book3.reparentTo(
            self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self,
                                   "book",
                                   startFrame=CIGlobals.CloseBookFromFrame,
                                   endFrame=CIGlobals.CloseBookToFrame,
                                   name=self.uniqueName('enterCloseBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback,
                        [callback, extraArgs])
        self.track.start(ts)
        self.book3.play("chan",
                        fromFrame=CIGlobals.CloseBookFromFrame,
                        toFrame=CIGlobals.CloseBookToFrame)
        self.lerpLookAt(self.getPart('head'), (0, 0, 0))

    def exitCloseBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book3:
            self.book3.cleanup()
            self.book3 = None
        self.playingAnim = 'neutral'

    def enterTeleportOut(self, ts=0, callback=None, extraArgs=[]):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": enterTeleportOut")
        self.playingAnim = 'tele'
        self.portal1 = Actor(
            "phase_3.5/models/props/portal-mod.bam",
            {"chan": "phase_3.5/models/props/portal-chan.bam"})
        self.portal1.play("chan")
        self.portal1.reparentTo(
            self.getPart('legs').find('**/def_joint_right_hold'))
        self.play("tele")
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterTeleportOut')
        else:
            name = 'enterTeleportOut'

        self.track = Sequence(Wait(0.4),
                              Func(self.teleportOutSfx),
                              Wait(1.3),
                              Func(self.throwPortal),
                              Wait(1.1),
                              Func(self.__actAsGone),
                              Wait(1.5),
                              name=name)

        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportOutDone,
                        [callback, extraArgs])
        self.track.start(ts)

    def doPortalBins(self, portal):
        portal.setBin('portal', 19)
        portal.setDepthWrite(0)
        portal.setDepthTest(0)

    def teleportOutDone(self, callback, requestStatus):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": teleportOutDone")
        self.__doCallback(callback, requestStatus)
        self.exitTeleportOut()

    def teleportOutSfx(self):
        self.outSfx = base.audio3d.loadSfx(
            "phase_3.5/audio/sfx/AV_teleport.ogg")
        base.audio3d.attachSoundToObject(self.outSfx, self.portal1)
        base.playSfx(self.outSfx, node=self)

    def throwPortal(self):
        self.doPortalBins(self.portal1)
        self.portal1.reparentTo(self.getPart('legs').find('**/joint_nameTag'))
        self.portal1.setScale(CIGlobals.PortalScale)
        self.portal1.setY(6.5)
        self.portal1.setH(180)

    def exitTeleportOut(self):
        self.notify.info(
            str(self.doId) + "-" + str(self.zoneId) + ": exitTeleportOut")
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal1:
            self.portal1.cleanup()
            self.portal1 = None
        if hasattr(self, 'shadow') and self.shadow is not None:
            self.shadow.show()
        self.__restoreHide()
        self.playingAnim = 'neutral'

    def getTeleportInTrack(self, portal):
        self.doPortalBins(portal)

        holeTrack = Sequence()
        holeTrack.append(Func(portal.reparentTo, self))
        pos = Point3(0, -2.4, 0)
        holeTrack.append(Func(portal.setPos, pos))
        holeTrack.append(
            ActorInterval(portal, 'chan', startTime=3.4, endTime=3.1))
        holeTrack.append(Wait(0.6))
        holeTrack.append(
            ActorInterval(portal, 'chan', startTime=3.1, endTime=3.4))

        def restorePortal(portal):
            portal.setPos(0, 0, 0)
            portal.detachNode()
            portal.clearBin()
            portal.clearDepthTest()
            portal.clearDepthWrite()

        holeTrack.append(Func(restorePortal, portal))
        toonTrack = Sequence(Wait(0.3), Func(self.__restoreHide),
                             ActorInterval(self, 'happy', startTime=0.45))

        if hasattr(self, 'uniqueName'):
            trackName = self.uniqueName('teleportIn')
        else:
            trackName = 'teleportIn'
        return Parallel(toonTrack, holeTrack, name=trackName)

    def enterTeleportIn(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'happy'
        self.portal2 = Actor(
            "phase_3.5/models/props/portal-mod.bam",
            {"chan": "phase_3.5/models/props/portal-chan.bam"})
        self.show()
        self.getGeomNode().hide()
        self.nametag3d.hide()
        self.track = self.getTeleportInTrack(self.portal2)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportInDone,
                        [callback, extraArgs])
        if hasattr(self, 'acquireDelayDelete'):
            self.track.delayDelete = DelayDelete.DelayDelete(
                self, self.track.getName())
        self.track.start(ts)

    def teleportInDone(self, callback, extraArgs):
        self.exitTeleportIn()
        self.__doCallback(callback, extraArgs)

    def exitTeleportIn(self):
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal2:
            self.portal2.cleanup()
            self.portal2 = None
        if self.nametag3d:
            self.nametag3d.show()
        self.playingAnim = 'neutral'

    def enterFallFWD(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'fallf'
        self.play("fallf")
        Sequence(Wait(0.5), SoundInterval(self.fallSfx, node=self)).start()

    def exitFallFWD(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterFallBCK(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'fallb'
        self.play("fallb")
        Sequence(Wait(0.5), SoundInterval(self.fallSfx, node=self)).start()

    def exitFallBCK(self):
        self.playingAnim = 'neutral'
        self.exitGeneral()

    def enterHappyJump(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'happy'
        self.play("happy")

    def exitHappyJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterSwim(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'swim'
        self.loop("swim")

        self.resetTorsoRotation()

        toon = self.getGeomNode()
        toon.setP(-89.0)

        if self.shadow:
            self.shadow.hide()

        self.swimBobTrack = Sequence(
            LerpPosInterval(toon,
                            duration=1,
                            pos=(0, -3, 3),
                            startPos=(0, -3, 4),
                            blendType='easeInOut'),
            LerpPosInterval(toon,
                            duration=1,
                            pos=(0, -3, 4),
                            startPos=(0, -3, 3),
                            blendType='easeInOut'))
        self.swimBobTrack.loop()
        self.nametag3d.setZ(5.0)

    def exitSwim(self):
        self.swimBobTrack.finish()
        del self.swimBobTrack
        if self.shadow:
            self.shadow.show()
        self.exitGeneral()
        self.getGeomNode().setPosHpr(0, 0, 0, 0, 0, 0)
        nt = self.nametag3d
        nt.setX(0)
        nt.setY(0)
        nt.setZ(self.getHeight() + 0.5)
        self.playingAnim = 'neutral'

    def enterDied(self, ts=0, callback=None, extraArgs=[]):
        def shouldDisableGags():
            if hasattr(self, 'disableGags'):
                self.disableGags()
            if hasattr(self, 'setEquippedAttack'):
                self.setEquippedAttack(-1)

        self.playingAnim = 'lose'
        self.isdying = True
        self.play("lose")
        self.track = Sequence(Func(self.clearForcedTorsoAnim),
                              Func(shouldDisableGags),
                              Wait(2.2),
                              Func(self.dieSfx),
                              Wait(2.8),
                              self.getGeomNode().scaleInterval(
                                  2,
                                  Point3(0.01),
                                  startScale=(self.getGeomNode().getScale())),
                              Func(self.delToon),
                              name=self.uniqueName('enterDied'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.diedDone,
                        [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(
            self, 'enterTeleportOut')
        self.track.start(ts)

    def diedDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitDied()

    def __doCallback(self, callback, extraArgs):
        if callback:
            if extraArgs:
                callback(*extraArgs)
            else:
                callback()

    def dieSfx(self):
        self.Losesfx = base.audio3d.loadSfx("phase_5/audio/sfx/ENC_Lose.ogg")
        base.audio3d.attachSoundToObject(self.Losesfx, self)
        base.playSfx(self.Losesfx, node=self)

    def delToon(self):
        self.isdead = True

    def exitDied(self):
        if self.track != None:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if hasattr(self, 'enableGags'):
            self.enableGags()

        self.rescaleToon()
        self.playingAnim = 'neutral'

    def enterBow(self, ts=0, callback=None, extraArgs=[]):
        self.play("bow")
        self.playingAnim = 'bow'

    def exitBow(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterJump(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'jump'
        self.loop("jump")

    def exitJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterLeap(self, ts=0, callback=None, extraArgs=[]):
        self.playingAnim = 'leap'
        self.loop("leap")

    def exitLeap(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterCringe(self, ts=0, callback=None, extraArgs=[]):
        self.play("cringe")

    def exitCringe(self):
        self.exitGeneral()

    def enterConked(self, ts=0, callback=None, extraArgs=[]):
        self.play("conked")

    def exitConked(self):
        self.exitGeneral()
예제 #22
0
파일: Toon.py 프로젝트: coginvasion/src
class Toon(Avatar.Avatar, ToonHead, ToonDNA.ToonDNA):

    def __init__(self, cr, mat = 0):
        self.cr = cr
        try:
            self.Toon_initialized
            return
        except:
            self.Toon_initialized = 1

        Avatar.Avatar.__init__(self, mat)
        ToonDNA.ToonDNA.__init__(self)
        ToonHead.__init__(self, cr)
        self.forwardSpeed = 0.0
        self.rotateSpeed = 0.0
        self.avatarType = CIGlobals.Toon
        self.track = None
        self.standWalkRunReverse = None
        self.playingAnim = None
        self.tag = None
        self.money = 0
        self.lookAtTrack = None
        self.portal1 = None
        self.portal2 = None
        self.gunAttached = False
        self.gun = None
        self.tokenIcon = None
        self.tokenIconIval = None
        self.backpack = None
        self.forcedTorsoAnim = None
        self.fallSfx = base.audio3d.loadSfx('phase_4/audio/sfx/MG_cannon_hit_dirt.mp3')
        base.audio3d.attachSoundToObject(self.fallSfx, self)
        self.eyes = loader.loadTexture('phase_3/maps/eyes.jpg', 'phase_3/maps/eyes_a.rgb')
        self.myTaskId = random.uniform(0, 1231231232132131231232L)
        self.closedEyes = loader.loadTexture('phase_3/maps/eyesClosed.jpg', 'phase_3/maps/eyesClosed_a.rgb')
        self.soundChatBubble = loader.loadSfx('phase_3/audio/sfx/GUI_balloon_popup.mp3')
        self.shadowCaster = None
        self.chatSoundDict = {}
        self.animFSM = ClassicFSM('Toon', [State('off', self.enterOff, self.exitOff),
         State('neutral', self.enterNeutral, self.exitNeutral),
         State('swim', self.enterSwim, self.exitSwim),
         State('walk', self.enterWalk, self.exitWalk),
         State('run', self.enterRun, self.exitRun),
         State('openBook', self.enterOpenBook, self.exitOpenBook),
         State('readBook', self.enterReadBook, self.exitReadBook),
         State('closeBook', self.enterCloseBook, self.exitCloseBook),
         State('teleportOut', self.enterTeleportOut, self.exitTeleportOut),
         State('teleportIn', self.enterTeleportIn, self.exitTeleportIn),
         State('died', self.enterDied, self.exitDied),
         State('fallFWD', self.enterFallFWD, self.exitFallFWD),
         State('fallBCK', self.enterFallBCK, self.exitFallBCK),
         State('jump', self.enterJump, self.exitJump),
         State('leap', self.enterLeap, self.exitLeap),
         State('laugh', self.enterLaugh, self.exitLaugh),
         State('happy', self.enterHappyJump, self.exitHappyJump),
         State('shrug', self.enterShrug, self.exitShrug),
         State('hdance', self.enterHDance, self.exitHDance),
         State('wave', self.enterWave, self.exitWave),
         State('scientistEmcee', self.enterScientistEmcee, self.exitScientistEmcee),
         State('scientistWork', self.enterScientistWork, self.exitScientistWork),
         State('scientistGame', self.enterScientistGame, self.exitScientistGame),
         State('scientistJealous', self.enterScientistJealous, self.exitScientistJealous),
         State('cringe', self.enterCringe, self.exitCringe),
         State('conked', self.enterConked, self.exitConked),
         State('win', self.enterWin, self.exitWin),
         State('walkBack', self.enterWalkBack, self.exitWalkBack),
         State('deadNeutral', self.enterDeadNeutral, self.exitDeadNeutral),
         State('deadWalk', self.enterDeadWalk, self.exitDeadWalk),
         State('squish', self.enterSquish, self.exitSquish),
         State('Happy', self.enterHappy, self.exitHappy),
         State('Sad', self.enterSad, self.exitSad)], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()
        if not hasattr(base, 'localAvatar') or not base.localAvatar == self:
            Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 3, 1)
        return

    def enterHappy(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = None
        self.standWalkRunReverse = (('neutral', 1.0),
         ('walk', 1.0),
         ('run', 1.0),
         ('walk', -1.0))
        self.setSpeed(self.forwardSpeed, self.rotateSpeed)
        return

    def exitHappy(self):
        self.standWalkRunReverse = None
        self.stop()
        return

    def enterSad(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'sad'
        self.standWalkRunReverse = (('dneutral', 1.0),
         ('dwalk', 1.2),
         ('dwalk', 1.2),
         ('dwalk', -1.0))
        self.setSpeed(0, 0)

    def exitSad(self):
        self.standWalkRunReverse = None
        self.stop()
        return

    def setSpeed(self, forwardSpeed, rotateSpeed):
        self.forwardSpeed = forwardSpeed
        self.rotateSpeed = rotateSpeed
        action = None
        if self.standWalkRunReverse != None:
            if forwardSpeed >= CIGlobals.RunCutOff:
                action = CIGlobals.RUN_INDEX
            elif forwardSpeed > CIGlobals.WalkCutOff:
                action = CIGlobals.WALK_INDEX
            elif forwardSpeed < -CIGlobals.WalkCutOff:
                action = CIGlobals.REVERSE_INDEX
            elif rotateSpeed != 0.0:
                action = CIGlobals.WALK_INDEX
            else:
                action = CIGlobals.STAND_INDEX
            anim, rate = self.standWalkRunReverse[action]
            if anim != self.playingAnim:
                self.playingAnim = anim
                self.stop()
                self.loop(anim)
                self.setPlayRate(rate, anim)
        return action

    def enterSquish(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'squish'
        sound = loader.loadSfx('phase_9/audio/sfx/toon_decompress.mp3')
        lerpTime = 0.1
        node = self.getGeomNode().getChild(0)
        origScale = node.getScale()
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('getSquished')
        else:
            name = 'getSquished'
        self.track = Sequence(LerpScaleInterval(node, lerpTime, VBase3(2, 2, 0.025), blendType='easeInOut'), Wait(1.0), Parallel(Sequence(Wait(0.4), LerpScaleInterval(node, lerpTime, VBase3(1.4, 1.4, 1.4), blendType='easeInOut'), LerpScaleInterval(node, lerpTime / 2.0, VBase3(0.8, 0.8, 0.8), blendType='easeInOut'), LerpScaleInterval(node, lerpTime / 3.0, origScale, blendType='easeInOut')), ActorInterval(self, 'happy', startTime=0.2), SoundInterval(sound)), name=name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.squishDone, [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.start(ts)

    def squishDone(self, callback = None, extraArgs = []):
        self.__doCallback(callback, extraArgs)

    def exitSquish(self):
        if self.track:
            self.ignore(self.track.getName())
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track.finish()
            self.track = None
        self.playingAnim = 'neutral'
        return

    def enterDeadNeutral(self, ts = 0, callback = None, extraArgs = []):
        self.loop('dneutral')

    def exitDeadNeutral(self):
        self.stop()

    def enterDeadWalk(self, ts = 0, callback = None, extraArgs = []):
        self.loop('dwalk')

    def exitDeadWalk(self):
        self.stop()

    def setBackpack(self, pack):
        self.backpack = pack

    def getGhost(self):
        return 0

    def updateChatSoundDict(self):
        self.chatSoundDict['exclaim'] = base.audio3d.loadSfx(self.getToonAnimalNoise('exclaim'))
        self.chatSoundDict['question'] = base.audio3d.loadSfx(self.getToonAnimalNoise('question'))
        self.chatSoundDict['short'] = base.audio3d.loadSfx(self.getToonAnimalNoise('short'))
        self.chatSoundDict['medium'] = base.audio3d.loadSfx(self.getToonAnimalNoise('med'))
        self.chatSoundDict['long'] = base.audio3d.loadSfx(self.getToonAnimalNoise('long'))
        self.chatSoundDict['howl'] = base.audio3d.loadSfx(self.getToonAnimalNoise('howl'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['exclaim'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['question'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['short'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['medium'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['long'], self.getPart('head'))
        base.audio3d.attachSoundToObject(self.chatSoundDict['howl'], self.getPart('head'))

    def ghostOn(self):
        self.getGeomNode().hide()
        self.getNameTag().hide()
        self.getShadow().hide()
        if self.tokenIcon:
            self.tokenIcon.hide()
        self.stashBodyCollisions()

    def ghostOff(self):
        self.unstashBodyCollisions()
        if self.tokenIcon:
            self.tokenIcon.show()
        self.getShadow().show()
        self.getNameTag().show()
        self.getGeomNode().show()

    def attachGun(self, gunName):
        self.detachGun()
        if gunName == 'pistol':
            self.gun = loader.loadModel('phase_4/models/props/water-gun.bam')
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(0.28, 0.1, 0.08))
            self.gun.setHpr(VBase3(85.6, -4.44, 94.43))
            self.gunAttached = True
        elif gunName == 'shotgun':
            self.gun = loader.loadModel('phase_4/models/props/shotgun.egg')
            self.gun.setScale(0.75)
            self.gun.reparentTo(self.find('**/def_joint_right_hold'))
            self.gun.setPos(Point3(-0.5, -0.2, 0.19))
            self.gun.setHpr(Vec3(350, 272.05, 0))
            color = random.choice([VBase4(1, 0.25, 0.25, 1), VBase4(0.25, 1, 0.25, 1), VBase4(0.25, 0.25, 1, 1)])
            self.gun.setColorScale(color)
            self.gunAttached = True

    def detachGun(self):
        if self.gun and self.gunAttached:
            self.gun.removeNode()
            self.gun = None
            self.gunAttached = False
        return

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                notify.warning('animFSM in flux, state=%s, not requesting off' % self.animFSM.getCurrentState().getName())
        else:
            notify.warning('animFSM has been deleted')
        if self.track != None:
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        return

    def disable(self):
        try:
            self.Toon_disabled
        except:
            self.Toon_disabled = 1
            self.backpack = None
            self.stopAnimations()
            self.removeAdminToken()
            ToonHead.delete(self)
            self.deleteCurrentToon()
            self.chatSoundDict = {}

        return

    def delete(self):
        try:
            self.Toon_deleted
        except:
            self.Toon_deleted = 1
            del self.animFSM
            self.forwardSpeed = None
            self.chatSoundDict = None
            self.rotateSpeed = None
            self.avatarType = None
            self.track = None
            self.standWalkRunReverse = None
            self.currentAnim = None
            self.toon_head = None
            self.forcedTorsoAnim = None
            self.toon_torso = None
            self.toon_legs = None
            self.gender = None
            self.headtype = None
            self.head = None
            self.legtype = None
            self.torsotype = None
            self.hr = None
            self.hg = None
            self.hb = None
            self.tr = None
            self.tg = None
            self.tb = None
            self.lr = None
            self.lg = None
            self.lb = None
            self.shir = None
            self.shig = None
            self.shib = None
            self.shor = None
            self.shog = None
            self.shob = None
            self.shirt = None
            self.sleeve = None
            self.short = None
            self.tag = None
            self.money = None
            self.lookAtTrack = None
            self.portal1 = None
            self.portal2 = None
            self.backpack = None
            self.fallSfx = None
            self.eyes = None
            self.myTaskId = None
            self.closedEyes = None
            self.soundChatBubble = None
            self.lastAction = None
            self.lastState = None
            self.playingAnim = None
            Avatar.Avatar.delete(self)

        return

    def initCollisions(self):
        self.collNodePath.setCollideMask(BitMask32(0))
        self.collNodePath.node().setFromCollideMask(CIGlobals.WallBitmask)
        pusher = CollisionHandlerPusher()
        pusher.setInPattern('%in')
        pusher.addCollider(self.collNodePath, self)
        base.cTrav.addCollider(self.collNodePath, pusher)

    def deleteCurrentToon(self):
        if self.shadowCaster:
            self.shadowCaster.clear()
            self.shadowCaster = None
        try:
            self.stopLookAround()
            self.stopBlink()
        except:
            pass

        self.pupils = []
        if 'head' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['head']
        if 'torso' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['torso']
        if 'legs' in self._Actor__commonBundleHandles:
            del self._Actor__commonBundleHandles['legs']
        self.deleteShadow()
        self.removePart('head')
        self.removePart('torso')
        self.removePart('legs')
        self.detachGun()
        return

    def enterGagShop(self):
        DirectLabel(text='ENTERED GAG SHOP', relief=None, text_scale=0.08)
        return

    def setAdminToken(self, tokenId):
        tokens = {0: 500}
        if tokenId in tokens.keys():
            icons = loader.loadModel('phase_3/models/props/gm_icons.bam')
            self.tokenIcon = icons.find('**/access_level_%s' % tokens[tokenId])
            self.tokenIcon.reparentTo(self)
            x = self.getNameTag().getX()
            y = self.getNameTag().getY()
            z = self.getNameTag().getZ()
            self.tokenIcon.setPos(Vec3(x, y, z) + (0, 0, 0.5))
            self.tokenIcon.setScale(0.4)
            self.tokenIconIval = Sequence(LerpHprInterval(self.tokenIcon, duration=3.0, hpr=Vec3(360, 0, 0), startHpr=Vec3(0, 0, 0)))
            self.tokenIconIval.loop()
            icons.removeNode()

    def removeAdminToken(self):
        if self.tokenIcon != None and self.tokenIconIval != None:
            self.tokenIconIval.finish()
            self.tokenIcon.removeNode()
            self.tokenIconIval = None
            self.tokenIcon = None
        return

    def setChat(self, chatString):
        if not self.isThought(chatString):
            if not self.getGhost() or self.doId == base.localAvatar.doId:
                if 'ooo' in chatString.lower():
                    sfx = self.chatSoundDict['howl']
                elif '!' in chatString.lower():
                    sfx = self.chatSoundDict['exclaim']
                elif '?' in chatString.lower():
                    sfx = self.chatSoundDict['question']
                elif len(chatString) <= 9:
                    sfx = self.chatSoundDict['short']
                elif 10 <= len(chatString) <= 19:
                    sfx = self.chatSoundDict['medium']
                elif len(chatString) >= 20:
                    sfx = self.chatSoundDict['long']
                sfx.play()
        Avatar.Avatar.setChat(self, chatString)

    def setName(self, nameString):
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType)

    def setDNAStrand(self, dnaStrand, makeTag = 1):
        ToonDNA.ToonDNA.setDNAStrand(self, dnaStrand)
        self.deleteCurrentToon()
        self.generateToon(makeTag)

    def generateToon(self, makeTag = 1):
        self.generateLegs()
        self.generateTorso()
        self.generateHead()
        self.setToonColor()
        self.setClothes()
        self.setGloves()
        self.parentToonParts()
        self.rescaleToon()
        if makeTag:
            self.setupNameTag()
        Avatar.Avatar.initShadow(self)
        if self.cr.isShowingPlayerIds:
            self.showAvId()
        self.updateChatSoundDict()

    def attachTNT(self):
        self.pies.attachTNT()
        self.holdTNTAnim()

    def detachTNT(self):
        self.pies.detachTNT()
        self.animFSM.request(self.animFSM.getCurrentState().getName())

    def holdTNTAnim(self):
        self.pose('toss', 22, partName='torso')

    def parentToonParts(self):
        self.attach('head', 'torso', 'def_head')
        self.attach('torso', 'legs', 'joint_hips')

    def unparentToonParts(self):
        self.getPart('head').reparentTo(self.getGeomNode())
        self.getPart('torso').reparentTo(self.getGeomNode())
        self.getPart('legs').reparentTo(self.getGeomNode())

    def rescaleToon(self):
        animal = self.getAnimal()
        bodyScale = CIGlobals.toonBodyScales[animal]
        headScale = CIGlobals.toonHeadScales[animal][2]
        shoulderHeight = CIGlobals.legHeightDict[self.legs] * bodyScale + CIGlobals.torsoHeightDict[self.torso] * bodyScale
        height = shoulderHeight + CIGlobals.headHeightDict[self.head] * headScale
        bodyScale = CIGlobals.toonBodyScales[animal]
        self.setAvatarScale(bodyScale)
        self.setHeight(height)

    def setGloves(self):
        color = self.getGloveColor()
        gloves = self.find('**/hands')
        gloves.setColor(color)

    def setClothes(self):
        shirt, shirtcolor = self.getShirtStyle()
        short, shortcolor = self.getShortStyle()
        sleeve, sleevecolor = self.getSleeveStyle()
        torsot = self.findAllMatches('**/torso-top')
        torsob = self.findAllMatches('**/torso-bot')
        sleeves = self.findAllMatches('**/sleeves')
        torsot.setTexture(loader.loadTexture(shirt), 1)
        torsob.setTexture(loader.loadTexture(short), 1)
        sleeves.setTexture(loader.loadTexture(sleeve), 1)
        torsot.setColor(shirtcolor)
        sleeves.setColor(sleevecolor)
        torsob.setColor(shortcolor)

    def generateLegs(self):
        legtype = self.getLegs()
        self.loadModel('phase_3/models/char/tt_a_chr_' + legtype + '_shorts_legs_' + str(CIGlobals.getModelDetail(self.avatarType)) + '.bam', 'legs')
        self.loadAnims({'neutral': 'phase_3/models/char/tt_a_chr_' + legtype + '_shorts_legs_neutral.bam',
         'run': 'phase_3/models/char/tt_a_chr_' + legtype + '_shorts_legs_run.bam',
         'walk': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_walk.bam',
         'pie': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_pie-throw.bam',
         'fallb': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_slip-backward.bam',
         'fallf': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_slip-forward.bam',
         'lose': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_lose.bam',
         'win': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_victory-dance.bam',
         'squirt': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_water-gun.bam',
         'zend': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_jump-zend.bam',
         'tele': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_teleport.bam',
         'book': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_book.bam',
         'leap': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_leap_zhang.bam',
         'jump': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_jump-zhang.bam',
         'happy': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_jump.bam',
         'shrug': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_shrug.bam',
         'hdance': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_happy-dance.bam',
         'wave': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_wave.bam',
         'scemcee': 'phase_4/models/char/tt_a_chr_dgm_shorts_legs_scientistEmcee.bam',
         'scwork': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_scientistWork.bam',
         'scgame': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_scientistGame.bam',
         'scjealous': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_scientistJealous.bam',
         'swim': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_swim.bam',
         'toss': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_toss.bam',
         'cringe': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_cringe.bam',
         'conked': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_conked.bam',
         'catchneutral': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_gameneutral.bam',
         'catchrun': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_gamerun.bam',
         'hold-bottle': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_hold-bottle.bam',
         'push-button': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_press-button.bam',
         'happy-dance': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_happy-dance.bam',
         'juggle': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_juggle.bam',
         'shout': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_shout.bam',
         'dneutral': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_sad-neutral.bam',
         'dwalk': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_losewalk.bam',
         'smooch': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_smooch.bam',
         'conked': 'phase_3.5/models/char/tt_a_chr_' + legtype + '_shorts_legs_conked.bam',
         'sound': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_shout.bam',
         'sprinkle-dust': 'phase_5/models/char/tt_a_chr_' + legtype + '_shorts_legs_sprinkle-dust.bam',
         'start-sit': 'phase_4/models/char/tt_a_chr_' + legtype + '_shorts_legs_intoSit.bam',
         'sit': 'phase_4/models/char/char/tt_a_chr_' + legtype + '_shorts_legs_sit.bam'}, 'legs')
        self.findAllMatches('**/boots_long').stash()
        self.findAllMatches('**/boots_short').stash()
        self.findAllMatches('**/shoes').stash()

    def generateTorso(self):
        torsotype = self.getTorso()
        self.loadModel('phase_3/models/char/tt_a_chr_' + torsotype + '_torso_' + str(CIGlobals.getModelDetail(self.avatarType)) + '.bam', 'torso')
        self.loadAnims({'neutral': 'phase_3/models/char/tt_a_chr_' + torsotype + '_torso_neutral.bam',
         'run': 'phase_3/models/char/tt_a_chr_' + torsotype + '_torso_run.bam',
         'walk': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_walk.bam',
         'pie': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_pie-throw.bam',
         'fallb': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_slip-backward.bam',
         'fallf': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_slip-forward.bam',
         'lose': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_lose.bam',
         'win': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_victory-dance.bam',
         'squirt': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_water-gun.bam',
         'zend': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_jump-zend.bam',
         'tele': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_teleport.bam',
         'book': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_book.bam',
         'leap': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_leap_zhang.bam',
         'jump': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_jump-zhang.bam',
         'happy': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_jump.bam',
         'shrug': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_shrug.bam',
         'hdance': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_happy-dance.bam',
         'wave': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_wave.bam',
         'scemcee': 'phase_4/models/char/tt_a_chr_dgm_shorts_torso_scientistEmcee.bam',
         'scwork': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_scientistWork.bam',
         'scgame': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_scientistGame.bam',
         'scjealous': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_scientistJealous.bam',
         'swim': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_swim.bam',
         'toss': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_toss.bam',
         'cringe': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_cringe.bam',
         'conked': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_conked.bam',
         'catchneutral': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_gameneutral.bam',
         'catchrun': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_gamerun.bam',
         'hold-bottle': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_hold-bottle.bam',
         'push-button': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_press-button.bam',
         'happy-dance': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_happy-dance.bam',
         'juggle': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_juggle.bam',
         'shout': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_shout.bam',
         'dneutral': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_sad-neutral.bam',
         'dwalk': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_losewalk.bam',
         'smooch': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_smooch.bam',
         'conked': 'phase_3.5/models/char/tt_a_chr_' + torsotype + '_torso_conked.bam',
         'sound': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_shout.bam',
         'sprinkle-dust': 'phase_5/models/char/tt_a_chr_' + torsotype + '_torso_sprinkle-dust.bam',
         'start-sit': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_intoSit.bam',
         'sit': 'phase_4/models/char/tt_a_chr_' + torsotype + '_torso_sit.bam'}, 'torso')

    def generateHead(self, pat = 0):
        gender = self.getGender()
        head = self.getAnimal()
        headtype = self.getHead()
        ToonHead.generateHead(self, gender, head, headtype)

    def setToonColor(self):
        self.setHeadColor()
        self.setTorsoColor()
        self.setLegColor()

    def setLegColor(self):
        legcolor = self.getLegColor()
        self.findAllMatches('**/legs').setColor(legcolor)
        self.findAllMatches('**/feet').setColor(legcolor)

    def setTorsoColor(self):
        torsocolor = self.getTorsoColor()
        self.findAllMatches('**/arms').setColor(torsocolor)
        self.findAllMatches('**/neck').setColor(torsocolor)
        self.findAllMatches('**/hands').setColor(1, 1, 1, 1)

    def setForcedTorsoAnim(self, string):
        self.forcedTorsoAnim = string
        self.loop(string, partName='torso')

    def clearForcedTorsoAnim(self):
        self.forcedTorsoAnim = None
        self.animFSM.request(self.animFSM.getCurrentState().getName())
        return

    def enterOff(self, ts = 0, callback = None, extraArgs = []):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterWin(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'win'
        self.sfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/ENC_Win.mp3')
        self.sfx.setLoop(True)
        base.audio3d.attachSoundToObject(self.sfx, self)
        base.playSfx(self.sfx)
        self.loop('win')

    def exitWin(self):
        self.stop()
        self.sfx.stop()
        del self.sfx
        self.playingAnim = 'neutral'

    def enterShrug(self, ts = 0, callback = None, extraArgs = []):
        self.play('shrug')

    def exitShrug(self):
        self.exitGeneral()

    def enterHDance(self, ts = 0, callback = None, extraArgs = []):
        self.play('hdance')

    def exitHDance(self):
        self.exitGeneral()

    def enterScientistWork(self, ts = 0, callback = None, extraArgs = []):
        self.loop('scwork')

    def exitScientistWork(self):
        self.exitGeneral()

    def enterScientistEmcee(self, ts = 0, callback = None, extraArgs = []):
        self.loop('scemcee')

    def exitScientistEmcee(self):
        self.exitGeneral()

    def enterScientistGame(self, ts = 0, callback = None, extraArgs = []):
        self.loop('scgame')

    def exitScientistGame(self):
        self.exitGeneral()

    def enterScientistJealous(self, ts = 0, callback = None, extraArgs = []):
        self.loop('scjealous')

    def exitScientistJealous(self):
        self.exitGeneral()

    def enterWave(self, ts = 0, callback = None, extraArgs = []):
        self.play('wave')

    def exitWave(self):
        self.exitGeneral()

    def enterLaugh(self, ts = 0, callback = None, extraArgs = []):
        self.setPlayRate(5.0, 'neutral')
        self.loop('neutral')

    def exitLaugh(self):
        self.setPlayRate(1.0, 'neutral')
        self.stop()

    def enterNeutral(self, ts = 0, callback = None, extraArgs = []):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() != GagState.LOADED:
                    self.loop('neutral', partName='legs')
                    if self.animal == 'dog':
                        self.loop('neutral', partName='head')
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('neutral', partName='legs')
            return
        else:
            self.loop('neutral')
            self.playingAnim = 'neutral'
            return

    def exitNeutral(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def exitGeneral(self):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() != GagState.LOADED:
                    self.stop(partName='legs')
                else:
                    self.stop()
            else:
                self.stop()
        else:
            self.stop()

    def enterRun(self, ts = 0, callback = None, extraArgs = []):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() != GagState.LOADED:
                    self.loop('run', partName='legs')
                    if self.animal == 'dog':
                        self.loop('run', partName='head')
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('run', partName='legs')
            return
        else:
            self.loop('run')
            return

    def exitRun(self):
        self.exitGeneral()

    def enterWalk(self, ts = 0, callback = None, extraArgs = []):
        if self.backpack:
            if self.backpack.getCurrentGag():
                if self.backpack.getCurrentGag().getState() != GagState.LOADED:
                    self.loop('walk', partName='legs')
                    if self.animal == 'dog':
                        self.loop('walk', partName='head')
                    return
        if self.forcedTorsoAnim != None:
            self.loop(self.forcedTorsoAnim, partName='torso')
            self.loop('walk', partName='legs')
            return
        else:
            self.loop('walk')
            return

    def exitWalk(self):
        self.exitGeneral()

    def enterWalkBack(self, ts = 0, callback = None, extraArgs = []):
        self.setPlayRate(-1.0, 'walk')
        self.enterWalk()

    def exitWalkBack(self):
        self.exitWalk()
        self.setPlayRate(1.0, 'walk')

    def enterOpenBook(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'book'
        self.book1 = Actor('phase_3.5/models/props/book-mod.bam', {'chan': 'phase_3.5/models/props/book-chan.bam'})
        self.book1.reparentTo(self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self, 'book', startFrame=CIGlobals.OpenBookFromFrame, endFrame=CIGlobals.OpenBookToFrame, name=self.uniqueName('enterOpenBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback, [callback, extraArgs])
        self.track.start(ts)
        self.book1.play('chan', fromFrame=CIGlobals.OpenBookFromFrame, toFrame=CIGlobals.OpenBookToFrame)

    def exitOpenBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book1:
            self.book1.cleanup()
            self.book1 = None
        self.playingAnim = 'neutral'
        return

    def enterReadBook(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'book'
        self.book2 = Actor('phase_3.5/models/props/book-mod.bam', {'chan': 'phase_3.5/models/props/book-chan.bam'})
        self.book2.reparentTo(self.getPart('torso').find('**/def_joint_right_hold'))
        self.pingpong('book', fromFrame=CIGlobals.ReadBookFromFrame, toFrame=CIGlobals.ReadBookToFrame)
        self.book2.pingpong('chan', fromFrame=CIGlobals.ReadBookFromFrame, toFrame=CIGlobals.ReadBookToFrame)

    def exitReadBook(self):
        if self.book2:
            self.book2.cleanup()
            self.book2 = None
        self.playingAnim = 'neutral'
        return

    def enterCloseBook(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'book'
        self.book3 = Actor('phase_3.5/models/props/book-mod.bam', {'chan': 'phase_3.5/models/props/book-chan.bam'})
        self.book3.reparentTo(self.getPart('torso').find('**/def_joint_right_hold'))
        self.track = ActorInterval(self, 'book', startFrame=CIGlobals.CloseBookFromFrame, endFrame=CIGlobals.CloseBookToFrame, name=self.uniqueName('enterCloseBook'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.__doCallback, [callback, extraArgs])
        self.track.start(ts)
        self.book3.play('chan', fromFrame=CIGlobals.CloseBookFromFrame, toFrame=CIGlobals.CloseBookToFrame)

    def exitCloseBook(self):
        if self.track:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            self.track = None
        if self.book3:
            self.book3.cleanup()
            self.book3 = None
        self.playingAnim = 'neutral'
        return

    def enterTeleportOut(self, ts = 0, callback = None, extraArgs = []):
        self.notify.info(str(self.doId) + '-' + str(self.zoneId) + ': enterTeleportOut')
        self.playingAnim = 'tele'
        self.portal1 = Actor('phase_3.5/models/props/portal-mod.bam', {'chan': 'phase_3.5/models/props/portal-chan.bam'})
        self.portal1.play('chan')
        self.portal1.reparentTo(self.getPart('legs').find('**/def_joint_right_hold'))
        self.play('tele')
        if hasattr(self, 'uniqueName'):
            name = self.uniqueName('enterTeleportOut')
        else:
            name = 'enterTeleportOut'
        self.track = Sequence(Wait(0.4), Func(self.teleportOutSfx), Wait(1.3), Func(self.throwPortal), Wait(3.4), name=name)
        self.track.delayDelete = DelayDelete.DelayDelete(self, name)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportOutDone, [callback, extraArgs])
        self.track.start(ts)

    def doPortalBins(self, portal):
        portal.setBin('shadow', 0)
        portal.setDepthWrite(0)
        portal.setDepthTest(0)

    def teleportOutDone(self, callback, requestStatus):
        self.notify.info(str(self.doId) + '-' + str(self.zoneId) + ': teleportOutDone')
        self.__doCallback(callback, requestStatus)
        self.exitTeleportOut()

    def teleportOutSfx(self):
        self.outSfx = base.audio3d.loadSfx('phase_3.5/audio/sfx/AV_teleport.mp3')
        base.audio3d.attachSoundToObject(self.outSfx, self.portal1)
        self.outSfx.play()

    def throwPortal(self):
        self.doPortalBins(self.portal1)
        self.portal1.reparentTo(self.getPart('legs').find('**/joint_nameTag'))
        self.portal1.setScale(CIGlobals.PortalScale)
        self.portal1.setY(6.5)
        self.portal1.setH(180)

    def exitTeleportOut(self):
        self.notify.info(str(self.doId) + '-' + str(self.zoneId) + ': exitTeleportOut')
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal1:
            self.portal1.cleanup()
            self.portal1 = None
        self.playingAnim = 'neutral'
        return

    def getTeleportInTrack(self, portal):
        self.doPortalBins(portal)
        holeTrack = Sequence()
        holeTrack.append(Func(portal.reparentTo, self))
        pos = Point3(0, -2.4, 0)
        holeTrack.append(Func(portal.setPos, pos))
        holeTrack.append(ActorInterval(portal, 'chan', startTime=3.4, endTime=3.1))
        holeTrack.append(Wait(0.6))
        holeTrack.append(ActorInterval(portal, 'chan', startTime=3.1, endTime=3.4))

        def restorePortal(portal):
            portal.setPos(0, 0, 0)
            portal.detachNode()
            portal.clearBin()
            portal.clearDepthTest()
            portal.clearDepthWrite()

        holeTrack.append(Func(restorePortal, portal))
        toonTrack = Sequence(Wait(0.3), Func(self.getGeomNode().show), Func(self.getNameTag().show), ActorInterval(self, 'happy', startTime=0.45))
        if hasattr(self, 'uniqueName'):
            trackName = self.uniqueName('teleportIn')
        else:
            trackName = 'teleportIn'
        return Parallel(toonTrack, holeTrack, name=trackName)

    def enterTeleportIn(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'happy'
        self.portal2 = Actor('phase_3.5/models/props/portal-mod.bam', {'chan': 'phase_3.5/models/props/portal-chan.bam'})
        self.show()
        self.getGeomNode().hide()
        self.getNameTag().hide()
        self.track = self.getTeleportInTrack(self.portal2)
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getName(), self.teleportInDone, [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, self.track.getName())
        self.track.start(ts)

    def teleportInDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitTeleportIn()

    def exitTeleportIn(self):
        if self.track != None:
            self.ignore(self.track.getName())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        if self.portal2:
            self.portal2.cleanup()
            self.portal2 = None
        if self.getGeomNode():
            self.getGeomNode().show()
        if self.getNameTag():
            self.getNameTag().show()
        self.playingAnim = 'neutral'
        return

    def enterFallFWD(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'fallf'
        self.play('fallf')
        Sequence(Wait(0.5), Func(self.fallSfx.play)).start()

    def exitFallFWD(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterFallBCK(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'fallb'
        self.play('fallb')
        Sequence(Wait(0.5), Func(self.fallSfx.play)).start()

    def exitFallBCK(self):
        self.playingAnim = 'neutral'
        self.exitGeneral()

    def enterHappyJump(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'happy'
        self.play('happy')

    def exitHappyJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterSwim(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'swim'
        self.loop('swim')
        self.getGeomNode().setP(-89.0)
        self.getGeomNode().setZ(4.0)
        nt = self.getNameTag()
        nt.setX(0)
        nt.setY(-2)
        nt.setZ(5.0)

    def exitSwim(self):
        self.exitGeneral()
        self.getGeomNode().setP(0.0)
        self.getGeomNode().setZ(0.0)
        nt = self.getNameTag()
        nt.setX(0)
        nt.setY(0)
        nt.setZ(self.getHeight() + 0.3)
        self.playingAnim = 'neutral'

    def enterDied(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'lose'
        self.isdying = True
        self.play('lose')
        self.track = Sequence(Wait(2.2), Func(self.dieSfx), Wait(2.8), self.getGeomNode().scaleInterval(2, Point3(0.01), startScale=self.getGeomNode().getScale()), Func(self.delToon), name=self.uniqueName('enterDied'))
        self.track.setDoneEvent(self.track.getName())
        self.acceptOnce(self.track.getDoneEvent(), self.diedDone, [callback, extraArgs])
        self.track.delayDelete = DelayDelete.DelayDelete(self, 'enterTeleportOut')
        self.track.start(ts)

    def diedDone(self, callback, extraArgs):
        self.__doCallback(callback, extraArgs)
        self.exitDied()

    def __doCallback(self, callback, extraArgs):
        if callback:
            if extraArgs:
                callback(*extraArgs)
            else:
                callback()

    def dieSfx(self):
        self.Losesfx = base.audio3d.loadSfx('phase_5/audio/sfx/ENC_Lose.mp3')
        base.audio3d.attachSoundToObject(self.Losesfx, self)
        self.Losesfx.play()

    def delToon(self):
        self.isdead = True

    def exitDied(self):
        if self.track != None:
            self.ignore(self.track.getDoneEvent())
            self.track.finish()
            DelayDelete.cleanupDelayDeletes(self.track)
            self.track = None
        self.rescaleToon()
        self.playingAnim = 'neutral'
        return

    def enterJump(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'jump'
        self.loop('jump')

    def exitJump(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterLeap(self, ts = 0, callback = None, extraArgs = []):
        self.playingAnim = 'leap'
        self.loop('leap')

    def exitLeap(self):
        self.exitGeneral()
        self.playingAnim = 'neutral'

    def enterCringe(self, ts = 0, callback = None, extraArgs = []):
        self.play('cringe')

    def exitCringe(self):
        self.exitGeneral()

    def enterConked(self, ts = 0, callback = None, extraArgs = []):
        self.play('conked')

    def exitConked(self):
        self.exitGeneral()
예제 #23
0
class ToonHead(Actor.Actor):
    notify = DirectNotifyGlobal.directNotify.newCategory('ToonHead')
    EyesOpen = loader.loadTexture('phase_3/maps/eyes.jpg',
                                  'phase_3/maps/eyes_a.rgb')
    EyesOpen.setMinfilter(Texture.FTLinear)
    EyesOpen.setMagfilter(Texture.FTLinear)
    EyesClosed = loader.loadTexture('phase_3/maps/eyesClosed.jpg',
                                    'phase_3/maps/eyesClosed_a.rgb')
    EyesClosed.setMinfilter(Texture.FTLinear)
    EyesClosed.setMagfilter(Texture.FTLinear)
    EyesSadOpen = loader.loadTexture('phase_3/maps/eyesSad.jpg',
                                     'phase_3/maps/eyesSad_a.rgb')
    EyesSadOpen.setMinfilter(Texture.FTLinear)
    EyesSadOpen.setMagfilter(Texture.FTLinear)
    EyesSadClosed = loader.loadTexture('phase_3/maps/eyesSadClosed.jpg',
                                       'phase_3/maps/eyesSadClosed_a.rgb')
    EyesSadClosed.setMinfilter(Texture.FTLinear)
    EyesSadClosed.setMagfilter(Texture.FTLinear)
    EyesAngryOpen = loader.loadTexture('phase_3/maps/eyesAngry.jpg',
                                       'phase_3/maps/eyesAngry_a.rgb')
    EyesAngryOpen.setMinfilter(Texture.FTLinear)
    EyesAngryOpen.setMagfilter(Texture.FTLinear)
    EyesAngryClosed = loader.loadTexture('phase_3/maps/eyesAngryClosed.jpg',
                                         'phase_3/maps/eyesAngryClosed_a.rgb')
    EyesAngryClosed.setMinfilter(Texture.FTLinear)
    EyesAngryClosed.setMagfilter(Texture.FTLinear)
    EyesSurprised = loader.loadTexture('phase_3/maps/eyesSurprised.jpg',
                                       'phase_3/maps/eyesSurprised_a.rgb')
    EyesSurprised.setMinfilter(Texture.FTLinear)
    EyesSurprised.setMagfilter(Texture.FTLinear)
    Muzzle = loader.loadTexture('phase_3/maps/muzzleShrtGeneric.jpg')
    Muzzle.setMinfilter(Texture.FTLinear)
    Muzzle.setMagfilter(Texture.FTLinear)
    MuzzleSurprised = loader.loadTexture(
        'phase_3/maps/muzzleShortSurprised.jpg')
    MuzzleSurprised.setMinfilter(Texture.FTLinear)
    MuzzleSurprised.setMagfilter(Texture.FTLinear)
    LeftA = Point3(0.06, 0.0, 0.14)
    LeftB = Point3(-0.13, 0.0, 0.1)
    LeftC = Point3(-0.05, 0.0, 0.0)
    LeftD = Point3(0.06, 0.0, 0.0)
    RightA = Point3(0.13, 0.0, 0.1)
    RightB = Point3(-0.06, 0.0, 0.14)
    RightC = Point3(-0.06, 0.0, 0.0)
    RightD = Point3(0.05, 0.0, 0.0)
    LeftAD = Point3(
        LeftA[0] - LeftA[2] * (LeftD[0] - LeftA[0]) / (LeftD[2] - LeftA[2]),
        0.0, 0.0)
    LeftBC = Point3(
        LeftB[0] - LeftB[2] * (LeftC[0] - LeftB[0]) / (LeftC[2] - LeftB[2]),
        0.0, 0.0)
    RightAD = Point3(
        RightA[0] - RightA[2] * (RightD[0] - RightA[0]) /
        (RightD[2] - RightA[2]), 0.0, 0.0)
    RightBC = Point3(
        RightB[0] - RightB[2] * (RightC[0] - RightB[0]) /
        (RightC[2] - RightB[2]), 0.0, 0.0)

    def __init__(self):
        Actor.Actor.__init__(self)
        self.toonName = 'ToonHead-' + str(self.this)
        self.__blinkName = 'blink-' + self.toonName
        self.__stareAtName = 'stareAt-' + self.toonName
        self.__lookName = 'look-' + self.toonName
        self.lookAtTrack = None
        self.__muzzles = []
        self.__surpriseMuzzles = []
        self.__angryMuzzles = []
        self.__sadMuzzles = []
        self.__smileMuzzles = []
        self.__laughMuzzles = []
        self.__eyes = None
        self.__eyelashOpen = None
        self.__eyelashClosed = None
        self.__lod500Eyes = None
        self.__lod250Eyes = None
        self.__lpupil = None
        self.__lod500lPupil = None
        self.__lod250lPupil = None
        self.__rpupil = None
        self.__lod500rPupil = None
        self.__lod250rPupil = None
        self.__muzzle = None
        self.__eyesOpen = ToonHead.EyesOpen
        self.__eyesClosed = ToonHead.EyesClosed
        self.__height = 0.0
        self.__eyelashesHiddenByGlasses = False
        self.randGen = random.Random()
        self.randGen.seed(random.random())
        self.eyelids = ClassicFSM('eyelids', [
            State('off', self.enterEyelidsOff, self.exitEyelidsOff,
                  ['open', 'closed', 'surprised']),
            State('open', self.enterEyelidsOpen, self.exitEyelidsOpen,
                  ['closed', 'surprised', 'off']),
            State('surprised', self.enterEyelidsSurprised,
                  self.exitEyelidsSurprised, ['open', 'closed', 'off']),
            State('closed', self.enterEyelidsClosed, self.exitEyelidsClosed,
                  ['open', 'surprised', 'off'])
        ], 'off', 'off')
        self.eyelids.enterInitialState()
        self.emote = None
        self.__stareAtNode = NodePath()
        self.__defaultStarePoint = Point3(0, 0, 0)
        self.__stareAtPoint = self.__defaultStarePoint
        self.__stareAtTime = 0
        self.lookAtPositionCallbackArgs = None

    def delete(self):
        try:
            self.ToonHead_deleted
        except:
            self.ToonHead_deleted = 1
            taskMgr.remove(self.__blinkName)
            taskMgr.remove(self.__lookName)
            taskMgr.remove(self.__stareAtName)
            if self.lookAtTrack:
                self.lookAtTrack.finish()
                self.lookAtTrack = None
            del self.eyelids
            del self.__stareAtNode
            del self.__stareAtPoint
            if self.__eyes:
                del self.__eyes
            if self.__lpupil:
                del self.__lpupil
            if self.__rpupil:
                del self.__rpupil
            if self.__eyelashOpen:
                del self.__eyelashOpen
            if self.__eyelashClosed:
                del self.__eyelashClosed
            self.lookAtPositionCallbackArgs = None
            Actor.Actor.delete(self)

        return

    def setupHead(self, dna, forGui=0):
        self.__height = self.generateToonHead(dna, forGui=forGui)
        self.generateToonColor(dna)
        animalStyle = dna.getAnimal()
        bodyScale = toonBodyScales[animalStyle]
        headScale = toonHeadScales[animalStyle]
        self.getGeomNode().setScale(headScale[0] * bodyScale * 1.3,
                                    headScale[1] * bodyScale * 1.3,
                                    headScale[2] * bodyScale * 1.3)
        if forGui:
            self.getGeomNode().setDepthWrite(1)
            self.getGeomNode().setDepthTest(1)
        if dna.getAnimal() == 'dog':
            self.loop('neutral')

    def fitAndCenterHead(self, maxDim, forGui=0):
        p1 = Point3()
        p2 = Point3()
        self.calcTightBounds(p1, p2)
        if forGui:
            h = 180
            t = p1[0]
            p1.setX(-p2[0])
            p2.setX(-t)
        else:
            h = 0
        d = p2 - p1
        biggest = max(d[0], d[2])
        s = maxDim / biggest
        mid = (p1 + d / 2.0) * s
        self.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], h, 0, 0, s, s, s)

    def setLookAtPositionCallbackArgs(self, argTuple):
        self.lookAtPositionCallbackArgs = argTuple

    def getHeight(self):
        return self.__height

    def getRandomForwardLookAtPoint(self):
        x = self.randGen.choice((-0.8, -0.5, 0, 0.5, 0.8))
        z = self.randGen.choice((-0.5, 0, 0.5, 0.8))
        return Point3(x, 1.5, z)

    def findSomethingToLookAt(self):
        if self.lookAtPositionCallbackArgs is not None:
            pnt = self.lookAtPositionCallbackArgs[0].getLookAtPosition(
                self.lookAtPositionCallbackArgs[1],
                self.lookAtPositionCallbackArgs[2])
            self.startStareAt(self, pnt)
            return
        if self.randGen.random() < 0.33:
            lookAtPnt = self.getRandomForwardLookAtPoint()
        else:
            lookAtPnt = self.__defaultStarePoint
        self.lerpLookAt(lookAtPnt, blink=1)
        return

    def generateToonHead(self, style, lods=['1000'], forGui=0):
        headStyle = style.head
        fix = None
        if headStyle == 'dls':
            filePrefix = HeadDict['dls']
            headHeight = 0.75
        elif headStyle == 'dss':
            filePrefix = HeadDict['dss']
            headHeight = 0.5
        elif headStyle == 'dsl':
            filePrefix = HeadDict['dsl']
            headHeight = 0.5
        elif headStyle == 'dll':
            filePrefix = HeadDict['dll']
            headHeight = 0.75
        elif headStyle == 'cls':
            filePrefix = HeadDict['c']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'css':
            filePrefix = HeadDict['c']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'csl':
            filePrefix = HeadDict['c']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'cll':
            filePrefix = HeadDict['c']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'hls':
            filePrefix = HeadDict['h']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'hss':
            filePrefix = HeadDict['h']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'hsl':
            filePrefix = HeadDict['h']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'hll':
            filePrefix = HeadDict['h']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'mls':
            filePrefix = HeadDict['m']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'mss':
            filePrefix = HeadDict['m']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'rls':
            filePrefix = HeadDict['r']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'rss':
            filePrefix = HeadDict['r']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'rsl':
            filePrefix = HeadDict['r']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'rll':
            filePrefix = HeadDict['r']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'fls':
            filePrefix = HeadDict['f']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'fss':
            filePrefix = HeadDict['f']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'fsl':
            filePrefix = HeadDict['f']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'fll':
            filePrefix = HeadDict['f']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'pls':
            filePrefix = HeadDict['p']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'pss':
            filePrefix = HeadDict['p']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'psl':
            filePrefix = HeadDict['p']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'pll':
            filePrefix = HeadDict['p']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'bls':
            filePrefix = HeadDict['b']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'bss':
            filePrefix = HeadDict['b']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'bsl':
            filePrefix = HeadDict['b']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'bll':
            filePrefix = HeadDict['b']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'sls':
            filePrefix = HeadDict['s']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'sss':
            filePrefix = HeadDict['s']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'ssl':
            filePrefix = HeadDict['s']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'sll':
            filePrefix = HeadDict['s']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        else:
            ToonHead.notify.error('unknown head style: %s' % headStyle)
        if len(lods) == 1:
            self.loadModel('phase_3' + filePrefix + lods[0], 'head', 'lodRoot',
                           1)
            if fix is not None:
                fix(style, None, 1)
            if not forGui:
                self.__lods = lods
                self.__style = style
                self.__headStyle = headStyle
                self.__copy = 1
        else:
            for lod in lods:
                self.loadModel('phase_3' + filePrefix + lod, 'head', lod, 1)
                if fix is not None:
                    fix(style, lod, 1)
                if not forGui:
                    self.__lods = lods
                    self.__style = style
                    self.__headStyle = headStyle
                    self.__copy = 1

        self.__fixEyes(style, forGui)
        self.setupEyelashes(style)
        self.eyelids.request('closed')
        self.eyelids.request('open')
        self.setupMuzzles(style)
        return headHeight

    def hideEars(self):
        self.findAllMatches('**/ears*;+s').stash()

    def showEars(self):
        self.findAllMatches('**/ears*;+s').unstash()

    def hideEyelashes(self):
        if self.__eyelashOpen:
            self.__eyelashOpen.stash()
        if self.__eyelashClosed:
            self.__eyelashClosed.stash()
        self.__eyelashesHiddenByGlasses = True

    def showEyelashes(self):
        if self.__eyelashOpen:
            self.__eyelashOpen.unstash()
        if self.__eyelashClosed:
            self.__eyelashClosed.unstash()
        self.__eyelashesHiddenByGlasses = False

    def generateToonColor(self, style):
        parts = self.findAllMatches('**/head*')
        parts.setColor(style.getHeadColor())
        animalType = style.getAnimal()
        if animalType == 'cat' or animalType == 'rabbit' or animalType == 'bear' or animalType == 'mouse' or animalType == 'pig':
            parts = self.findAllMatches('**/ear?-*')
            parts.setColor(style.getHeadColor())

    def __fixEyes(self, style, forGui=0):
        mode = -3
        if forGui:
            mode = -2
        if self.hasLOD():
            for lodName in self.getLODNames():
                self.drawInFront('eyes*', 'head-front*', mode, lodName=lodName)
                if not self.find('**/joint_pupil*').isEmpty():
                    self.drawInFront('joint_pupil*',
                                     'eyes*',
                                     -1,
                                     lodName=lodName)
                else:
                    self.drawInFront('def_*_pupil',
                                     'eyes*',
                                     -1,
                                     lodName=lodName)

            self.__eyes = self.getLOD(1000).find('**/eyes*')
            self.__lod500Eyes = self.getLOD(500).find('**/eyes*')
            self.__lod250Eyes = self.getLOD(250).find('**/eyes*')
            if self.__lod500Eyes.isEmpty():
                self.__lod500Eyes = None
            else:
                self.__lod500Eyes.setColorOff()
                if not self.find('**/joint_pupilL*').isEmpty():
                    self.__lod500lPupil = self.__lod500Eyes.find(
                        '**/joint_pupilL*')
                    self.__lod500rPupil = self.__lod500Eyes.find(
                        '**/joint_pupilR*')
                else:
                    self.__lod500lPupil = self.__lod500Eyes.find(
                        '**/def_left_pupil*')
                    self.__lod500rPupil = self.__lod500Eyes.find(
                        '**/def_right_pupil*')

            if self.__lod250Eyes.isEmpty():
                self.__lod250Eyes = None
            else:
                self.__lod250Eyes.setColorOff()
                if not self.find('**/joint_pupilL*').isEmpty():
                    self.__lod250lPupil = self.__lod250Eyes.find(
                        '**/joint_pupilL*')
                    self.__lod250rPupil = self.__lod250Eyes.find(
                        '**/joint_pupilR*')
                else:
                    self.__lod250lPupil = self.__lod250Eyes.find(
                        '**/def_left_pupil*')
                    self.__lod250rPupil = self.__lod250Eyes.find(
                        '**/def_right_pupil*')

        else:
            self.drawInFront('eyes*', 'head-front*', mode)
            if not self.find('joint_pupil*').isEmpty():
                self.drawInFront('joint_pupil*', 'eyes*', -1)
            else:
                self.drawInFront('def_*_pupil', 'eyes*', -1)

            self.__eyes = self.find('**/eyes*')
        if not self.__eyes.isEmpty():
            self.__eyes.setColorOff()
            self.__lpupil = None
            self.__rpupil = None
            if not self.find('**/joint_pupilL*').isEmpty():
                if self.getLOD(1000):
                    lp = self.getLOD(1000).find('**/joint_pupilL*')
                    rp = self.getLOD(1000).find('**/joint_pupilR*')
                else:
                    lp = self.find('**/joint_pupilL*')
                    rp = self.find('**/joint_pupilR*')
            elif not self.getLOD(1000):
                lp = self.find('**/def_left_pupil*')
                rp = self.find('**/def_right_pupil*')
            else:
                lp = self.getLOD(1000).find('**/def_left_pupil*')
                rp = self.getLOD(1000).find('**/def_right_pupil*')

            if lp.isEmpty() or rp.isEmpty():
                print('Unable to locate pupils.')
            else:
                leye = self.__eyes.attachNewNode('leye')
                reye = self.__eyes.attachNewNode('reye')
                lmat = Mat4(0.802174, 0.59709, 0, 0, -0.586191, 0.787531,
                            0.190197, 0, 0.113565, -0.152571, 0.981746, 0,
                            -0.233634, 0.418062, 0.0196875, 1)
                leye.setMat(lmat)
                rmat = Mat4(0.786788, -0.617224, 0, 0, 0.602836, 0.768447,
                            0.214658, 0, -0.132492, -0.16889, 0.976689, 0,
                            0.233634, 0.418062, 0.0196875, 1)
                reye.setMat(rmat)
                self.__lpupil = leye.attachNewNode('lpupil')
                self.__rpupil = reye.attachNewNode('rpupil')
                lpt = self.__eyes.attachNewNode('')
                rpt = self.__eyes.attachNewNode('')
                lpt.wrtReparentTo(self.__lpupil)
                rpt.wrtReparentTo(self.__rpupil)
                lp.reparentTo(lpt)
                rp.reparentTo(rpt)
                self.__lpupil.adjustAllPriorities(1)
                self.__rpupil.adjustAllPriorities(1)
                if self.__lod500Eyes:
                    self.__lod500lPupil.adjustAllPriorities(1)
                    self.__lod500rPupil.adjustAllPriorities(1)
                if self.__lod250Eyes:
                    self.__lod250lPupil.adjustAllPriorities(1)
                    self.__lod250rPupil.adjustAllPriorities(1)
                animalType = style.getAnimal()
                if animalType != 'dog':
                    self.__lpupil.flattenStrong()
                    self.__rpupil.flattenStrong()

    def __setPupilDirection(self, x, y):
        if y < 0.0:
            y2 = -y
            left1 = self.LeftAD + (self.LeftD - self.LeftAD) * y2
            left2 = self.LeftBC + (self.LeftC - self.LeftBC) * y2
            right1 = self.RightAD + (self.RightD - self.RightAD) * y2
            right2 = self.RightBC + (self.RightC - self.RightBC) * y2
        else:
            y2 = y
            left1 = self.LeftAD + (self.LeftA - self.LeftAD) * y2
            left2 = self.LeftBC + (self.LeftB - self.LeftBC) * y2
            right1 = self.RightAD + (self.RightA - self.RightAD) * y2
            right2 = self.RightBC + (self.RightB - self.RightBC) * y2
        left0 = Point3(
            0.0, 0.0, left1[2] - left1[0] * (left2[2] - left1[2]) /
            (left2[0] - left1[0]))
        right0 = Point3(
            0.0, 0.0, right1[2] - right1[0] * (right2[2] - right1[2]) /
            (right2[0] - right1[0]))
        if x < 0.0:
            x2 = -x
            left = left0 + (left2 - left0) * x2
            right = right0 + (right2 - right0) * x2
        else:
            x2 = x
            left = left0 + (left1 - left0) * x2
            right = right0 + (right1 - right0) * x2
        self.__lpupil.setPos(left)
        self.__rpupil.setPos(right)

    def __lookPupilsAt(self, node, point):
        if node is not None:
            mat = node.getMat(self.__eyes)
            point = mat.xformPoint(point)
        distance = 1.0
        recip_z = 1.0 / max(0.1, point[1])
        x = distance * point[0] * recip_z
        y = distance * point[2] * recip_z
        x = min(max(x, -1), 1)
        y = min(max(y, -1), 1)
        self.__setPupilDirection(x, y)

    def __lookHeadAt(self, node, point, frac=1.0, lod=None):
        reachedTarget = 1
        if lod is None:
            head = self.getPart('head', self.getLODNames()[0])
        else:
            head = self.getPart('head', lod)
        if node is not None:
            headParent = head.getParent()
            mat = node.getMat(headParent)
            point = mat.xformPoint(point)
        rot = Mat3(0, 0, 0, 0, 0, 0, 0, 0, 0)
        lookAt(rot, Vec3(point), Vec3(0, 0, 1), CSDefault)
        scale = VBase3(0, 0, 0)
        hpr = VBase3(0, 0, 0)
        if decomposeMatrix(rot, scale, hpr, CSDefault):
            hpr = VBase3(min(max(hpr[0], -60), 60), min(max(hpr[1], -20), 30),
                         0)
            if frac != 1:
                currentHpr = head.getHpr()
                reachedTarget = abs(hpr[0] - currentHpr[0]) < 1.0 and abs(
                    hpr[1] - currentHpr[1]) < 1.0
                hpr = currentHpr + (hpr - currentHpr) * frac
            if lod is None:
                for lodName in self.getLODNames():
                    head = self.getPart('head', lodName)
                    head.setHpr(hpr)

            else:
                head.setHpr(hpr)
        return reachedTarget

    def setupEyelashes(self, style):
        if style.getGender() == 'm':
            if self.__eyelashOpen:
                self.__eyelashOpen.removeNode()
                self.__eyelashOpen = None
            if self.__eyelashClosed:
                self.__eyelashClosed.removeNode()
                self.__eyelashClosed = None
        else:
            if self.__eyelashOpen:
                self.__eyelashOpen.removeNode()
            if self.__eyelashClosed:
                self.__eyelashClosed.removeNode()
            animal = style.head[0]
            model = loader.loadModel('phase_3' + EyelashDict[animal])
            if self.hasLOD():
                head = self.getPart('head', '1000')
            else:
                head = self.getPart('head', 'lodRoot')
            length = style.head[1]
            if length == 'l':
                openString = 'open-long'
                closedString = 'closed-long'
            else:
                openString = 'open-short'
                closedString = 'closed-short'
            self.__eyelashOpen = model.find('**/' + openString).copyTo(head)
            self.__eyelashClosed = model.find('**/' +
                                              closedString).copyTo(head)
            model.removeNode()

    def __fixHeadLongLong(self, style, lodName=None, copy=1):
        if lodName is None:
            searchRoot = self
        else:
            searchRoot = self.find('**/' + str(lodName))
        otherParts = searchRoot.findAllMatches('**/*short*')
        for partNum in range(0, otherParts.getNumPaths()):
            if copy:
                otherParts.getPath(partNum).removeNode()
            else:
                otherParts.getPath(partNum).stash()

    def __fixHeadLongShort(self, style, lodName=None, copy=1):
        animalType = style.getAnimal()
        if lodName is None:
            searchRoot = self
        else:
            searchRoot = self.find('**/' + str(lodName))
        if animalType != 'duck' and animalType != 'horse':
            if animalType == 'rabbit':
                if copy:
                    searchRoot.find('**/ears-long').removeNode()
                else:
                    searchRoot.find('**/ears-long').hide()
            elif copy:
                searchRoot.find('**/ears-short').removeNode()
            else:
                searchRoot.find('**/ears-short').hide()
        if animalType != 'rabbit':
            if copy:
                searchRoot.find('**/eyes-short').removeNode()
            else:
                searchRoot.find('**/eyes-short').hide()
        if animalType != 'dog':
            if copy:
                searchRoot.find('**/joint_pupilL_short').removeNode()
                searchRoot.find('**/joint_pupilR_short').removeNode()
            else:
                searchRoot.find('**/joint_pupilL_short').stash()
                searchRoot.find('**/joint_pupilR_short').stash()
        if copy:
            self.find('**/head-short').removeNode()
            self.find('**/head-front-short').removeNode()
        else:
            self.find('**/head-short').hide()
            self.find('**/head-front-short').hide()
        if animalType != 'rabbit':
            muzzleParts = searchRoot.findAllMatches('**/muzzle-long*')
            for partNum in range(0, muzzleParts.getNumPaths()):
                if copy:
                    muzzleParts.getPath(partNum).removeNode()
                else:
                    muzzleParts.getPath(partNum).hide()

        else:
            muzzleParts = searchRoot.findAllMatches('**/muzzle-short*')
            for partNum in range(0, muzzleParts.getNumPaths()):
                if copy:
                    muzzleParts.getPath(partNum).removeNode()
                else:
                    muzzleParts.getPath(partNum).hide()

    def __fixHeadShortLong(self, style, lodName=None, copy=1):
        animalType = style.getAnimal()
        if lodName is None:
            searchRoot = self
        else:
            searchRoot = self.find('**/' + str(lodName))
        if animalType != 'duck' and animalType != 'horse':
            if animalType == 'rabbit':
                if copy:
                    searchRoot.find('**/ears-short').removeNode()
                else:
                    searchRoot.find('**/ears-short').hide()
            elif copy:
                searchRoot.find('**/ears-long').removeNode()
            else:
                searchRoot.find('**/ears-long').hide()
        if animalType != 'rabbit':
            if copy:
                searchRoot.find('**/eyes-long').removeNode()
            else:
                searchRoot.find('**/eyes-long').hide()
        if animalType != 'dog':
            if copy:
                searchRoot.find('**/joint_pupilL_long').removeNode()
                searchRoot.find('**/joint_pupilR_long').removeNode()
            else:
                searchRoot.find('**/joint_pupilL_long').stash()
                searchRoot.find('**/joint_pupilR_long').stash()
        if copy:
            searchRoot.find('**/head-long').removeNode()
            searchRoot.find('**/head-front-long').removeNode()
        else:
            searchRoot.find('**/head-long').hide()
            searchRoot.find('**/head-front-long').hide()
        if animalType != 'rabbit':
            muzzleParts = searchRoot.findAllMatches('**/muzzle-short*')
            for partNum in range(0, muzzleParts.getNumPaths()):
                if copy:
                    muzzleParts.getPath(partNum).removeNode()
                else:
                    muzzleParts.getPath(partNum).hide()

        else:
            muzzleParts = searchRoot.findAllMatches('**/muzzle-long*')
            for partNum in range(0, muzzleParts.getNumPaths()):
                if copy:
                    muzzleParts.getPath(partNum).removeNode()
                else:
                    muzzleParts.getPath(partNum).hide()

    def __fixHeadShortShort(self, style, lodName=None, copy=1):
        if lodName is None:
            searchRoot = self
        else:
            searchRoot = self.find('**/' + str(lodName))
        otherParts = searchRoot.findAllMatches('**/*long*')
        for partNum in range(0, otherParts.getNumPaths()):
            if copy:
                otherParts.getPath(partNum).removeNode()
            else:
                otherParts.getPath(partNum).stash()

    def __blinkOpenEyes(self, task):
        if self.eyelids.getCurrentState().getName() == 'closed':
            self.eyelids.request('open')
        r = self.randGen.random()
        if r < 0.1:
            t = 0.2
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self.__blinkCloseEyes, self.__blinkName)
        return task.done

    def __blinkCloseEyes(self, task):
        if self.eyelids.getCurrentState().getName() != 'open':
            taskMgr.doMethodLater(4.0, self.__blinkCloseEyes, self.__blinkName)
        else:
            self.eyelids.request('closed')
            taskMgr.doMethodLater(0.125, self.__blinkOpenEyes,
                                  self.__blinkName)
        return task.done

    def startBlink(self):
        taskMgr.remove(self.__blinkName)
        if self.__eyes:
            self.openEyes()
        taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1,
                              self.__blinkCloseEyes, self.__blinkName)

    def stopBlink(self):
        taskMgr.remove(self.__blinkName)
        if self.__eyes:
            self.eyelids.request('open')

    def closeEyes(self):
        self.eyelids.request('closed')

    def openEyes(self):
        self.eyelids.request('open')

    def surpriseEyes(self):
        self.eyelids.request('surprised')

    def sadEyes(self):
        self.__eyesOpen = ToonHead.EyesSadOpen
        self.__eyesClosed = ToonHead.EyesSadClosed

    def angryEyes(self):
        self.__eyesOpen = ToonHead.EyesAngryOpen
        self.__eyesClosed = ToonHead.EyesAngryClosed

    def normalEyes(self):
        self.__eyesOpen = ToonHead.EyesOpen
        self.__eyesClosed = ToonHead.EyesClosed

    def blinkEyes(self):
        taskMgr.remove(self.__blinkName)
        self.eyelids.request('closed')
        taskMgr.doMethodLater(0.1, self.__blinkOpenEyes, self.__blinkName)

    def __stareAt(self, task):
        frac = 2 * globalClock.getDt()
        reachedTarget = self.__lookHeadAt(self.__stareAtNode,
                                          self.__stareAtPoint, frac)
        self.__lookPupilsAt(self.__stareAtNode, self.__stareAtPoint)
        if reachedTarget and self.__stareAtNode is None:
            return task.done
        return task.cont

    def doLookAroundToStareAt(self, node, point):
        self.startStareAt(node, point)
        self.startLookAround()

    def startStareAtHeadPoint(self, point):
        self.startStareAt(self, point)

    def startStareAt(self, node, point):
        taskMgr.remove(self.__stareAtName)
        if self.lookAtTrack:
            self.lookAtTrack.finish()
            self.lookAtTrack = None
        self.__stareAtNode = node
        if point is not None:
            self.__stareAtPoint = point
        else:
            self.__stareAtPoint = self.__defaultStarePoint
        self.__stareAtTime = globalClock.getFrameTime()
        taskMgr.add(self.__stareAt, self.__stareAtName)

    def lerpLookAt(self, point, time=1.0, blink=0):
        taskMgr.remove(self.__stareAtName)
        if self.lookAtTrack:
            self.lookAtTrack.finish()
            self.lookAtTrack = None
        lodNames = self.getLODNames()
        if lodNames:
            lodName = lodNames[0]
        else:
            return 0
        head = self.getPart('head', lodName)
        startHpr = head.getHpr()
        startLpupil = self.__lpupil.getPos()
        startRpupil = self.__rpupil.getPos()
        self.__lookHeadAt(None, point, lod=lodName)
        self.__lookPupilsAt(None, point)
        endHpr = head.getHpr()
        endLpupil = self.__lpupil.getPos() * 0.5
        endRpupil = self.__rpupil.getPos() * 0.5
        head.setHpr(startHpr)
        self.__lpupil.setPos(startLpupil)
        self.__rpupil.setPos(startRpupil)
        if startHpr.almostEqual(endHpr, 10):
            return 0
        if blink:
            self.blinkEyes()
        lookToTgt_TimeFraction = 0.2
        lookToTgtTime = time * lookToTgt_TimeFraction
        returnToEyeCenterTime = time - lookToTgtTime - 0.5
        origin = Point3(0, 0, 0)
        blendType = 'easeOut'
        self.lookAtTrack = Parallel(Sequence(
            LerpPosInterval(self.__lpupil,
                            lookToTgtTime,
                            endLpupil,
                            blendType=blendType), Wait(0.5),
            LerpPosInterval(self.__lpupil,
                            returnToEyeCenterTime,
                            origin,
                            blendType=blendType)),
                                    Sequence(
                                        LerpPosInterval(self.__rpupil,
                                                        lookToTgtTime,
                                                        endRpupil,
                                                        blendType=blendType),
                                        Wait(0.5),
                                        LerpPosInterval(self.__rpupil,
                                                        returnToEyeCenterTime,
                                                        origin,
                                                        blendType=blendType)),
                                    name=self.__stareAtName)
        for lodName in self.getLODNames():
            head = self.getPart('head', lodName)
            self.lookAtTrack.append(
                LerpHprInterval(head, time, endHpr, blendType='easeInOut'))

        self.lookAtTrack.start()
        return 1

    def stopStareAt(self):
        self.lerpLookAt(Vec3.forward())

    def stopStareAtNow(self):
        taskMgr.remove(self.__stareAtName)
        if self.lookAtTrack:
            self.lookAtTrack.finish()
            self.lookAtTrack = None
        if self.__lpupil and self.__rpupil:
            self.__setPupilDirection(0, 0)
        for lodName in self.getLODNames():
            head = self.getPart('head', lodName)
            head.setHpr(0, 0, 0)

    def __lookAround(self, task):
        self.findSomethingToLookAt()
        t = self.randGen.random() * 4.0 + 3.0
        taskMgr.doMethodLater(t, self.__lookAround, self.__lookName)
        return task.done

    def startLookAround(self):
        taskMgr.remove(self.__lookName)
        t = self.randGen.random() * 5.0 + 2.0
        taskMgr.doMethodLater(t, self.__lookAround, self.__lookName)

    def stopLookAround(self):
        taskMgr.remove(self.__lookName)
        self.stopStareAt()

    def stopLookAroundNow(self):
        taskMgr.remove(self.__lookName)
        self.stopStareAtNow()

    def enterEyelidsOff(self):
        pass

    def exitEyelidsOff(self):
        pass

    def enterEyelidsOpen(self):
        if not self.__eyes.isEmpty():
            self.__eyes.setTexture(self.__eyesOpen, 1)
            if self.__eyelashOpen:
                self.__eyelashOpen.show()
            if self.__eyelashClosed:
                self.__eyelashClosed.hide()
            if self.__lod500Eyes:
                self.__lod500Eyes.setTexture(self.__eyesOpen, 1)
            if self.__lod250Eyes:
                self.__lod250Eyes.setTexture(self.__eyesOpen, 1)
            if self.__lpupil:
                self.__lpupil.show()
                self.__rpupil.show()
            if self.__lod500lPupil:
                self.__lod500lPupil.show()
                self.__lod500rPupil.show()
            if self.__lod250lPupil:
                self.__lod250lPupil.show()
                self.__lod250rPupil.show()

    def exitEyelidsOpen(self):
        pass

    def enterEyelidsClosed(self):
        if not self.__eyes.isEmpty() and self.__eyesClosed:
            self.__eyes.setTexture(self.__eyesClosed, 1)
            if self.__eyelashOpen:
                self.__eyelashOpen.hide()
            if self.__eyelashClosed:
                self.__eyelashClosed.show()
            if self.__lod500Eyes:
                self.__lod500Eyes.setTexture(self.__eyesClosed, 1)
            if self.__lod250Eyes:
                self.__lod250Eyes.setTexture(self.__eyesClosed, 1)
            if self.__lpupil:
                self.__lpupil.hide()
                self.__rpupil.hide()
            if self.__lod500lPupil:
                self.__lod500lPupil.hide()
                self.__lod500rPupil.hide()
            if self.__lod250lPupil:
                self.__lod250lPupil.hide()
                self.__lod250rPupil.hide()

    def exitEyelidsClosed(self):
        pass

    def enterEyelidsSurprised(self):
        if not self.__eyes.isEmpty() and ToonHead.EyesSurprised:
            self.__eyes.setTexture(ToonHead.EyesSurprised, 1)
            if self.__eyelashOpen:
                self.__eyelashOpen.hide()
            if self.__eyelashClosed:
                self.__eyelashClosed.hide()
            if self.__lod500Eyes:
                self.__lod500Eyes.setTexture(ToonHead.EyesSurprised, 1)
            if self.__lod250Eyes:
                self.__lod250Eyes.setTexture(ToonHead.EyesSurprised, 1)
            if self.__muzzle:
                self.__muzzle.setTexture(ToonHead.MuzzleSurprised, 1)
            if self.__lpupil:
                self.__lpupil.show()
                self.__rpupil.show()
            if self.__lod500lPupil:
                self.__lod500lPupil.show()
                self.__lod500rPupil.show()
            if self.__lod250lPupil:
                self.__lod250lPupil.show()
                self.__lod250rPupil.show()

    def exitEyelidsSurprised(self):
        if self.__muzzle:
            self.__muzzle.setTexture(ToonHead.Muzzle, 1)

    def setupMuzzles(self, style):
        self.__muzzles = []
        self.__surpriseMuzzles = []
        self.__angryMuzzles = []
        self.__sadMuzzles = []
        self.__smileMuzzles = []
        self.__laughMuzzles = []

        def hideAddNonEmptyItemToList(item, list):
            if not item.isEmpty():
                item.hide()
                list.append(item)

        if self.hasLOD():
            for lodName in self.getLODNames():
                animal = style.getAnimal()
                if animal != 'dog':
                    muzzle = self.find('**/' + lodName + '/**/muzzle*neutral')
                else:
                    muzzle = self.find('**/' + lodName + '/**/muzzle*')
                    if lodName == '1000' or lodName == '500':
                        filePrefix = DogMuzzleDict[style.head]
                        muzzles = loader.loadModel('phase_3' + filePrefix +
                                                   lodName)
                        if not self.find(
                                '**/' + lodName +
                                '/**/__Actor_head/def_head').isEmpty():
                            muzzles.reparentTo(
                                self.find('**/' + lodName +
                                          '/**/__Actor_head/def_head'))
                        else:
                            muzzles.reparentTo(
                                self.find('**/' + lodName +
                                          '/**/joint_toHead'))

                surpriseMuzzle = self.find('**/' + lodName +
                                           '/**/muzzle*surprise')
                angryMuzzle = self.find('**/' + lodName + '/**/muzzle*angry')
                sadMuzzle = self.find('**/' + lodName + '/**/muzzle*sad')
                smileMuzzle = self.find('**/' + lodName + '/**/muzzle*smile')
                laughMuzzle = self.find('**/' + lodName + '/**/muzzle*laugh')
                self.__muzzles.append(muzzle)
                hideAddNonEmptyItemToList(surpriseMuzzle,
                                          self.__surpriseMuzzles)
                hideAddNonEmptyItemToList(angryMuzzle, self.__angryMuzzles)
                hideAddNonEmptyItemToList(sadMuzzle, self.__sadMuzzles)
                hideAddNonEmptyItemToList(smileMuzzle, self.__smileMuzzles)
                hideAddNonEmptyItemToList(laughMuzzle, self.__laughMuzzles)

        else:
            if style.getAnimal() != 'dog':
                muzzle = self.find('**/muzzle*neutral')
            else:
                muzzle = self.find('**/muzzle*')
                filePrefix = DogMuzzleDict[style.head]
                muzzles = loader.loadModel('phase_3' + filePrefix + '1000')
                if not self.find('**/def_head').isEmpty():
                    muzzles.reparentTo(self.find('**/def_head'))
                else:
                    muzzles.reparentTo(self.find('**/joint_toHead'))

            surpriseMuzzle = self.find('**/muzzle*surprise')
            angryMuzzle = self.find('**/muzzle*angry')
            sadMuzzle = self.find('**/muzzle*sad')
            smileMuzzle = self.find('**/muzzle*smile')
            laughMuzzle = self.find('**/muzzle*laugh')
            self.__muzzles.append(muzzle)
            hideAddNonEmptyItemToList(surpriseMuzzle, self.__surpriseMuzzles)
            hideAddNonEmptyItemToList(angryMuzzle, self.__angryMuzzles)
            hideAddNonEmptyItemToList(sadMuzzle, self.__sadMuzzles)
            hideAddNonEmptyItemToList(smileMuzzle, self.__smileMuzzles)
            hideAddNonEmptyItemToList(laughMuzzle, self.__laughMuzzles)

    def getMuzzles(self):
        return self.__muzzles

    def getSurpriseMuzzles(self):
        return self.__surpriseMuzzles

    def getAngryMuzzles(self):
        return self.__angryMuzzles

    def getSadMuzzles(self):
        return self.__sadMuzzles

    def getSmileMuzzles(self):
        return self.__smileMuzzles

    def getLaughMuzzles(self):
        return self.__laughMuzzles

    def showNormalMuzzle(self):
        for muzzleNum in range(len(self.__muzzles)):
            self.__muzzles[muzzleNum].show()

    def hideNormalMuzzle(self):
        for muzzleNum in range(len(self.__muzzles)):
            self.__muzzles[muzzleNum].hide()

    def showAngryMuzzle(self):
        for muzzleNum in range(len(self.__angryMuzzles)):
            self.__angryMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideAngryMuzzle(self):
        for muzzleNum in range(len(self.__angryMuzzles)):
            self.__angryMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def showSadMuzzle(self):
        for muzzleNum in range(len(self.__sadMuzzles)):
            self.__sadMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideSadMuzzle(self):
        for muzzleNum in range(len(self.__sadMuzzles)):
            self.__sadMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def showSmileMuzzle(self):
        for muzzleNum in range(len(self.__smileMuzzles)):
            self.__smileMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideSmileMuzzle(self):
        for muzzleNum in range(len(self.__smileMuzzles)):
            self.__smileMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def showLaughMuzzle(self):
        for muzzleNum in range(len(self.__laughMuzzles)):
            self.__laughMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideLaughMuzzle(self):
        for muzzleNum in range(len(self.__laughMuzzles)):
            self.__laughMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def showSurpriseMuzzle(self):
        for muzzleNum in range(len(self.__surpriseMuzzles)):
            self.__surpriseMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideSurpriseMuzzle(self):
        for muzzleNum in range(len(self.__surpriseMuzzles)):
            self.__surpriseMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()
예제 #24
0
class CogInvasionClientRepository(AstronClientRepository):
    notify = directNotify.newCategory('CIClientRepository')
    GameGlobalsId = DO_ID_COGINVASION
    SetZoneDoneEvent = 'CICRSetZoneDone'
    EmuSetZoneDoneEvent = 'CICREmuSetZoneDone'
    SetInterest = 'Set'
    ClearInterest = 'Clear'
    ClearInterestDoneEvent = 'CICRClearInterestDone'
    ITAG_PERM = 'perm'
    ITAG_AVATAR = 'avatar'
    ITAG_SHARD = 'shard'
    ITAG_WORLD = 'world'
    ITAG_GAME = 'game'

    def __init__(self, music, serverVersion):
        self.music = music
        self.serverVersion = serverVersion
        AstronClientRepository.__init__(self, ['phase_3/etc/direct.dc', 'phase_3/etc/toon.dc'])
        self.loginFSM = ClassicFSM('login', [State('off', self.enterOff, self.exitOff),
         State('connect', self.enterConnect, self.exitConnect),
         State('disconnect', self.enterDisconnect, self.exitDisconnect),
         State('avChoose', self.enterAvChoose, self.exitAvChoose),
         State('playingGame', self.enterPlayingGame, self.exitPlayingGame),
         State('serverUnavailable', self.enterServerUnavailable, self.exitServerUnavailable),
         State('makeAToon', self.enterMakeAToon, self.exitMakeAToon),
         State('submitNewToon', self.enterSubmitNewToon, self.exitSubmitNewToon),
         State('noShards', self.enterNoShards, self.exitNoShards),
         State('waitForSetAvatarResponse', self.enterWaitForSetAvatarResponse, self.exitWaitForSetAvatarResponse),
         State('waitForShardList', self.enterWaitForShardList, self.exitWaitForShardList),
         State('ejected', self.enterEjected, self.exitEjected),
         State('districtReset', self.enterDistrictReset, self.exitDistrictReset),
         State('died', self.enterDied, self.exitDied),
         State('betaInform', self.enterBetaInform, self.exitBetaInform)], 'off', 'off')
        self.loginFSM.enterInitialState()
        self.gameFSM = ClassicFSM('game', [State('off', self.enterGameOff, self.exitGameOff),
         State('waitForGameEnterResponse', self.enterWaitForGameEnterResponse, self.exitWaitForGameEnterResponse),
         State('playGame', self.enterPlayGame, self.exitPlayGame),
         State('closeShard', self.enterCloseShard, self.exitCloseShard),
         State('switchShards', self.enterSwitchShards, self.exitSwitchShards)], 'off', 'off')
        self.gameFSM.enterInitialState()
        self.avChooser = AvChooser(self.loginFSM)
        self.playGame = PlayGame(self.gameFSM, 'playGameDone')
        self.hoodMgr = HoodMgr()
        self.makeAToon = MakeAToon()
        self.loginToken = os.environ.get('LOGIN_TOKEN')
        self.serverAddress = os.environ.get('GAME_SERVER')
        self.serverURL = URLSpec('http://%s' % self.serverAddress)
        self.parentMgr.registerParent(CIGlobals.SPRender, render)
        self.parentMgr.registerParent(CIGlobals.SPHidden, hidden)
        self.adminAccess = False
        self.localAvChoice = None
        self.SuitsActive = 0
        self.BossActive = 0
        self.accServerTimesNA = 0
        self.maxAccServerTimesNA = 10
        self.setZonesEmulated = 0
        self.old_setzone_interest_handle = None
        self.setZoneQueue = Queue()
        self.accept(self.SetZoneDoneEvent, self._handleEmuSetZoneDone)
        self.handler = None
        self.__currentAvId = 0
        self.myDistrict = None
        self.activeDistricts = {}
        self.shardListHandle = None
        self.uberZoneInterest = None
        self.isShowingPlayerIds = False
        self.doBetaInform = True
        self.dTutorial = None
        self.requestedName = None
        self.whisperNoise = base.loadSfx('phase_3.5/audio/sfx/GUI_whisper_3.ogg')
        self.checkHttp()
        base.audio3d = Audio3DManager(base.sfxManagerList[0], camera)
        base.audio3d.setDropOffFactor(0)
        base.audio3d.setDopplerFactor(3.0)
        base.lifter = CollisionHandlerFloor()
        base.pusher = CollisionHandlerPusher()
        base.queue = CollisionHandlerQueue()
        base.minigame = None
        base.finalExitCallbacks.insert(0, self.__handleExit)
        self.csm = self.generateGlobalObject(DO_ID_CLIENT_SERVICES_MANAGER, 'ClientServicesManager')
        self.friendsManager = self.generateGlobalObject(DO_ID_FRIENDS_MANAGER, 'FriendsManager')
        SpeedHackChecker.startChecking()
        self.loginFSM.request('connect')
        return

    def __handleExit(self):
        try:
            base.localAvatar.b_setAnimState('teleportOut')
        except:
            pass

        self.gameFSM.request('closeShard', ['off'])

    def showPlayerIds(self):
        print 'Showing player ids...'
        self.isShowingPlayerIds = True
        for av in self.doId2do.values():
            if av.__class__.__name__ in ('DistributedToon', 'LocalToon', 'DistributedSuit'):
                av.showAvId()

    def hidePlayerIds(self):
        print 'Hiding player ids...'
        self.isShowingPlayerIds = False
        for av in self.doId2do.values():
            if av.__class__.__name__ in ('DistributedToon', 'LocalToon', 'DistributedSuit'):
                av.showName()

    def sendSetLocation(self, doId, parentId, zoneId):
        dg = PyDatagram()
        dg.addUint16(CLIENT_OBJECT_LOCATION)
        dg.addUint32(doId)
        dg.addUint32(parentId)
        dg.addUint32(zoneId)
        self.send(dg)

    def getNextSetZoneDoneEvent(self):
        return '%s-%s' % (self.EmuSetZoneDoneEvent, self.setZonesEmulated + 1)

    def getLastSetZoneDoneEvent(self):
        return '%s-%s' % (self.EmuSetZoneDoneEvent, self.setZonesEmulated)

    def getQuietZoneLeftEvent(self):
        return 'leftQuietZone-%s' % (id(self),)

    def sendSetZoneMsg(self, zoneId, visibleZoneList=None):
        event = self.getNextSetZoneDoneEvent()
        self.setZonesEmulated += 1
        parentId = base.localAvatar.defaultShard
        self.sendSetLocation(base.localAvatar.doId, parentId, zoneId)
        localAvatar.setLocation(parentId, zoneId)
        interestZones = zoneId
        if visibleZoneList is not None:
            interestZones = visibleZoneList
        self._addInterestOpToQueue(self.SetInterest, [parentId, interestZones, 'OldSetZoneEmulator'], event)
        return

    def resetInterestStateForConnectionLoss(self):
        self.old_setzone_interest_handle = None
        self.setZoneQueue.clear()
        return

    def _removeEmulatedSetZone(self, doneEvent):
        self._addInterestOpToQueue(self.ClearInterest, None, doneEvent)
        return

    def _addInterestOpToQueue(self, op, args, event):
        self.setZoneQueue.push([op, args, event])
        if len(self.setZoneQueue) == 1:
            self._sendNextSetZone()

    def _sendNextSetZone(self):
        op, args, event = self.setZoneQueue.top()
        if op == self.SetInterest:
            parentId, interestZones, name = args
            if self.old_setzone_interest_handle == None:
                self.old_setzone_interest_handle = self.addInterest(parentId, interestZones, name, self.SetZoneDoneEvent)
            else:
                self.alterInterest(self.old_setzone_interest_handle, parentId, interestZones, name, self.SetZoneDoneEvent)
        else:
            if op == self.ClearInterest:
                self.removeInterest(self.old_setzone_interest_handle, self.SetZoneDoneEvent)
                self.old_setzone_interest_handle = None
            else:
                self.notify.error('unknown setZone op: %s' % op)
        return

    def _handleEmuSetZoneDone(self):
        op, args, event = self.setZoneQueue.pop()
        queueIsEmpty = self.setZoneQueue.isEmpty()
        if event is not None:
            messenger.send(event)
        if not queueIsEmpty:
            self._sendNextSetZone()
        return

    def enterSwitchShards(self, shardId, hoodId, zoneId, avId):
        self._switchShardParams = [
         shardId, hoodId, zoneId, avId]
        self.removeShardInterest(self._handleOldShardGone)

    def _handleOldShardGone(self):
        status = {}
        status['hoodId'] = self._switchShardParams[1]
        status['zoneId'] = self._switchShardParams[2]
        status['avId'] = self._switchShardParams[3]
        print status['avId']
        self.gameFSM.request('waitForGameEnterResponse', [status, self._switchShardParams[0]])

    def exitSwitchShards(self):
        del self._switchShardParams

    def enterBetaInform(self):
        msg = 'There may be some features that are present in the game, but are neither finished nor fully functional yet.\n\nAre you sure you want to enter?'
        self.dialog = GlobalDialog(message=msg, style=1, doneEvent='gameEnterChoice')
        self.dialog.show()
        self.acceptOnce('gameEnterChoice', self.handleGameEnterChoice)

    def handleGameEnterChoice(self):
        value = self.dialog.getValue()
        if value:
            self.loginFSM.request('avChoose')
        else:
            sys.exit()

    def exitBetaInform(self):
        self.ignore('gameEnterChoice')
        self.dialog.cleanup()
        del self.dialog

    def enterCloseShard(self, nextState='avChoose'):
        self.setNoNewInterests(True)
        self._removeLocalAvFromStateServer(nextState)

    def exitCloseShard(self):
        self.setNoNewInterests(False)
        self.ignore(self.ClearInterestDoneEvent)

    def _removeLocalAvFromStateServer(self, nextState):
        self.sendSetAvatarIdMsg(0)
        self._removeAllOV()
        callback = Functor(self.loginFSM.request, nextState)
        self.removeShardInterest(callback)

    def removeShardInterest(self, callback):
        self._removeCurrentShardInterest(Functor(self._removeShardInterestComplete, callback))

    def _removeShardInterestComplete(self, callback):
        self.cache.flush()
        self.doDataCache.flush()
        callback()

    def _removeCurrentShardInterest(self, callback):
        if self.old_setzone_interest_handle is None:
            callback()
            return
        self.acceptOnce(self.ClearInterestDoneEvent, Functor(self._removeCurrentUberZoneInterest, callback))
        self._removeEmulatedSetZone(self.ClearInterestDoneEvent)
        return

    def _removeCurrentUberZoneInterest(self, callback):
        self.acceptOnce(self.ClearInterestDoneEvent, Functor(self._removeShardInterestDone, callback))
        self.removeInterest(self.uberZoneInterest, self.ClearInterestDoneEvent)

    def _removeShardInterestDone(self, callback):
        self.uberZoneInterest = None
        callback()
        return

    def _removeAllOV(self):
        owners = self.doId2ownerView.keys()
        for doId in owners:
            self.disableDoId(doId, ownerView=True)

    def enterDied(self):
        self.deathDialog = GlobalDialog(message=CIGlobals.SuitDefeatMsg, style=2, doneEvent='deathChoice')
        self.deathDialog.show()
        self.acceptOnce('deathChoice', self.handleDeathChoice)

    def handleDeathChoice(self):
        value = self.deathDialog.getValue()
        if value:
            self.loginFSM.request('avChoose')
        else:
            sys.exit()

    def exitDied(self):
        self.deathDialog.cleanup()
        del self.deathDialog
        self.ignore('deathChoice')

    def enterConnect(self):
        self.connectingDialog = GlobalDialog(message=CIGlobals.ConnectingMsg)
        self.connectingDialog.show()
        self.connect([self.serverURL], successCallback=self.handleConnected, failureCallback=self.handleConnectFail)

    def handleConnected(self):
        self.notify.info('Sending CLIENT_HELLO...')
        self.acceptOnce('CLIENT_HELLO_RESP', self.handleClientHelloResp)
        self.acceptOnce('CLIENT_EJECT', self.handleEjected)
        self.acceptOnce('LOST_CONNECTION', self.handleLostConnection)
        AstronClientRepository.sendHello(self, self.serverVersion)

    def handleLostConnection(self):
        self.deleteAllObjects()
        self.loginFSM.request('disconnect', [1])

    def deleteAllObjects(self):
        for doId in self.doId2do.keys():
            obj = self.doId2do[doId]
            if hasattr(base, 'localAvatar'):
                if doId != base.localAvatar.doId:
                    if obj.__class__.__name__ not in ('ClientServicesManager', 'DistributedDistrict',
                                                      'FriendsManager', 'HolidayManager',
                                                      'NameServicesManager'):
                        self.deleteObject(doId)
            else:
                self.deleteObject(doId)

    def handleEjected(self, errorCode, reason):
        self.notify.info('OMG I WAS EJECTED!')
        self.ignore('LOST_CONNECTION')
        errorMsg = ErrorCode2ErrorMsg.get(errorCode, None) or UnknownErrorMsg % errorCode
        self.loginFSM.request('ejected', [errorMsg])
        return

    def handleClientHelloResp(self):
        self.notify.info('Got CLIENT_HELLO_RESP!')
        self.acceptOnce(self.csm.getLoginAcceptedEvent(), self.handleLoginAccepted)
        self.csm.d_requestLogin(self.loginToken)

    def handleLoginAccepted(self):
        self.notify.info('Woo-hoo, I am authenticated!')
        base.cr.holidayManager = self.generateGlobalObject(DO_ID_HOLIDAY_MANAGER, 'HolidayManager')
        base.cr.nameServicesManager = self.generateGlobalObject(DO_ID_NAME_SERVICES_MANAGER, 'NameServicesManager')
        self.loginFSM.request('waitForShardList')

    def handleConnectFail(self, foo1, foo2):
        self.notify.info('Could not connect to gameserver, notifying user.')
        self.connectingDialog.cleanup()
        self.connectingDialog = GlobalDialog(message=CIGlobals.NoConnectionMsg % self.serverAddress + ' ' + CIGlobals.TryAgain, style=2, doneEvent='connectFail')
        self.connectingDialog.show()
        self.acceptOnce('connectFail', self.handleConnectFailButton)

    def handleConnectFailButton(self):
        value = self.connectingDialog.getValue()
        if value:
            self.loginFSM.request('connect')
        else:
            sys.exit()

    def exitConnect(self):
        self.ignore('connectFail')
        self.ignore('CLIENT_HELLO_RESP')
        self.ignore(self.csm.getLoginAcceptedEvent())
        self.connectingDialog.cleanup()
        del self.connectingDialog

    def enterEjected(self, errorMsg):
        self.ejectDialog = GlobalDialog(message=errorMsg, style=3, doneEvent='ejectDone')
        self.ejectDialog.show()
        self.acceptOnce('ejectDone', sys.exit)

    def exitEjected(self):
        self.ignore('ejectDone')
        self.ejectDialog.cleanup()
        del self.ejectDialog

    def enterServerUnavailable(self):
        self.notify.info(CIGlobals.ServerUnavailable)
        self.serverNA = GlobalDialog(message=CIGlobals.ServerUnavailable, style=4, doneEvent='serverNAEvent')
        self.serverNA.show()
        self.acceptOnce('serverNAEvent', sys.exit)
        self.startServerNAPoll()

    def startServerNAPoll(self):
        self.notify.info('Starting server poll...')
        self.accServerTimesNA = 1
        taskMgr.add(self.serverNAPoll, 'serverNAPoll')

    def serverNAPoll(self, task):
        dg = PyDatagram()
        dg.addUint16(ACC_IS_SERVER_UP)
        self.send(dg)
        task.delayTime = 3.0
        return Task.again

    def __handleServerNAResp(self, resp):
        if resp == ACC_SERVER_UP:
            taskMgr.remove('serverNAPoll')
            self.loginFSM.request(self.loginFSM.getLastState().getName())
        else:
            self.accServerTimesNA += 1
            if self.accServerTimesNA >= self.maxAccServerTimesNA:
                taskMgr.remove('serverNAPoll')
                self.notify.info('Giving up on polling account server after %s times.' % self.accServerTimesNA)
                self.loginFSM.request('disconnect', enterArgList=[1])
                self.accServerTimesNA = 0

    def exitServerUnavailable(self):
        self.ignore('serverNAEvent')
        self.serverNA.cleanup()
        del self.serverNA

    def enterOff(self):
        pass

    def exitOff(self):
        pass

    def enterAvChoose(self):
        ModelPool.garbageCollect()
        TexturePool.garbageCollect()
        self.avChooser.load()
        self.avChooser.enter()
        if not self.music:
            self.music = base.loadMusic(CIGlobals.getThemeSong())
            base.playMusic(self.music, volume=0.75, looping=1)
        self.accept('enterMakeAToon', self.__handleMakeAToonReq)
        self.accept('avChooseDone', self.__handleAvChooseDone)

    def __handleMakeAToonReq(self, slot):
        self.loginFSM.request('makeAToon', [slot])

    def __handleAvChooseDone(self, avChoice):
        print '------- AvChooseDone -------'
        print 'Toon name: %s' % avChoice.getName()
        print 'Slot: %s' % avChoice.getSlot()
        print 'DNA: %s' % avChoice.getDNA()
        self.loginFSM.request('waitForSetAvatarResponse', [avChoice])

    def exitAvChoose(self):
        self.avChooser.exit()
        self.avChooser.unload()
        self.ignore('enterMakeAToon')
        self.ignore('avChooseDone')

    def handlePlayGame(self, msgType, di):
        if msgType == CLIENT_ENTER_OBJECT_REQUIRED_OTHER_OWNER:
            self.handleGenerateWithRequiredOtherOwner(msgType, di)
        else:
            AstronClientRepository.handleDatagram(self, di)

    def enterPlayingGame(self):
        zoneId = localAvatar.getLastHood()
        hoodId = ZoneUtil.getHoodId(zoneId)
        status = {'hoodId': hoodId, 'zoneId': zoneId, 
           'avId': self.localAvId}
        shardId = self.myDistrict.doId
        self.gameFSM.request('waitForGameEnterResponse', [status, shardId])

    def exitPlayingGame(self):
        self.deleteAllObjects()
        self.handler = None
        self.gameFSM.request('off')
        if hasattr(base, 'localAvatar'):
            camera.reparentTo(render)
            camera.setPos(0, 0, 0)
            camera.setHpr(0, 0, 0)
            del base.localAvatar
            del __builtins__['localAvatar']
        self.localAvChoice = None
        if loader.inBulkBlock:
            loader.endBulkLoad(loader.blockName)
        return

    def enterNoShards(self):
        self.noShardDialog = GlobalDialog(message=CIGlobals.NoShardsMsg + ' ' + CIGlobals.TryAgain, style=2, doneEvent='noShardsDone')
        self.noShardDialog.show()
        self.acceptOnce('noShardsDone', self.handleNoShardsDone)

    def handleNoShardsDone(self):
        value = self.noShardDialog.getValue()
        if value:
            self.loginFSM.request('waitForShardList')
        else:
            sys.exit()

    def exitNoShards(self):
        self.noShardDialog.cleanup()
        del self.noShardDialog
        self.ignore('noShardsDone')

    def enterWaitForShardList(self):
        self.shardListHandle = self.addTaggedInterest(self.GameGlobalsId, CIGlobals.DistrictZone, self.ITAG_PERM, 'localShardList', event='shardList_complete')
        self.acceptOnce('shardList_complete', self._handleShardListComplete)

    def _handleShardListComplete(self):
        if self._shardsAreAvailable():
            self.myDistrict = self._chooseAShard()
            if self.doBetaInform:
                self.loginFSM.request('betaInform')
            else:
                self.loginFSM.request('avChoose')
            taskMgr.add(self.monitorDistrict, 'monitorMyDistrict')
        else:
            self.loginFSM.request('noShards')

    def monitorDistrict(self, task):
        if self.myDistrict is None and self.isConnected():
            self.loginFSM.request('districtReset')
            return task.done
        return task.cont

    def _shardsAreAvailable(self):
        for shard in self.activeDistricts.values():
            if shard.available:
                return True

        return False

    def _chooseAShard(self):
        choices = []
        for shard in self.activeDistricts.values():
            choices.append(shard)

        return random.choice(choices)

    def exitWaitForShardList(self):
        self.ignore('shardList_complete')

    def enterDistrictReset(self):
        self.districtResetDialog = GlobalDialog(message=CIGlobals.DistrictResetMsg, style=3, doneEvent='distresetdone')
        self.districtResetDialog.show()
        self.acceptOnce('distresetdone', sys.exit)

    def exitDistrictReset(self):
        self.districtResetDialog.cleanup()
        del self.districtResetDialog

    def enterWaitForSetAvatarResponse(self, choice):
        self.sendSetAvatarMsg(choice)

    def enterLoadDone(self):
        self.loginFSM.request('playingGame')

    def __handleSetAvatarResponse(self, avId, di):
        print 'Entering game...'
        enterLoad = EnterLoad(self.enterLoadDone)
        dclass = self.dclassesByName['DistributedToon']
        localAvatar = LocalToon.LocalToon(base.cr)
        localAvatar.dclass = dclass
        base.localAvatar = localAvatar
        __builtins__['localAvatar'] = base.localAvatar
        localAvatar.doId = avId
        self.localAvId = avId
        parentId = None
        zoneId = None
        localAvatar.setLocation(parentId, zoneId)
        localAvatar.generateInit()
        localAvatar.generate()
        dclass.receiveUpdateBroadcastRequiredOwner(localAvatar, di)
        localAvatar.announceGenerate()
        localAvatar.postGenerateMessage()
        self.doId2do[avId] = localAvatar
        localAvatar.hoodsDiscovered = [
         1000, 2000, 3000, 4000, 5000, 9000]
        localAvatar.teleportAccess = [1000, 2000, 3000, 4000, 5000, 9000]
        enterLoad.load()
        del enterLoad
        return

    def exitWaitForSetAvatarResponse(self):
        self.ignore(self.csm.getSetAvatarEvent())

    def enterWaitForGameEnterResponse(self, status, shardId):
        if shardId is not None:
            district = self.activeDistricts[shardId]
        else:
            district = None
        if not district:
            self.loginFSM.request('noShards')
            return
        self.myDistrict = district
        self.notify.info('Entering shard %s' % shardId)
        localAvatar.setLocation(shardId, status['zoneId'])
        localAvatar.defaultShard = shardId
        self.handleEnteredShard(status)
        return

    def handleEnteredShard(self, status):
        self.uberZoneInterest = self.addInterest(localAvatar.defaultShard, CIGlobals.UberZone, 'uberZone', 'uberZoneInterestComplete')
        self.acceptOnce('uberZoneInterestComplete', self.uberZoneInterestComplete, [status])

    def uberZoneInterestComplete(self, status):
        self.__gotTimeSync = 0
        if self.timeManager == None:
            print 'No time manager'
            DistributedSmoothNode.globalActivateSmoothing(0, 0)
            self.gotTimeSync(status)
        else:
            print 'Time manager found'
            DistributedSmoothNode.globalActivateSmoothing(1, 0)
            if self.timeManager.synchronize('startup'):
                self.accept('gotTimeSync', self.gotTimeSync, [status])
            else:
                self.gotTimeSync(status)
        return

    def exitWaitForGameEnterResponse(self):
        self.ignore('uberZoneInterestComplete')

    def gotTimeSync(self, status):
        self.notify.info('gotTimeSync')
        self.ignore('gotTimeSync')
        self.__gotTimeSync = 1
        self.prepareToEnter(status)

    def prepareToEnter(self, status):
        if not self.__gotTimeSync:
            self.notify.info('still waiting for time sync')
            return
        self.gameFSM.request('playGame', [status])

    def enterMakeAToon(self, slot):
        if self.music:
            self.music.stop()
            self.music = None
        self.makeAToon.setSlot(slot)
        self.makeAToon.loadEnviron()
        self.makeAToon.load()
        self.makeAToon.matFSM.request('genderShop')
        self.acceptOnce('quitCreateAToon', self.__handleMakeAToonQuit)
        self.acceptOnce('createAToonFinished', self.__handleMakeAToonDone)
        return

    def __handleMakeAToonQuit(self):
        self.loginFSM.request('avChoose')

    def __handleMakeAToonDone(self, dnaStrand, slot, name):
        self.loginFSM.request('submitNewToon', enterArgList=[dnaStrand, slot, name])

    def exitMakeAToon(self):
        self.makeAToon.setSlot(-1)
        self.makeAToon.enterExit(None)
        self.ignore('quitCreateAToon')
        self.ignore('createAToonFinished')
        return

    def enterSubmitNewToon(self, dnaStrand, slot, name, skipTutorial=0):
        self.submittingDialog = GlobalDialog(message=CIGlobals.Submitting)
        self.submittingDialog.show()
        self.acceptOnce(self.csm.getToonCreatedEvent(), self.__handleSubmitNewToonResp)
        self.csm.sendSubmitNewToon(dnaStrand, slot, name, skipTutorial)

    def __handleSubmitNewToonResp(self, avId):
        if self.requestedName is not None:
            self.nameServicesManager.d_requestName(self.requestedName, avId)
            self.requestedName = None
        self.loginFSM.request('avChoose')
        return

    def exitSubmitNewToon(self):
        self.ignore(self.csm.getToonCreatedEvent())
        self.submittingDialog.cleanup()
        del self.submittingDialog

    def enterGameOff(self):
        pass

    def exitGameOff(self):
        pass

    def enterPlayGame(self, status):
        if self.music:
            self.music.stop()
            self.music = None
        base.transitions.noFade()
        if self.localAvChoice == None:
            self.notify.error('called enterPlayGame() without self.localAvChoice being set!')
            return
        if localAvatar.getTutorialCompleted() == 1 or True:
            zoneId = status['zoneId']
            hoodId = status['hoodId']
            avId = status['avId']
            self.playGame.load()
            self.playGame.enter(hoodId, zoneId, avId)
        else:
            self.sendQuietZoneRequest()
            localAvatar.sendUpdate('createTutorial')
        self.myDistrict.d_joining()
        return

    def tutorialCreated(self, zoneId):
        requestStatus = {'zoneId': zoneId}
        self.tutQuietZoneState = QuietZoneState('tutQuietZoneDone')
        self.tutQuietZoneState.load()
        self.tutQuietZoneState.enter(requestStatus)
        self.acceptOnce('tutQuietZoneDone', self.__handleTutQuietZoneDone)

    def __handleTutQuietZoneDone(self):
        self.tutQuietZoneState.exit()
        self.tutQuietZoneState.unload()
        del self.tutQuietZoneState

    def exitPlayGame(self):
        self.ignore('tutQuietZoneDone')
        if hasattr(self, 'tutQuietZoneDone'):
            self.tutQuietZoneState.exit()
            self.tutQuietZoneState.unload()
            del self.tutQuietZoneState
        if self.music:
            self.music.stop()
            self.music = None
        self.playGame.exit()
        self.playGame.unload()
        return

    def enterDisconnect(self, isPlaying, booted=0, bootReason=None):
        self.notify.info('Disconnect details: isPlaying = %s, booted = %s, bootReason = %s' % (
         isPlaying, booted, bootReason))
        style = 3
        if isPlaying == 1:
            if not booted:
                msg = CIGlobals.DisconnectionMsg
        else:
            if not booted:
                msg = CIGlobals.JoinFailureMsg
        if self.isConnected():
            self.sendDisconnect()
        self.disconnectDialog = GlobalDialog(message=msg, style=style, doneEvent='disconnectDone')
        self.disconnectDialog.show()
        if style == 3:
            self.acceptOnce('disconnectDone', sys.exit)
        else:
            self.acceptOnce('disconnectDone', self.handleDisconnectDone)

    def handleDisconnectDone(self):
        value = self.disconnectDialog.getValue()
        if value:
            self.loginFSM.request('connect')
        else:
            sys.exit()

    def exitDisconnect(self):
        self.ignore('disconnectDone')
        self.disconnectDialog.cleanup()
        del self.disconnectDialog

    def renderFrame(self):
        gsg = base.win.getGsg()
        if gsg:
            render2d.prepareScene(gsg)
        base.graphicsEngine.renderFrame()

    def handleDatagram(self, di):
        if self.notify.getDebug():
            print 'ClientRepository received datagram:'
        msgType = self.getMsgType()
        self.currentSenderId = None
        if self.handler == None:
            self.astronHandle(di)
        else:
            self.handler(msgType, di)
        self.considerHeartbeat()
        return

    def astronHandle(self, di):
        AstronClientRepository.handleDatagram(self, di)

    def handleQuietZoneGenerateWithRequired(self, di):
        doId = di.getUint32()
        parentId = di.getUint32()
        zoneId = di.getUint32()
        classId = di.getUint16()
        dclass = self.dclassesByNumber[classId]
        if dclass.getClassDef().neverDisable:
            dclass.startGenerate()
            distObj = self.generateWithRequiredFields(dclass, doId, di, parentId, zoneId)
            dclass.stopGenerate()

    def handleQuietZoneGenerateWithRequiredOther(self, di):
        doId = di.getUint32()
        parentId = di.getUint32()
        zoneId = di.getUint32()
        classId = di.getUint16()
        dclass = self.dclassesByNumber[classId]
        if dclass.getClassDef().neverDisable:
            dclass.startGenerate()
            distObj = self.generateWithRequiredOtherFields(dclass, doId, di, parentId, zoneId)
            dclass.stopGenerate()

    def handleQuietZoneUpdateField(self, di):
        di2 = DatagramIterator(di)
        doId = di2.getUint32()
        if doId in self.deferredDoIds:
            args, deferrable, dg0, updates = self.deferredDoIds[doId]
            dclass = args[2]
            if not dclass.getClassDef().neverDisable:
                return
        else:
            do = self.getDo(doId)
            if do:
                if not do.neverDisable:
                    return
        AstronClientRepository.handleUpdateField(self, di)

    def handleDelete(self, di):
        doId = di.getUint32()
        self.deleteObject(doId)

    def _abandonShard(self):
        for doId, obj in self.doId2do.items():
            if obj.parentId == localAvatar.defaultShard and obj is not localAvatar:
                self.deleteObject(doId)

    def handleEnterObjectRequiredOwner(self, di):
        if self.loginFSM.getCurrentState().getName() == 'waitForSetAvatarResponse':
            doId = di.getUint32()
            parentId = di.getUint32()
            zoneId = di.getUint32()
            dclassId = di.getUint16()
            self.__handleSetAvatarResponse(doId, di)

    def addTaggedInterest(self, parentId, zoneId, mainTag, desc, otherTags=[], event=None):
        return self.addInterest(parentId, zoneId, desc, event)

    def sendSetAvatarMsg(self, choice):
        avId = choice.getAvId()
        self.sendSetAvatarIdMsg(avId)
        self.localAvChoice = choice

    def sendSetAvatarIdMsg(self, avId):
        if avId != self.__currentAvId:
            self.__currentAvId = avId
            self.csm.sendSetAvatar(avId)

    def sendQuietZoneRequest(self):
        self.sendSetZoneMsg(CIGlobals.QuietZone)
예제 #25
0
class Char(Avatar.Avatar):

    def __init__(self):
        try:
            self.Char_initialized
            return
        except:
            self.Char_initialized = 1

        Avatar.Avatar.__init__(self)
        self.avatarType = CIGlobals.CChar
        self.avatarName = None
        self.currentAnim = None
        self.charType = ''
        self.eyes = loader.loadTexture('phase_3/maps/eyes1.jpg', 'phase_3/maps/eyes1_a.rgb')
        self.closedEyes = loader.loadTexture('phase_3/maps/mickey_eyes_closed.jpg', 'phase_3/maps/mickey_eyes_closed_a.rgb')
        self.animFSM = ClassicFSM('Char', [State('off', self.enterOff, self.exitOff),
         State('neutral', self.enterNeutral, self.exitNeutral),
         State('walk', self.enterWalk, self.exitWalk),
         State('run', self.enterRun, self.exitRun)], 'off', 'off')
        animStateList = self.animFSM.getStates()
        self.animFSM.enterInitialState()
        Avatar.Avatar.initializeBodyCollisions(self, self.avatarType, 3.5, 1)
        return

    def getNametagJoints(self):
        return []

    def stopAnimations(self):
        if hasattr(self, 'animFSM'):
            if not self.animFSM.isInternalStateInFlux():
                self.animFSM.request('off')
            else:
                notify.warning('animFSM in flux, state=%s, not requesting off' % self.animFSM.getCurrentState().getName())
        else:
            notify.warning('animFSM has been deleted')

    def disable(self):
        self.stopBlink()
        self.stopAnimations()
        Avatar.Avatar.disable(self)

    def delete(self):
        try:
            self.Char_deleted
        except:
            self.Char_deleted = 1
            del self.animFSM
            Avatar.Avatar.delete(self)

    def setChat(self, chatString):
        if self.charType == CIGlobals.Mickey:
            self.dial = base.audio3d.loadSfx('phase_3/audio/dial/mickey.ogg')
        else:
            if self.charType == CIGlobals.Minnie:
                self.dial = base.audio3d.loadSfx('phase_3/audio/dial/minnie.ogg')
            else:
                if self.charType == CIGlobals.Goofy:
                    self.dial = base.audio3d.loadSfx('phase_6/audio/dial/goofy.ogg')
        base.audio3d.attachSoundToObject(self.dial, self)
        self.dial.play()
        Avatar.Avatar.setChat(self, chatString)

    def setName(self, nameString, charName=None):
        self.avatarName = nameString
        Avatar.Avatar.setName(self, nameString, avatarType=self.avatarType, charName=charName)

    def setupNameTag(self, tempName=None):
        Avatar.Avatar.setupNameTag(self, tempName)
        self.nametag.setNametagColor(NametagGlobals.NametagColors[NametagGlobals.CCNPC])
        self.nametag.setActive(0)
        self.nametag.updateAll()

    def generateChar(self, charType):
        self.charType = charType
        if charType == CIGlobals.Mickey or charType == CIGlobals.Minnie:
            self.loadModel('phase_3/models/char/' + charType.lower() + '-' + str(CIGlobals.ModelDetail(self.avatarType)) + '.bam')
            self.loadAnims({'neutral': 'phase_3/models/char/' + charType.lower() + '-wait.bam', 'walk': 'phase_3/models/char/' + charType.lower() + '-walk.bam', 
               'run': 'phase_3/models/char/' + charType.lower() + '-run.bam', 
               'left-start': 'phase_3.5/models/char/' + charType.lower() + '-left-start.bam', 
               'left': 'phase_3.5/models/char/' + charType.lower() + '-left.bam', 
               'right-start': 'phase_3.5/models/char/' + charType.lower() + '-right-start.bam', 
               'right': 'phase_3.5/models/char/' + charType.lower() + '-right.bam'})
            if charType == CIGlobals.Mickey:
                self.mickeyEye = self.controlJoint(None, 'modelRoot', 'joint_pupilR')
                self.mickeyEye.setY(0.025)
            for bundle in self.getPartBundleDict().values():
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                earNull.clearNetTransforms()

            for bundle in self.getPartBundleDict().values():
                charNodepath = bundle['modelRoot'].partBundleNP
                bundle = bundle['modelRoot'].getBundle()
                earNull = bundle.findChild('sphere3')
                if not earNull:
                    earNull = bundle.findChild('*sphere3')
                ears = charNodepath.find('**/sphere3')
                if ears.isEmpty():
                    ears = charNodepath.find('**/*sphere3')
                ears.clearEffect(CharacterJointEffect.getClassType())
                earRoot = charNodepath.attachNewNode('earRoot')
                earPitch = earRoot.attachNewNode('earPitch')
                earPitch.setP(40.0)
                ears.reparentTo(earPitch)
                earNull.addNetTransform(earRoot.node())
                ears.clearMat()
                ears.node().setPreserveTransform(ModelNode.PTNone)
                ears.setP(-40.0)
                ears.flattenMedium()
                ears.setBillboardAxis()
                self.startBlink()

        else:
            if charType == CIGlobals.Pluto:
                self.loadModel('phase_6/models/char/pluto-1000.bam')
                self.loadAnims({'walk': 'phase_6/models/char/pluto-walk.bam', 'neutral': 'phase_6/models/char/pluto-neutral.bam', 
                   'sit': 'phase_6/models/char/pluto-sit.bam', 
                   'stand': 'phase_6/models/char/pluto-stand.bam'})
            else:
                if charType == CIGlobals.Goofy:
                    self.loadModel('phase_6/models/char/TT_G-1500.bam')
                    self.loadAnims({'neutral': 'phase_6/models/char/TT_GWait.bam', 'walk': 'phase_6/models/char/TT_GWalk.bam'})
                else:
                    raise StandardError('unknown char %s!' % charType)
        Avatar.Avatar.initShadow(self)
        return

    def initializeLocalCollisions(self, name, radius):
        Avatar.Avatar.initializeLocalCollisions(self, radius, 2, name)

    def startBlink(self):
        randomStart = random.uniform(0.5, 5)
        taskMgr.add(self.blinkTask, 'blinkTask')

    def stopBlink(self):
        taskMgr.remove('blinkTask')
        taskMgr.remove('doBlink')
        taskMgr.remove('openEyes')

    def blinkTask(self, task):
        taskMgr.add(self.doBlink, 'doBlink')
        delay = random.uniform(0.5, 7)
        task.delayTime = delay
        return task.again

    def doBlink(self, task):
        self.closeEyes()
        taskMgr.doMethodLater(0.2, self.openEyes, 'openEyes')
        return task.done

    def closeEyes(self):
        self.find('**/joint_pupilR').hide()
        self.find('**/joint_pupilL').hide()
        if self.charType == CIGlobals.Mickey:
            self.mickeyEye.setY(-0.025)
            self.mickeyEye.hide()
        self.find('**/eyes').setTexture(self.closedEyes, 1)

    def openEyes(self, task):
        self.find('**/joint_pupilR').show()
        self.find('**/joint_pupilL').show()
        if self.charType == CIGlobals.Mickey:
            self.mickeyEye.setY(0.025)
            self.mickeyEye.show()
        self.find('**/eyes').setTexture(self.eyes, 1)
        return task.done

    def enterOff(self):
        self.currentAnim = None
        return

    def exitOff(self):
        pass

    def enterNeutral(self):
        self.loop('neutral')

    def exitNeutral(self):
        self.stop()

    def enterWalk(self):
        self.loop('walk')

    def exitWalk(self):
        self.stop()

    def enterRun(self):
        self.loop('run')

    def exitRun(self):
        self.stop()
예제 #26
0
class ToonHead(Actor.Actor):

    notify = DirectNotifyGlobal.directNotify.newCategory('ToonHead')
    EyesOpen = loader.loadTexture('phase_3/maps/eyes.jpg', 'phase_3/maps/eyes_a.rgb')
    EyesOpen.setMinfilter(Texture.FTLinear)
    EyesOpen.setMagfilter(Texture.FTLinear)
    EyesClosed = loader.loadTexture('phase_3/maps/eyesClosed.jpg', 'phase_3/maps/eyesClosed_a.rgb')
    EyesClosed.setMinfilter(Texture.FTLinear)
    EyesClosed.setMagfilter(Texture.FTLinear)
    EyesSadOpen = loader.loadTexture('phase_3/maps/eyesSad.jpg', 'phase_3/maps/eyesSad_a.rgb')
    EyesSadOpen.setMinfilter(Texture.FTLinear)
    EyesSadOpen.setMagfilter(Texture.FTLinear)
    EyesSadClosed = loader.loadTexture('phase_3/maps/eyesSadClosed.jpg', 'phase_3/maps/eyesSadClosed_a.rgb')
    EyesSadClosed.setMinfilter(Texture.FTLinear)
    EyesSadClosed.setMagfilter(Texture.FTLinear)
    EyesAngryOpen = loader.loadTexture('phase_3/maps/eyesAngry.jpg', 'phase_3/maps/eyesAngry_a.rgb')
    EyesAngryOpen.setMinfilter(Texture.FTLinear)
    EyesAngryOpen.setMagfilter(Texture.FTLinear)
    EyesAngryClosed = loader.loadTexture('phase_3/maps/eyesAngryClosed.jpg', 'phase_3/maps/eyesAngryClosed_a.rgb')
    EyesAngryClosed.setMinfilter(Texture.FTLinear)
    EyesAngryClosed.setMagfilter(Texture.FTLinear)
    EyesSurprised = loader.loadTexture('phase_3/maps/eyesSurprised.jpg', 'phase_3/maps/eyesSurprised_a.rgb')
    EyesSurprised.setMinfilter(Texture.FTLinear)
    EyesSurprised.setMagfilter(Texture.FTLinear)
    Muzzle = loader.loadTexture('phase_3/maps/muzzleShrtGeneric.jpg')
    Muzzle.setMinfilter(Texture.FTLinear)
    Muzzle.setMagfilter(Texture.FTLinear)
    MuzzleSurprised = loader.loadTexture('phase_3/maps/muzzleShortSurprised.jpg')
    MuzzleSurprised.setMinfilter(Texture.FTLinear)
    MuzzleSurprised.setMagfilter(Texture.FTLinear)
    LeftA = Point3(0.06, 0.0, 0.14)
    LeftB = Point3(-0.13, 0.0, 0.1)
    LeftC = Point3(-0.05, 0.0, 0.0)
    LeftD = Point3(0.06, 0.0, 0.0)
    RightA = Point3(0.13, 0.0, 0.1)
    RightB = Point3(-0.06, 0.0, 0.14)
    RightC = Point3(-0.06, 0.0, 0.0)
    RightD = Point3(0.05, 0.0, 0.0)
    LeftAD = Point3(LeftA[0] - LeftA[2] * (LeftD[0] - LeftA[0]) / (LeftD[2] - LeftA[2]), 0.0, 0.0)
    LeftBC = Point3(LeftB[0] - LeftB[2] * (LeftC[0] - LeftB[0]) / (LeftC[2] - LeftB[2]), 0.0, 0.0)
    RightAD = Point3(RightA[0] - RightA[2] * (RightD[0] - RightA[0]) / (RightD[2] - RightA[2]), 0.0, 0.0)
    RightBC = Point3(RightB[0] - RightB[2] * (RightC[0] - RightB[0]) / (RightC[2] - RightB[2]), 0.0, 0.0)

    def __init__(self):
        try:
            self.ToonHead_initialized
        except:
            self.ToonHead_initialized = 1
            Actor.Actor.__init__(self)
            self.toonName = 'ToonHead-' + str(self.this)
            self.__blinkName = 'blink-' + self.toonName
            self.__stareAtName = 'stareAt-' + self.toonName
            self.__lookName = 'look-' + self.toonName
            self.lookAtTrack = None
            self.__eyes = None
            self.__eyelashOpen = None
            self.__eyelashClosed = None
            self.__lod500Eyes = None
            self.__lod250Eyes = None
            self.__lpupil = None
            self.__lod500lPupil = None
            self.__lod250lPupil = None
            self.__rpupil = None
            self.__lod500rPupil = None
            self.__lod250rPupil = None
            self.__muzzle = None
            self.__eyesOpen = ToonHead.EyesOpen
            self.__eyesClosed = ToonHead.EyesClosed
            self.__height = 0.0
            self.__eyelashesHiddenByGlasses = False
            self.randGen = random.Random()
            self.randGen.seed(random.random())
            self.eyelids = ClassicFSM('eyelids', [
             State('off', self.enterEyelidsOff, self.exitEyelidsOff, ['open', 'closed', 'surprised']),
             State('open', self.enterEyelidsOpen, self.exitEyelidsOpen, ['closed', 'surprised', 'off']),
             State('surprised', self.enterEyelidsSurprised, self.exitEyelidsSurprised, ['open', 'closed', 'off']),
             State('closed', self.enterEyelidsClosed, self.exitEyelidsClosed, ['open', 'surprised', 'off'])
            ], 'off', 'off')
            self.eyelids.enterInitialState()
            self.emote = None
            self.__stareAtNode = NodePath()
            self.__defaultStarePoint = Point3(0, 0, 0)
            self.__stareAtPoint = self.__defaultStarePoint
            self.__stareAtTime = 0
            self.lookAtPositionCallbackArgs = None

    def delete(self):
        try:
            self.ToonHead_deleted
        except:
            self.ToonHead_deleted = 1
            taskMgr.remove(self.__blinkName)
            taskMgr.remove(self.__lookName)
            taskMgr.remove(self.__stareAtName)
            if self.lookAtTrack:
                self.lookAtTrack.finish()
                self.lookAtTrack = None
            del self.eyelids
            del self.__stareAtNode
            del self.__stareAtPoint
            if self.__eyes:
                del self.__eyes
            if self.__lpupil:
                del self.__lpupil
            if self.__rpupil:
                del self.__rpupil
            if self.__eyelashOpen:
                del self.__eyelashOpen
            if self.__eyelashClosed:
                del self.__eyelashClosed
            self.lookAtPositionCallbackArgs = None
            Actor.Actor.delete(self)

    def setupHead(self, dna, forGui = 0):
        self.__height = self.generateToonHead(1, dna, ('1000',), forGui)
        self.generateToonColor(dna)
        animalStyle = dna.getAnimal()
        bodyScale = ToontownGlobals.toonBodyScales[animalStyle]
        headScale = ToontownGlobals.toonHeadScales[animalStyle]
        self.getGeomNode().setScale(headScale[0] * bodyScale * 1.3, headScale[1] * bodyScale * 1.3, headScale[2] * bodyScale * 1.3)
        if forGui:
            self.getGeomNode().setDepthWrite(1)
            self.getGeomNode().setDepthTest(1)
        if dna.getAnimal() == 'dog':
            self.loop('neutral')

    def fitAndCenterHead(self, maxDim, forGui = 0):
        p1 = Point3()
        p2 = Point3()
        self.calcTightBounds(p1, p2)
        if forGui:
            h = 180
            t = p1[0]
            p1.setX(-p2[0])
            p2.setX(-t)
        else:
            h = 0
        d = p2 - p1
        biggest = max(d[0], d[2])
        s = maxDim / biggest
        mid = (p1 + d / 2.0) * s
        self.setPosHprScale(-mid[0], -mid[1] + 1, -mid[2], h, 0, 0, s, s, s)

    def setLookAtPositionCallbackArgs(self, argTuple):
        self.lookAtPositionCallbackArgs = argTuple

    def getHeight(self):
        return self.__height

    def getRandomForwardLookAtPoint(self):
        x = self.randGen.choice((-0.8, -0.5, 0, 0.5, 0.8))
        z = self.randGen.choice((-0.5, 0, 0.5, 0.8))
        return Point3(x, 1.5, z)

    def findSomethingToLookAt(self):
        if self.lookAtPositionCallbackArgs != None:
            pnt = self.lookAtPositionCallbackArgs[0].getLookAtPosition(self.lookAtPositionCallbackArgs[1], self.lookAtPositionCallbackArgs[2])
            self.startStareAt(self, pnt)
            return
        if self.randGen.random() < 0.33:
            lookAtPnt = self.getRandomForwardLookAtPoint()
        else:
            lookAtPnt = self.__defaultStarePoint
        self.lerpLookAt(lookAtPnt, blink=1)

    def generateToonHead(self, copy, style, lods, forGui=0):
        headStyle = style.head
        fix = None
        if headStyle == 'dls':
            filePrefix = HeadDict['dls']
            headHeight = 0.75
        elif headStyle == 'dss':
            filePrefix = HeadDict['dss']
            headHeight = 0.5
        elif headStyle == 'dsl':
            filePrefix = HeadDict['dsl']
            headHeight = 0.5
        elif headStyle == 'dll':
            filePrefix = HeadDict['dll']
            headHeight = 0.75
        elif headStyle == 'cls':
            filePrefix = HeadDict['c']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'css':
            filePrefix = HeadDict['c']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'csl':
            filePrefix = HeadDict['c']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'cll':
            filePrefix = HeadDict['c']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'hls':
            filePrefix = HeadDict['h']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'hss':
            filePrefix = HeadDict['h']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'hsl':
            filePrefix = HeadDict['h']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'hll':
            filePrefix = HeadDict['h']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'mls':
            filePrefix = HeadDict['m']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'mss':
            filePrefix = HeadDict['m']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'rls':
            filePrefix = HeadDict['r']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'rss':
            filePrefix = HeadDict['r']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'rsl':
            filePrefix = HeadDict['r']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'rll':
            filePrefix = HeadDict['r']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'fls':
            filePrefix = HeadDict['f']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'fss':
            filePrefix = HeadDict['f']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'fsl':
            filePrefix = HeadDict['f']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'fll':
            filePrefix = HeadDict['f']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'pls':
            filePrefix = HeadDict['p']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'pss':
            filePrefix = HeadDict['p']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'psl':
            filePrefix = HeadDict['p']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'pll':
            filePrefix = HeadDict['p']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'bls':
            filePrefix = HeadDict['b']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'bss':
            filePrefix = HeadDict['b']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'bsl':
            filePrefix = HeadDict['b']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'bll':
            filePrefix = HeadDict['b']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        elif headStyle == 'sls':
            filePrefix = HeadDict['s']
            fix = self.__fixHeadLongShort
            headHeight = 0.75
        elif headStyle == 'sss':
            filePrefix = HeadDict['s']
            fix = self.__fixHeadShortShort
            headHeight = 0.5
        elif headStyle == 'ssl':
            filePrefix = HeadDict['s']
            fix = self.__fixHeadShortLong
            headHeight = 0.5
        elif headStyle == 'sll':
            filePrefix = HeadDict['s']
            fix = self.__fixHeadLongLong
            headHeight = 0.75
        else:
            ToonHead.notify.error('unknown head style: %s' % headStyle)
        if len(lods) == 1:
            self.loadModel('phase_3' + filePrefix + lods[0], 'head', 'lodRoot', copy)
            if not copy:
                self.showAllParts('head')
            if fix != None:
                fix(style, None, copy)
            if not forGui:
                self.__lods = lods
                self.__style = style
                self.__headStyle = headStyle
                self.__copy = copy
        else:
            for lod in lods:
                self.loadModel('phase_3' + filePrefix + lod, 'head', lod, copy)
                if not copy:
                    self.showAllParts('head', lod)
                if fix != None:
                    fix(style, lod, copy)
                if not forGui:
                    self.__lods = lods
                    self.__style = style
                    self.__headStyle = headStyle
                    self.__copy = copy
        self.__fixEyes(style, forGui)
        self.setupEyelashes(style)
        self.eyelids.request('closed')
        self.eyelids.request('open')
        self.setupMuzzles(style)
        return headHeight

    def hideEars(self):
        self.findAllMatches('**/ears*;+s').stash()

    def showEars(self):
        self.findAllMatches('**/ears*;+s').unstash()

    def hideEyelashes(self):
        if self.__eyelashOpen:
            self.__eyelashOpen.stash()
        if self.__eyelashClosed:
            self.__eyelashClosed.stash()
        self.__eyelashesHiddenByGlasses = True

    def showEyelashes(self):
        if self.__eyelashOpen:
            self.__eyelashOpen.unstash()
        if self.__eyelashClosed:
            self.__eyelashClosed.unstash()
        self.__eyelashesHiddenByGlasses = False

    def generateToonColor(self, style):
        parts = self.findAllMatches('**/head*')
        parts.setColor(style.getHeadColor())
        animalType = style.getAnimal()
        if animalType == 'cat' or animalType == 'rabbit' or animalType == 'bear' or animalType == 'mouse' or animalType == 'pig':
            parts = self.findAllMatches('**/ear?-*')
            parts.setColor(style.getHeadColor())

    def __fixEyes(self, style, forGui = 0):
        mode = -3
        if forGui:
            mode = -2
        if self.hasLOD():
            for lodName in self.getLODNames():
                self.drawInFront('eyes*', 'head-front*', mode, lodName=lodName)
                if base.config.GetBool('want-new-anims', 1):
                    if not self.find('**/joint_pupil*').isEmpty():
                        self.drawInFront('joint_pupil*', 'eyes*', -1, lodName=lodName)
                    else:
                        self.drawInFront('def_*_pupil', 'eyes*', -1, lodName=lodName)
                else:
                    self.drawInFront('joint_pupil*', 'eyes*', -1, lodName=lodName)

            self.__eyes = self.getLOD(1000).find('**/eyes*')
            self.__lod500Eyes = self.getLOD(500).find('**/eyes*')
            self.__lod250Eyes = self.getLOD(250).find('**/eyes*')
            if self.__lod500Eyes.isEmpty():
                self.__lod500Eyes = None
            else:
                self.__lod500Eyes.setColorOff()
                if base.config.GetBool('want-new-anims', 1):
                    if not self.find('**/joint_pupilL*').isEmpty():
                        self.__lod500lPupil = self.__lod500Eyes.find('**/joint_pupilL*')
                        self.__lod500rPupil = self.__lod500Eyes.find('**/joint_pupilR*')
                    else:
                        self.__lod500lPupil = self.__lod500Eyes.find('**/def_left_pupil*')
                        self.__lod500rPupil = self.__lod500Eyes.find('**/def_right_pupil*')
                else:
                    self.__lod500lPupil = self.__lod500Eyes.find('**/joint_pupilL*')
                    self.__lod500rPupil = self.__lod500Eyes.find('**/joint_pupilR*')
            if self.__lod250Eyes.isEmpty():
                self.__lod250Eyes = None
            else:
                self.__lod250Eyes.setColorOff()
                if base.config.GetBool('want-new-anims', 1):
                    if not self.find('**/joint_pupilL*').isEmpty():
                        self.__lod250lPupil = self.__lod250Eyes.find('**/joint_pupilL*')
                        self.__lod250rPupil = self.__lod250Eyes.find('**/joint_pupilR*')
                    else:
                        self.__lod250lPupil = self.__lod250Eyes.find('**/def_left_pupil*')
                        self.__lod250rPupil = self.__lod250Eyes.find('**/def_right_pupil*')
                else:
                    self.__lod250lPupil = self.__lod250Eyes.find('**/joint_pupilL*')
                    self.__lod250rPupil = self.__lod250Eyes.find('**/joint_pupilR*')
        else:
            self.drawInFront('eyes*', 'head-front*', mode)
            if base.config.GetBool('want-new-anims', 1):
                if not self.find('joint_pupil*').isEmpty():
                    self.drawInFront('joint_pupil*', 'eyes*', -1)
                else:
                    self.drawInFront('def_*_pupil', 'eyes*', -1)
            else:
                self.drawInFront('joint_pupil*', 'eyes*', -1)
            self.__eyes = self.find('**/eyes*')
        if not self.__eyes.isEmpty():
            self.__eyes.setColorOff()
            self.__lpupil = None
            self.__rpupil = None
            if base.config.GetBool('want-new-anims', 1):
                if not self.find('**/joint_pupilL*').isEmpty():
                    if self.getLOD(1000):
                        lp = self.getLOD(1000).find('**/joint_pupilL*')
                        rp = self.getLOD(1000).find('**/joint_pupilR*')
                    else:
                        lp = self.find('**/joint_pupilL*')
                        rp = self.find('**/joint_pupilR*')
                elif not self.getLOD(1000):
                    lp = self.find('**/def_left_pupil*')
                    rp = self.find('**/def_right_pupil*')
                else:
                    lp = self.getLOD(1000).find('**/def_left_pupil*')
                    rp = self.getLOD(1000).find('**/def_right_pupil*')
            else:
                lp = self.__eyes.find('**/joint_pupilL*')
                rp = self.__eyes.find('**/joint_pupilR*')
            if lp.isEmpty() or rp.isEmpty():
                print 'Unable to locate pupils.'
            else:
                leye = self.__eyes.attachNewNode('leye')
                reye = self.__eyes.attachNewNode('reye')
                lmat = Mat4(0.802174, 0.59709, 0, 0, -0.586191, 0.787531, 0.190197, 0, 0.113565, -0.152571, 0.981746, 0, -0.233634, 0.418062, 0.0196875, 1)
                leye.setMat(lmat)
                rmat = Mat4(0.786788, -0.617224, 0, 0, 0.602836, 0.768447, 0.214658, 0, -0.132492, -0.16889, 0.976689, 0, 0.233634, 0.418062, 0.0196875, 1)
                reye.setMat(rmat)
                self.__lpupil = leye.attachNewNode('lpupil')
                self.__rpupil = reye.attachNewNode('rpupil')
                lpt = self.__eyes.attachNewNode('')
                rpt = self.__eyes.attachNewNode('')
                lpt.wrtReparentTo(self.__lpupil)
                rpt.wrtReparentTo(self.__rpupil)
                lp.reparentTo(lpt)
                rp.reparentTo(rpt)
                self.__lpupil.adjustAllPriorities(1)
                self.__rpupil.adjustAllPriorities(1)
                if self.__lod500Eyes:
                    self.__lod500lPupil.adjustAllPriorities(1)
                    self.__lod500rPupil.adjustAllPriorities(1)
                if self.__lod250Eyes:
                    self.__lod250lPupil.adjustAllPriorities(1)
                    self.__lod250rPupil.adjustAllPriorities(1)
                animalType = style.getAnimal()
                if animalType != 'dog':
                    self.__lpupil.flattenStrong()
                    self.__rpupil.flattenStrong()
        return

    def __setPupilDirection(self, x, y):
        if y < 0.0:
            y2 = -y
            left1 = self.LeftAD + (self.LeftD - self.LeftAD) * y2
            left2 = self.LeftBC + (self.LeftC - self.LeftBC) * y2
            right1 = self.RightAD + (self.RightD - self.RightAD) * y2
            right2 = self.RightBC + (self.RightC - self.RightBC) * y2
        else:
            y2 = y
            left1 = self.LeftAD + (self.LeftA - self.LeftAD) * y2
            left2 = self.LeftBC + (self.LeftB - self.LeftBC) * y2
            right1 = self.RightAD + (self.RightA - self.RightAD) * y2
            right2 = self.RightBC + (self.RightB - self.RightBC) * y2
        left0 = Point3(0.0, 0.0, left1[2] - left1[0] * (left2[2] - left1[2]) / (left2[0] - left1[0]))
        right0 = Point3(0.0, 0.0, right1[2] - right1[0] * (right2[2] - right1[2]) / (right2[0] - right1[0]))
        if x < 0.0:
            x2 = -x
            left = left0 + (left2 - left0) * x2
            right = right0 + (right2 - right0) * x2
        else:
            x2 = x
            left = left0 + (left1 - left0) * x2
            right = right0 + (right1 - right0) * x2
        self.__lpupil.setPos(left)
        self.__rpupil.setPos(right)

    def __lookPupilsAt(self, node, point):
        if node != None:
            mat = node.getMat(self.__eyes)
            point = mat.xformPoint(point)
        distance = 1.0
        recip_z = 1.0 / max(0.1, point[1])
        x = distance * point[0] * recip_z
        y = distance * point[2] * recip_z
        x = min(max(x, -1), 1)
        y = min(max(y, -1), 1)
        self.__setPupilDirection(x, y)
        return

    def __lookHeadAt(self, node, point, frac = 1.0, lod = None):
        reachedTarget = 1
        if lod == None:
            head = self.getPart('head', self.getLODNames()[0])
        else:
            head = self.getPart('head', lod)
        if node != None:
            headParent = head.getParent()
            mat = node.getMat(headParent)
            point = mat.xformPoint(point)
        rot = Mat3(0, 0, 0, 0, 0, 0, 0, 0, 0)
        lookAt(rot, Vec3(point), Vec3(0, 0, 1), CSDefault)
        scale = VBase3(0, 0, 0)
        hpr = VBase3(0, 0, 0)
        if decomposeMatrix(rot, scale, hpr, CSDefault):
            hpr = VBase3(min(max(hpr[0], -60), 60), min(max(hpr[1], -20), 30), 0)
            if frac != 1:
                currentHpr = head.getHpr()
                reachedTarget = abs(hpr[0] - currentHpr[0]) < 1.0 and abs(hpr[1] - currentHpr[1]) < 1.0
                hpr = currentHpr + (hpr - currentHpr) * frac
            if lod == None:
                for lodName in self.getLODNames():
                    head = self.getPart('head', lodName)
                    head.setHpr(hpr)

            else:
                head.setHpr(hpr)
        return reachedTarget

    def setupEyelashes(self, style):
        if style.getGender() == 'm':
            if self.__eyelashOpen:
                self.__eyelashOpen.removeNode()
                self.__eyelashOpen = None
            if self.__eyelashClosed:
                self.__eyelashClosed.removeNode()
                self.__eyelashClosed = None
        else:
            if self.__eyelashOpen:
                self.__eyelashOpen.removeNode()
            if self.__eyelashClosed:
                self.__eyelashClosed.removeNode()
            animal = style.head[0]
            model = loader.loadModel('phase_3' + EyelashDict[animal])
            if self.hasLOD():
                head = self.getPart('head', '1000')
            else:
                head = self.getPart('head', 'lodRoot')
            length = style.head[1]
            if length == 'l':
                openString = 'open-long'
                closedString = 'closed-long'
            else:
                openString = 'open-short'
                closedString = 'closed-short'
            self.__eyelashOpen = model.find('**/' + openString).copyTo(head)
            self.__eyelashClosed = model.find('**/' + closedString).copyTo(head)
            model.removeNode()
        return

    def __fixHeadLongLong(self, style, lodName=None, copy=1):
        if lodName == None:
            searchRoot = self
        else:
            searchRoot = self.find('**/' + str(lodName))
        otherParts = searchRoot.findAllMatches('**/*short*')
        for partNum in range(0, otherParts.getNumPaths()):
            if copy:
                otherParts.getPath(partNum).removeNode()
            else:
                otherParts.getPath(partNum).stash()

    def __fixHeadLongShort(self, style, lodName=None, copy=1):
        animalType = style.getAnimal()
        headStyle = style.head
        if lodName == None:
            searchRoot = self
        else:
            searchRoot = self.find('**/' + str(lodName))
        if animalType != 'duck' and animalType != 'horse':
            if animalType == 'rabbit':
                if copy:
                    searchRoot.find('**/ears-long').removeNode()
                else:
                    searchRoot.find('**/ears-long').hide()
            elif copy:
                searchRoot.find('**/ears-short').removeNode()
            else:
                searchRoot.find('**/ears-short').hide()
        if animalType != 'rabbit':
            if copy:
                searchRoot.find('**/eyes-short').removeNode()
            else:
                searchRoot.find('**/eyes-short').hide()
        if animalType != 'dog':
            if copy:
                searchRoot.find('**/joint_pupilL_short').removeNode()
                searchRoot.find('**/joint_pupilR_short').removeNode()
            else:
                searchRoot.find('**/joint_pupilL_short').stash()
                searchRoot.find('**/joint_pupilR_short').stash()
        if copy:
            self.find('**/head-short').removeNode()
            self.find('**/head-front-short').removeNode()
        else:
            self.find('**/head-short').hide()
            self.find('**/head-front-short').hide()
        if animalType != 'rabbit':
            muzzleParts = searchRoot.findAllMatches('**/muzzle-long*')
            for partNum in range(0, muzzleParts.getNumPaths()):
                if copy:
                    muzzleParts.getPath(partNum).removeNode()
                else:
                    muzzleParts.getPath(partNum).hide()
        else:
            muzzleParts = searchRoot.findAllMatches('**/muzzle-short*')
            for partNum in range(0, muzzleParts.getNumPaths()):
                if copy:
                    muzzleParts.getPath(partNum).removeNode()
                else:
                    muzzleParts.getPath(partNum).hide()

    def __fixHeadShortLong(self, style, lodName=None, copy=1):
        animalType = style.getAnimal()
        headStyle = style.head
        if lodName == None:
            searchRoot = self
        else:
            searchRoot = self.find('**/' + str(lodName))
        if animalType != 'duck' and animalType != 'horse':
            if animalType == 'rabbit':
                if copy:
                    searchRoot.find('**/ears-short').removeNode()
                else:
                    searchRoot.find('**/ears-short').hide()
            elif copy:
                searchRoot.find('**/ears-long').removeNode()
            else:
                searchRoot.find('**/ears-long').hide()
        if animalType != 'rabbit':
            if copy:
                searchRoot.find('**/eyes-long').removeNode()
            else:
                searchRoot.find('**/eyes-long').hide()
        if animalType != 'dog':
            if copy:
                searchRoot.find('**/joint_pupilL_long').removeNode()
                searchRoot.find('**/joint_pupilR_long').removeNode()
            else:
                searchRoot.find('**/joint_pupilL_long').stash()
                searchRoot.find('**/joint_pupilR_long').stash()
        if copy:
            searchRoot.find('**/head-long').removeNode()
            searchRoot.find('**/head-front-long').removeNode()
        else:
            searchRoot.find('**/head-long').hide()
            searchRoot.find('**/head-front-long').hide()
        if animalType != 'rabbit':
            muzzleParts = searchRoot.findAllMatches('**/muzzle-short*')
            for partNum in range(0, muzzleParts.getNumPaths()):
                if copy:
                    muzzleParts.getPath(partNum).removeNode()
                else:
                    muzzleParts.getPath(partNum).hide()
        else:
            muzzleParts = searchRoot.findAllMatches('**/muzzle-long*')
            for partNum in range(0, muzzleParts.getNumPaths()):
                if copy:
                    muzzleParts.getPath(partNum).removeNode()
                else:
                    muzzleParts.getPath(partNum).hide()

    def __fixHeadShortShort(self, style, lodName=None, copy=1):
        if lodName == None:
            searchRoot = self
        else:
            searchRoot = self.find('**/' + str(lodName))
        otherParts = searchRoot.findAllMatches('**/*long*')
        for partNum in range(0, otherParts.getNumPaths()):
            if copy:
                otherParts.getPath(partNum).removeNode()
            else:
                otherParts.getPath(partNum).stash()

    def __blinkOpenEyes(self, task):
        if self.eyelids.getCurrentState().getName() == 'closed':
            self.eyelids.request('open')
        r = self.randGen.random()
        if r < 0.1:
            t = 0.2
        else:
            t = r * 4.0 + 1.0
        taskMgr.doMethodLater(t, self.__blinkCloseEyes, self.__blinkName)
        return Task.done

    def __blinkCloseEyes(self, task):
        if self.eyelids.getCurrentState().getName() != 'open':
            taskMgr.doMethodLater(4.0, self.__blinkCloseEyes, self.__blinkName)
        else:
            self.eyelids.request('closed')
            taskMgr.doMethodLater(0.125, self.__blinkOpenEyes, self.__blinkName)
        return Task.done

    def startBlink(self):
        taskMgr.remove(self.__blinkName)
        if self.__eyes:
            self.openEyes()
        taskMgr.doMethodLater(self.randGen.random() * 4.0 + 1, self.__blinkCloseEyes, self.__blinkName)

    def stopBlink(self):
        taskMgr.remove(self.__blinkName)
        if self.__eyes:
            self.eyelids.request('open')

    def closeEyes(self):
        self.eyelids.request('closed')

    def openEyes(self):
        self.eyelids.request('open')

    def surpriseEyes(self):
        self.eyelids.request('surprised')

    def sadEyes(self):
        self.__eyesOpen = ToonHead.EyesSadOpen
        self.__eyesClosed = ToonHead.EyesSadClosed

    def angryEyes(self):
        self.__eyesOpen = ToonHead.EyesAngryOpen
        self.__eyesClosed = ToonHead.EyesAngryClosed

    def normalEyes(self):
        self.__eyesOpen = ToonHead.EyesOpen
        self.__eyesClosed = ToonHead.EyesClosed

    def blinkEyes(self):
        taskMgr.remove(self.__blinkName)
        self.eyelids.request('closed')
        taskMgr.doMethodLater(0.1, self.__blinkOpenEyes, self.__blinkName)

    def __stareAt(self, task):
        frac = 2 * globalClock.getDt()
        reachedTarget = self.__lookHeadAt(self.__stareAtNode, self.__stareAtPoint, frac)
        self.__lookPupilsAt(self.__stareAtNode, self.__stareAtPoint)
        if reachedTarget and self.__stareAtNode == None:
            return Task.done
        else:
            return Task.cont
        return

    def doLookAroundToStareAt(self, node, point):
        self.startStareAt(node, point)
        self.startLookAround()

    def startStareAtHeadPoint(self, point):
        self.startStareAt(self, point)

    def startStareAt(self, node, point):
        taskMgr.remove(self.__stareAtName)
        if self.lookAtTrack:
            self.lookAtTrack.finish()
            self.lookAtTrack = None
        self.__stareAtNode = node
        if point != None:
            self.__stareAtPoint = point
        else:
            self.__stareAtPoint = self.__defaultStarePoint
        self.__stareAtTime = globalClock.getFrameTime()
        taskMgr.add(self.__stareAt, self.__stareAtName)
        return

    def lerpLookAt(self, point, time = 1.0, blink = 0):
        taskMgr.remove(self.__stareAtName)
        if self.lookAtTrack:
            self.lookAtTrack.finish()
            self.lookAtTrack = None
        lodNames = self.getLODNames()
        if lodNames:
            lodName = lodNames[0]
        else:
            return 0
        head = self.getPart('head', lodName)
        startHpr = head.getHpr()
        startLpupil = self.__lpupil.getPos()
        startRpupil = self.__rpupil.getPos()
        self.__lookHeadAt(None, point, lod=lodName)
        self.__lookPupilsAt(None, point)
        endHpr = head.getHpr()
        endLpupil = self.__lpupil.getPos() * 0.5
        endRpupil = self.__rpupil.getPos() * 0.5
        head.setHpr(startHpr)
        self.__lpupil.setPos(startLpupil)
        self.__rpupil.setPos(startRpupil)
        if startHpr.almostEqual(endHpr, 10):
            return 0
        if blink:
            self.blinkEyes()
        lookToTgt_TimeFraction = 0.2
        lookToTgtTime = time * lookToTgt_TimeFraction
        returnToEyeCenterTime = time - lookToTgtTime - 0.5
        origin = Point3(0, 0, 0)
        blendType = 'easeOut'
        self.lookAtTrack = Parallel(Sequence(LerpPosInterval(self.__lpupil, lookToTgtTime, endLpupil, blendType=blendType), Wait(0.5), LerpPosInterval(self.__lpupil, returnToEyeCenterTime, origin, blendType=blendType)), Sequence(LerpPosInterval(self.__rpupil, lookToTgtTime, endRpupil, blendType=blendType), Wait(0.5), LerpPosInterval(self.__rpupil, returnToEyeCenterTime, origin, blendType=blendType)), name=self.__stareAtName)
        for lodName in self.getLODNames():
            head = self.getPart('head', lodName)
            self.lookAtTrack.append(LerpHprInterval(head, time, endHpr, blendType='easeInOut'))
        self.lookAtTrack.start()
        return 1

    def stopStareAt(self):
        self.lerpLookAt(Vec3.forward())

    def stopStareAtNow(self):
        taskMgr.remove(self.__stareAtName)
        if self.lookAtTrack:
            self.lookAtTrack.finish()
            self.lookAtTrack = None
        if self.__lpupil and self.__rpupil:
            self.__setPupilDirection(0, 0)
        for lodName in self.getLODNames():
            head = self.getPart('head', lodName)
            head.setHpr(0, 0, 0)

    def __lookAround(self, task):
        self.findSomethingToLookAt()
        t = self.randGen.random() * 4.0 + 3.0
        taskMgr.doMethodLater(t, self.__lookAround, self.__lookName)
        return Task.done

    def startLookAround(self):
        taskMgr.remove(self.__lookName)
        t = self.randGen.random() * 5.0 + 2.0
        taskMgr.doMethodLater(t, self.__lookAround, self.__lookName)

    def stopLookAround(self):
        taskMgr.remove(self.__lookName)
        self.stopStareAt()

    def stopLookAroundNow(self):
        taskMgr.remove(self.__lookName)
        self.stopStareAtNow()

    def enterEyelidsOff(self):
        return None

    def exitEyelidsOff(self):
        return None

    def enterEyelidsOpen(self):
        if not self.__eyes.isEmpty():
            self.__eyes.setTexture(self.__eyesOpen, 1)
            if self.__eyelashOpen:
                self.__eyelashOpen.show()
            if self.__eyelashClosed:
                self.__eyelashClosed.hide()
            if self.__lod500Eyes:
                self.__lod500Eyes.setTexture(self.__eyesOpen, 1)
            if self.__lod250Eyes:
                self.__lod250Eyes.setTexture(self.__eyesOpen, 1)
            if self.__lpupil:
                self.__lpupil.show()
                self.__rpupil.show()
            if self.__lod500lPupil:
                self.__lod500lPupil.show()
                self.__lod500rPupil.show()
            if self.__lod250lPupil:
                self.__lod250lPupil.show()
                self.__lod250rPupil.show()

    def exitEyelidsOpen(self):
        return None

    def enterEyelidsClosed(self):
        if not self.__eyes.isEmpty() and self.__eyesClosed:
            self.__eyes.setTexture(self.__eyesClosed, 1)
            if self.__eyelashOpen:
                self.__eyelashOpen.hide()
            if self.__eyelashClosed:
                self.__eyelashClosed.show()
            if self.__lod500Eyes:
                self.__lod500Eyes.setTexture(self.__eyesClosed, 1)
            if self.__lod250Eyes:
                self.__lod250Eyes.setTexture(self.__eyesClosed, 1)
            if self.__lpupil:
                self.__lpupil.hide()
                self.__rpupil.hide()
            if self.__lod500lPupil:
                self.__lod500lPupil.hide()
                self.__lod500rPupil.hide()
            if self.__lod250lPupil:
                self.__lod250lPupil.hide()
                self.__lod250rPupil.hide()

    def exitEyelidsClosed(self):
        return None

    def enterEyelidsSurprised(self):
        if not self.__eyes.isEmpty() and ToonHead.EyesSurprised:
            self.__eyes.setTexture(ToonHead.EyesSurprised, 1)
            if self.__eyelashOpen:
                self.__eyelashOpen.hide()
            if self.__eyelashClosed:
                self.__eyelashClosed.hide()
            if self.__lod500Eyes:
                self.__lod500Eyes.setTexture(ToonHead.EyesSurprised, 1)
            if self.__lod250Eyes:
                self.__lod250Eyes.setTexture(ToonHead.EyesSurprised, 1)
            if self.__muzzle:
                self.__muzzle.setTexture(ToonHead.MuzzleSurprised, 1)
            if self.__lpupil:
                self.__lpupil.show()
                self.__rpupil.show()
            if self.__lod500lPupil:
                self.__lod500lPupil.show()
                self.__lod500rPupil.show()
            if self.__lod250lPupil:
                self.__lod250lPupil.show()
                self.__lod250rPupil.show()

    def exitEyelidsSurprised(self):
        if self.__muzzle:
            self.__muzzle.setTexture(ToonHead.Muzzle, 1)

    def setupMuzzles(self, style):
        self.__muzzles = []
        self.__surpriseMuzzles = []
        self.__angryMuzzles = []
        self.__sadMuzzles = []
        self.__smileMuzzles = []
        self.__laughMuzzles = []
        def hideAddNonEmptyItemToList(item, list):
            if not item.isEmpty():
                item.hide()
                list.append(item)

        def hideNonEmptyItem(item):
            if not item.isEmpty():
                item.hide()

        if self.hasLOD():
            for lodName in self.getLODNames():
                animal = style.getAnimal()
                if animal != 'dog':
                    muzzle = self.find('**/' + lodName + '/**/muzzle*neutral')
                else:
                    muzzle = self.find('**/' + lodName + '/**/muzzle*')
                    if lodName == '1000' or lodName == '500':
                        filePrefix = DogMuzzleDict[style.head]
                        muzzles = loader.loadModel('phase_3' + filePrefix + lodName)
                        if base.config.GetBool('want-new-anims', 1):
                            if not self.find('**/' + lodName + '/**/__Actor_head/def_head').isEmpty():
                                muzzles.reparentTo(self.find('**/' + lodName + '/**/__Actor_head/def_head'))
                            else:
                                muzzles.reparentTo(self.find('**/' + lodName + '/**/joint_toHead'))
                        elif self.find('**/' + lodName + '/**/joint_toHead'):
                            muzzles.reparentTo(self.find('**/' + lodName + '/**/joint_toHead'))
                surpriseMuzzle = self.find('**/' + lodName + '/**/muzzle*surprise')
                angryMuzzle = self.find('**/' + lodName + '/**/muzzle*angry')
                sadMuzzle = self.find('**/' + lodName + '/**/muzzle*sad')
                smileMuzzle = self.find('**/' + lodName + '/**/muzzle*smile')
                laughMuzzle = self.find('**/' + lodName + '/**/muzzle*laugh')
                self.__muzzles.append(muzzle)
                hideAddNonEmptyItemToList(surpriseMuzzle, self.__surpriseMuzzles)
                hideAddNonEmptyItemToList(angryMuzzle, self.__angryMuzzles)
                hideAddNonEmptyItemToList(sadMuzzle, self.__sadMuzzles)
                hideAddNonEmptyItemToList(smileMuzzle, self.__smileMuzzles)
                hideAddNonEmptyItemToList(laughMuzzle, self.__laughMuzzles)
        else:
            if style.getAnimal() != 'dog':
                muzzle = self.find('**/muzzle*neutral')
            else:
                muzzle = self.find('**/muzzle*')
                filePrefix = DogMuzzleDict[style.head]
                muzzles = loader.loadModel('phase_3' + filePrefix + '1000')
                if base.config.GetBool('want-new-anims', 1):
                    if not self.find('**/def_head').isEmpty():
                        muzzles.reparentTo(self.find('**/def_head'))
                    else:
                        muzzles.reparentTo(self.find('**/joint_toHead'))
                else:
                    muzzles.reparentTo(self.find('**/joint_toHead'))
            surpriseMuzzle = self.find('**/muzzle*surprise')
            angryMuzzle = self.find('**/muzzle*angry')
            sadMuzzle = self.find('**/muzzle*sad')
            smileMuzzle = self.find('**/muzzle*smile')
            laughMuzzle = self.find('**/muzzle*laugh')
            self.__muzzles.append(muzzle)
            hideAddNonEmptyItemToList(surpriseMuzzle, self.__surpriseMuzzles)
            hideAddNonEmptyItemToList(angryMuzzle, self.__angryMuzzles)
            hideAddNonEmptyItemToList(sadMuzzle, self.__sadMuzzles)
            hideAddNonEmptyItemToList(smileMuzzle, self.__smileMuzzles)
            hideAddNonEmptyItemToList(laughMuzzle, self.__laughMuzzles)

    def getMuzzles(self):
        return self.__muzzles

    def getSurpriseMuzzles(self):
        return self.__surpriseMuzzles

    def getAngryMuzzles(self):
        return self.__angryMuzzles

    def getSadMuzzles(self):
        return self.__sadMuzzles

    def getSmileMuzzles(self):
        return self.__smileMuzzles

    def getLaughMuzzles(self):
        return self.__laughMuzzles

    def showNormalMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__muzzles)):
            self.__muzzles[muzzleNum].show()

    def hideNormalMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__muzzles)):
            self.__muzzles[muzzleNum].hide()

    def showAngryMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__angryMuzzles)):
            self.__angryMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideAngryMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__angryMuzzles)):
            self.__angryMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def showSadMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__sadMuzzles)):
            self.__sadMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideSadMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__sadMuzzles)):
            self.__sadMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def showSmileMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__smileMuzzles)):
            self.__smileMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideSmileMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__smileMuzzles)):
            self.__smileMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def showLaughMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__laughMuzzles)):
            self.__laughMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideLaughMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__laughMuzzles)):
            self.__laughMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def showSurpriseMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__surpriseMuzzles)):
            self.__surpriseMuzzles[muzzleNum].show()
            self.__muzzles[muzzleNum].hide()

    def hideSurpriseMuzzle(self):
        if self.isIgnoreCheesyEffect():
            return
        for muzzleNum in range(len(self.__surpriseMuzzles)):
            self.__surpriseMuzzles[muzzleNum].hide()
            self.__muzzles[muzzleNum].show()

    def isIgnoreCheesyEffect(self):
        if hasattr(self, 'savedCheesyEffect'):
            if self.savedCheesyEffect == 10 or self.savedCheesyEffect == 11 or self.savedCheesyEffect == 12 or self.savedCheesyEffect == 13 or self.savedCheesyEffect == 14:
                return True
        return False