Exemple #1
0
class SeaMonster(UsesAnimationMixer, Avatar.Avatar, UsesEffectNode):
    FailsafeAnims = (('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0))
    SfxNames = {
        'death': SoundGlobals.SFX_MONSTER_DEATH }
    sfx = { }
    actor = None
    animInfo = { }
    
    class AnimationMixer(AnimationMixer):
        LOOP = AnimationMixer.LOOP
        ACTION = dict(AnimationMixer.ACTION)
        ACTION['MOVIE'] = AnimationMixer.ACTION_INDEX + 1

    
    def __init__(self, animationMixer = None):
        Avatar.Avatar.__init__(self)
        UsesEffectNode.__init__(self)
        self.setPickable(0)
        self.shadowFileName = 'models/misc/drop_shadow'
        self.nameText = None
        self.avatarType = None
        if not SeaMonster.sfx:
            for name in SeaMonster.SfxNames:
                SeaMonster.sfx[name] = loadSfx(SeaMonster.SfxNames[name])
            
        
        OTPRender.renderReflection(False, self, 'p_creature', None)
        animationMixer = self.AnimationMixer
        UsesAnimationMixer.__init__(self, animationMixer)

    
    def delete(self):
        Avatar.Avatar.delete(self)

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

    
    def generateSeaMonster(self):
        if self.actor:
            self.copyActor(self.actor)
        

    
    def setAvatarType(self, avatarType):
        if self.avatarType:
            self.initializeNametag3d()
            return None
        else:
            self.avatarType = avatarType
            self.height = EnemyGlobals.getHeight(avatarType)
            self.initializeDropShadow()
            self.initializeNametag3d()

    
    def initializeNametag3d(self):
        if self.avatarType.isA(AvatarType(base = AvatarTypes.Animal)):
            return None
        
        Avatar.Avatar.initializeNametag3d(self)
        self.nametag3d.setH(self.getGeomNode().getH())
        self.nametag3d.setFogOff()
        self.nametag3d.setLightOff()
        self.nametag3d.setColorScaleOff(100)
        self.nametag.setFont(PiratesGlobals.getPirateBoldOutlineFont())
        self.iconNodePath = self.nametag.getNameIcon()
        if self.iconNodePath.isEmpty():
            self.notify.warning('empty iconNodePath in initializeNametag3d')
            return 0
        
        if not self.nameText:
            self.nameText = OnscreenText(fg = Vec4(1, 1, 1, 1), bg = Vec4(0, 0, 0, 0), scale = 1.1000000000000001, align = TextNode.ACenter, mayChange = 1, font = PiratesGlobals.getPirateBoldOutlineFont())
            self.nameText.reparentTo(self.iconNodePath)
            self.nameText.setTransparency(TransparencyAttrib.MDual, 2)
            self.nameText.setColorScaleOff(100)
            self.nameText.setLightOff()
            self.nameText.setFogOff()
            scale = self.nametag3d.getScale(render)
            self.nameText.setScale(1.0 / scale[0])
        

    
    def scaleAnimRate(self, forwardSpeed):
        rate = 1.0
        myMaxSpeed = self.getMaxSpeed()
        if myMaxSpeed > 0 and forwardSpeed > 0:
            currTime = globalClockDelta.globalClock.getFrameTime()
            maxSpeed = myMaxSpeed * (currTime - self.prevSpeedClock)
            prevTime = self.prevSpeedClock
            self.prevSpeedClock = currTime
            rate = min(1.25, forwardSpeed / maxSpeed)
        
        return rate

    
    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 getAirborneHeight(self):
        return 0.0

    
    def getMaxSpeed(self):
        return 0

    
    def getRadius(self):
        return self.battleTubeRadius

    
    def play(self, *args, **kwArgs):
        UsesAnimationMixer.play(self, *args, **args)

    
    def loop(self, *args, **kwArgs):
        UsesAnimationMixer.loop(self, *args, **args)

    
    def pingpong(self, *args, **kwArgs):
        UsesAnimationMixer.pingpong(self, *args, **args)

    
    def pose(self, *args, **kwArgs):
        UsesAnimationMixer.pose(self, *args, **args)

    
    def stop(self, *args, **kwArgs):
        UsesAnimationMixer.stop(self, *args, **args)

    
    def getDeathAnimName(self, animNum = None):
        animStrings = [
            'death']
        if animNum not in range(len(animStrings)):
            animNum = random.choice([
                0])
        
        return animStrings[animNum]

    
    def getAnimInfo(self, state):
        return self.animInfo.get(state, self.FailsafeAnims)

    
    def setupAnimInfoState(cls, state, info):
        if len(info) < len(cls.FailsafeAnims):
            info += cls.FailsafeAnims[len(info) - len(cls.FailsafeAnims):]
        
        cls.animInfo[state] = info

    setupAnimInfoState = classmethod(setupAnimInfoState)
    
    def setupAnimInfo(cls):
        cls.setupAnimInfoState('LandRoam', cls.FailsafeAnims)
        cls.setupAnimInfoState('WaterRoam', cls.FailsafeAnims)

    setupAnimInfo = classmethod(setupAnimInfo)
    
    def setupAnims(cls):
        cls.animInfo = copy.copy(SeaMonster.animInfo)
        cls.setupAnimInfo()
        filePrefix = cls.ModelInfo[1]
        animList = cls.AnimList
        animDict = { }
        for anim in animList:
            animDict[anim[0]] = filePrefix + anim[1]
        
        cls.animDict = animDict

    setupAnims = classmethod(setupAnims)
    
    def setLODs(self):
        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,
                6,
                20,
                280]
        else:
            raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
        self.addLOD('low', dist[3], dist[2])
        self.addLOD('med', dist[2], dist[1])
        self.addLOD('hi', dist[1], dist[0])

    
    def setupAssets(cls):
        cls.animInfo = copy.copy(SeaMonster.animInfo)
        cls.setupAnimInfo()
        filePrefix = cls.ModelInfo[1]
        animList = cls.AnimList
        animDict = { }
        for anim in animList:
            animDict[anim[0]] = filePrefix + anim[1]
        
        cls.animDict = animDict
        filePrefix = cls.ModelInfo[1]
        for name in cls.SfxNames:
            cls.sfx[name] = loadSfx(cls.SfxNames[name])
        
        cls.actor = Actor.Actor()
        if loader.loadModel(filePrefix + 'med') != None:
            avatarDetail = base.config.GetString('avatar-detail', 'high')
            if avatarDetail == 'high':
                dist = [
                    0,
                    200,
                    800,
                    2800]
            elif avatarDetail == 'med':
                dist = [
                    0,
                    100,
                    400,
                    2800]
            elif avatarDetail == 'low':
                dist = [
                    0,
                    60,
                    200,
                    2800]
            else:
                raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
            cls.actor.setLODNode()
            cls.actor.addLOD('low', dist[3], dist[2])
            cls.actor.addLOD('med', dist[2], dist[1])
            cls.actor.addLOD('hi', dist[1], dist[0])
            cls.actor.loadModel(filePrefix + 'hi', 'modelRoot', 'hi')
            cls.actor.loadModel(filePrefix + 'med', 'modelRoot', 'med')
            cls.actor.loadModel(filePrefix + 'low', 'modelRoot', 'low')
            cls.actor.loadAnims(cls.animDict, 'modelRoot', 'all')
        else:
            cls.actor.loadModel(cls.ModelInfo[0])
            cls.actor.loadAnims(cls.animDict)
        cls.actor.getGeomNode().setH(180)

    setupAssets = classmethod(setupAssets)
class Creature(UsesAnimationMixer, Avatar.Avatar, UsesEffectNode):
    __module__ = __name__
    FailsafeAnims = (('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0))
    SfxNames = {'death': SoundGlobals.SFX_MONSTER_DEATH}
    sfx = {}
    actor = None
    animInfo = {}

    class AnimationMixer(AnimationMixer):
        __module__ = __name__
        LOOP = AnimationMixer.LOOP
        ACTION = dict(AnimationMixer.ACTION)
        ACTION['MOVIE'] = AnimationMixer.ACTION_INDEX + 1

    def __init__(self, animationMixer=None):
        Avatar.Avatar.__init__(self)
        UsesEffectNode.__init__(self)
        self.setPickable(0)
        self.shadowFileName = 'models/misc/drop_shadow'
        self.dimensions = VBase3(0.0, 0.0, 0.0)
        self.nameText = None
        self.avatarType = None
        self.level = None
        self.nametagOffset = 2.0
        self.headNode = self.find('**/def_head')
        if not Creature.sfx:
            for name in Creature.SfxNames:
                Creature.sfx[name] = loadSfx(Creature.SfxNames[name])

        self.setupReflection()
        animationMixer = animationMixer or self.AnimationMixer
        UsesAnimationMixer.__init__(self, animationMixer)
        self.deathEffect = None
        self.shockwaveRingIval = None
        self.shockwaveRingEffect = None
        self.spiritIval = None
        self.spiritEffect = None
        return

    def delete(self):
        if self.deathEffect:
            self.deathEffect.stop()
            self.deathEffect = None
        if self.shockwaveRingIval:
            self.shockwaveRingIval.pause()
            self.shockwaveRingIval = None
        if self.shockwaveRingEffect:
            self.shockwaveRingEffect.stop()
            self.shockwaveRingEffect = None
        if self.spiritIval:
            self.spiritIval.pause()
            self.spiritIval = None
        if self.spiritEffect:
            self.spiritEffect.stop()
            self.spiritEffect = None
        Avatar.Avatar.delete(self)
        UsesAnimationMixer.delete(self)
        UsesEffectNode.delete(self)
        return

    def setupReflection(self):
        OTPRender.renderReflection(False, self, 'p_creature', None)
        return

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

    def generateCreature(self):
        if self.actor:
            self.copyActor(self.actor)
        self.headNode = self.find('**/def_head')
        if base.options.character_detail_level == PiratesGlobals.CD_LOW:
            self.setLODAnimation(100, 5, 0.1)
        self.enableMixing()

    @report(types=['module', 'args'], dConfigParam='nametag')
    def setAvatarType(self, avatarType):
        self.avatarType = avatarType
        self.height = EnemyGlobals.getHeight(avatarType)
        self.initializeDropShadow()
        if base.options.terrain_detail_level == PiratesGlobals.CD_LOW:
            self.shadowPlacer.off()
        self.initializeNametag3d()

    def setLevel(self, level):
        self.level = level

    def getLevel(self):
        return self.level

    @report(types=['module', 'args'], dConfigParam='nametag')
    def initializeNametag3d(self):
        Avatar.Avatar.initializeNametag3d(self)
        self.nametag3d.setFogOff()
        self.nametag3d.setLightOff()
        self.nametag3d.setColorScaleOff(100)
        self.nametag3d.setH(self.getGeomNode().getH())
        self.nametag.setFont(PiratesGlobals.getPirateBoldOutlineFont())
        self.iconNodePath = self.nametag.getNameIcon()
        if self.iconNodePath.isEmpty():
            self.notify.warning('empty iconNodePath in initializeNametag3d')
            return 0
        if not self.nameText:
            self.nameText = OnscreenText(
                fg=Vec4(1, 1, 1, 1),
                bg=Vec4(0, 0, 0, 0),
                scale=1.1,
                align=TextNode.ACenter,
                mayChange=1,
                font=PiratesGlobals.getPirateBoldOutlineFont())
            self.nameText.reparentTo(self.iconNodePath)
            self.nameText.setTransparency(TransparencyAttrib.MDual, 2)
            self.nameText.setColorScaleOff(100)
            self.nameText.setLightOff()
            self.nameText.setFogOff()

    def initializeNametag3dPet(self):
        pass

    def getNameText(self):
        return self.nameText

    def scaleAnimRate(self, forwardSpeed):
        rate = 1.0
        myMaxSpeed = self.getMaxSpeed()
        if myMaxSpeed > 0 and forwardSpeed > 0:
            currTime = globalClockDelta.globalClock.getFrameTime()
            maxSpeed = myMaxSpeed * (currTime - self.prevSpeedClock)
            prevTime = self.prevSpeedClock
            self.prevSpeedClock = currTime
            rate = min(1.25, forwardSpeed / maxSpeed)
        return rate

    @report(types=['module', 'args'], dConfigParam='nametag')
    def getNametagJoints(self):
        joints = []
        for lodName in self.getLODNames():
            bundle = self.getPartBundle('modelRoot', lodName)
            joint = bundle.findChild('name_tag')
            if joint:
                joints.append(joint)

        return joints

    def adjustNametag3d(self, parentScale=1.0):
        self.nametag3d.setZ(self.scale * parentScale * self.nametagOffset -
                            self.nametagOffset)

    def getAirborneHeight(self):
        return 0.0

    def getMaxSpeed(self):
        return 0

    def getRadius(self):
        return self.battleTubeRadius

    def play(self, *args, **kwArgs):
        UsesAnimationMixer.play(self, *args, **kwArgs)

    def loop(self, *args, **kwArgs):
        UsesAnimationMixer.loop(self, *args, **kwArgs)

    def pingpong(self, *args, **kwArgs):
        UsesAnimationMixer.pingpong(self, *args, **kwArgs)

    def pose(self, *args, **kwArgs):
        UsesAnimationMixer.pose(self, *args, **kwArgs)

    def stop(self, *args, **kwArgs):
        UsesAnimationMixer.stop(self, *args, **kwArgs)

    def getDeathAnimName(self, animNum=None):
        animStrings = ['death']
        if animNum not in range(len(animStrings)):
            animNum = random.choice([0])
        return animStrings[animNum]

    def getAnimInfo(self, state):
        return self.animInfo.get(state, self.FailsafeAnims)

    @classmethod
    def setupAnimInfoState(cls, state, info):
        if len(info) < len(cls.FailsafeAnims):
            info += cls.FailsafeAnims[len(info) - len(cls.FailsafeAnims):]
        cls.animInfo[state] = info

    @classmethod
    def setupAnimInfo(cls):
        cls.setupAnimInfoState('LandRoam', cls.FailsafeAnims)
        cls.setupAnimInfoState('WaterRoam', cls.FailsafeAnims)

    def setLODs(self):
        avatarDetail = base.config.GetString('avatar-detail', 'high')
        if avatarDetail == 'high':
            dist = [0, 20, 80, 280]
        else:
            if avatarDetail == 'med':
                dist = [0, 10, 40, 280]
            else:
                if avatarDetail == 'low':
                    dist = [0, 5, 20, 280]
                else:
                    raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
        self.addLOD('hi', dist[1], dist[0])
        self.addLOD('med', dist[2], dist[1])
        self.addLOD('low', dist[3], dist[2])

    @classmethod
    def setupAssets(cls):
        cls.animInfo = Creature.animInfo.copy()
        cls.setupAnimInfo()
        filePrefix = cls.ModelInfo[1]
        animList = cls.AnimList
        animDict = {}
        for anim in animList:
            animDict[anim[0]] = filePrefix + anim[1]

        cls.animDict = animDict
        filePrefix = cls.ModelInfo[1]
        for name in cls.SfxNames:
            cls.sfx[name] = loadSfx(cls.SfxNames[name])

        cls.actor = Actor.Actor()
        if loader.loadModel(filePrefix + 'med') != None:
            avatarDetail = base.config.GetString('avatar-detail', 'high')
            if avatarDetail == 'high':
                dist = [0, 20, 80, 280]
            else:
                if avatarDetail == 'med':
                    dist = [0, 10, 40, 280]
                else:
                    if avatarDetail == 'low':
                        dist = [0, 6, 20, 280]
                    else:
                        raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
            cls.actor.setLODNode()
            cls.actor.addLOD('hi', dist[1], dist[0])
            cls.actor.addLOD('med', dist[2], dist[1])
            cls.actor.addLOD('low', dist[3], dist[2])
            creatureDetail = base.config.GetBool('want-high-creature-detail',
                                                 0)
            if creatureDetail:
                cls.actor.loadModel(filePrefix + 'hi', 'modelRoot', 'hi')
                cls.actor.loadModel(filePrefix + 'med', 'modelRoot', 'med')
                cls.actor.loadModel(filePrefix + 'low', 'modelRoot', 'low')
            else:
                cls.actor.loadModel(filePrefix + 'med', 'modelRoot', 'hi')
                cls.actor.loadModel(filePrefix + 'low', 'modelRoot', 'med')
                cls.actor.loadModel(filePrefix + 'super', 'modelRoot', 'low')
            cls.actor.loadAnims(cls.animDict, 'modelRoot', 'all')
        else:
            cls.actor.loadModel(cls.ModelInfo[0])
            cls.actor.loadAnims(cls.animDict)
        cls.actor.getGeomNode().setH(180)
        return

    def getSfx(self, name):
        return self.sfx.get(name)

    def shouldNotice(self):
        return 1

    def endShuffle(self):
        idleAnimInfo = self.animInfo['LandRoam'][PiratesGlobals.STAND_INDEX]
        try:
            self.loop(idleAnimInfo[0], blendDelay=0.3, rate=idleAnimInfo[1])
        except TypeError, e:
            self.notify.error('Invalid animation %s for %s' %
                              (idleAnimInfo, self))
class Biped(UsesAnimationMixer, Avatar, UsesEffectNode):
    SfxNames = {
        'death': SoundGlobals.SFX_MONSTER_DEATH }
    sfx = { }
    FailsafeAnims = (('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0))
    animInfo = { }

    def __init__(self, other = None, animationMixerClass = BipedAnimationMixer):
        self.wantZombie = base.config.GetBool('want-zombie', 0)
        Avatar.__init__(self, other)
        UsesAnimationMixer.__init__(self, animationMixerClass)
        UsesEffectNode.__init__(self)
        Biped.initSfx()
        self.setPickable(0)
        self.height = 0.0
        self.nametagOffset = -5.0
        self.rootScale = 1.0
        self.nameText = None
        self.iconNodePath = None
        self.battleTubeHeight = 0.0
        self.battleTubeRadius = 0.0
        self.lerpHeadTrack = None
        self.loadAnimatedHead = None
        self.rightHandNode = NodePath(ModelNode('rightHand'))
        self.leftHandNode = NodePath(ModelNode('leftHand'))
        self.weaponJointInstances = []
        self.headFudgeHpr = Vec3(0, 0, 0)
        self.renderReflection = False
        self.nametag2dContents = 0
        self.nametag2dDist = 0
        self.nametag2dNormalContents = 0
        self.nametag.getNametag2d().setContents(0)
        self.nametag3d.setLightOff()
        self.fader = None


    def setName(self, val):
        Avatar.setName(self, val)


    def deleteWeaponJoints(self):
        if not self.rightHandNode.isEmpty():
            self.rightHandNode.detachNode()

        if not self.leftHandNode.isEmpty():
            self.leftHandNode.detachNode()

        for node in self.weaponJointInstances:
            node.detachNode()

        self.weaponJointInstances = []


    def delete(self):
        self.loadAnimatedHead = None
        self.deleteWeaponJoints()
        Avatar.delete(self)
        UsesAnimationMixer.delete(self)
        UsesEffectNode.delete(self)


    def actorInterval(self, *args, **kwargs):
        if hasattr(self, 'undead') and self.undead:
            return UsesAnimationMixer.actorInterval(self.skeleton, *args, **kwArgs)
        else:
            bodyIval = UsesAnimationMixer.actorInterval(self, *args, **kwArgs)
            return bodyIval


    def play(self, *args, **kwArgs):
        if hasattr(self, 'undead') and self.undead:
            UsesAnimationMixer.play(self.skeleton, *args, **kwArgs)
        else:
            UsesAnimationMixer.play(self, *args, **kwArgs)


    def loop(self, *args, **kwArgs):
        if hasattr(self, 'undead') and self.undead:
            UsesAnimationMixer.loop(self.skeleton, *args, **kwArgs)
        else:
            UsesAnimationMixer.loop(self, *args, **kwArgs)


    def stop(self, *args, **kwArgs):
        if hasattr(self, 'undead') and self.undead:
            UsesAnimationMixer.stop(self.skeleton, *args, **kwArgs)
        else:
            UsesAnimationMixer.stop(self, *args, **kwArgs)


    def pose(self, *args, **kwArgs):
        if hasattr(self, 'undead') and self.undead:
            UsesAnimationMixer.pose(self.skeleton, *args, **kwArgs)
        else:
            UsesAnimationMixer.pose(self, *args, **kwArgs)


    def pingpong(self, *args, **kwArgs):
        if hasattr(self, 'undead') and self.undead:
            UsesAnimationMixer.pingpong(self.skeleton, *args, **kwArgs)
        else:
            UsesAnimationMixer.pingpong(self, *args, **kwArgs)


    def getDuration(self, animName = None, partName = None, fromFrame = None, toFrame = None):
        return Avatar.getDuration(self, animName, partName, fromFrame, toFrame)


    def getFrameTime(self, animName, frame, partName = None):
        return Avatar.getFrameTime(self, animName, frame, partName)


    def getRadius(self):
        return self.battleTubeRadius


    def getWeaponJoints(self):
        self.deleteWeaponJoints()
        lods = list(self.getLODNames())
        lods.sort()
        for lodName in lods:
            handLocator = self.getLOD(lodName).find('**/*weapon_right')
            if not handLocator.isEmpty():
                if lodName == lods[0]:
                    self.rightHandNode.reparentTo(handLocator)
                else:
                    self.weaponJointInstances.append(self.rightHandNode.instanceTo(handLocator))

            handLocator = self.getLOD(lodName).find('**/*weapon_left')
            if not handLocator.isEmpty():
                if lodName == lods[0]:
                    self.leftHandNode.reparentTo(handLocator)
                else:
                    self.weaponJointInstances.append(self.leftHandNode.instanceTo(handLocator))

        self.postAsyncLoadFix()


    def postAsyncLoadFix(self):
        pass


    def startLookAroundTask(self):
        taskMgr.remove(self.lookAroundTaskName)
        if self.headNode:
            delayTime = 1.0 + random.random() * 1.0
            taskMgr.doMethodLater(delayTime, self.lookAroundTask, self.lookAroundTaskName)



    def stopLookAroundTask(self):
        taskMgr.remove(self.lookAroundTaskName)
        if self.lerpHeadTrack:
            self.lerpHeadTrack.pause()
            self.lerpHeadTrack = None

        if self.headNode:
            self.headNode.setHpr(self.headFudgeHpr)



    def isInFov(self, target):
        k = 0.66579999999999995
        relPos = target.getPos(self)
        if relPos[1] > 0:
            tan = relPos[0] / relPos[1]
            if tan < k and tan > -k:
                return 1


        return 0


    def lookAroundTask(self, task):
        if self.isLocal():
            if self.gameFSM.state == 'Battle' and self.currentTarget and not self.currentTarget.isEmpty():
                task.delayTime = 5.0
                return Task.again


        if self.getCurrentAnim() in stopLookaroundAnimList:
            self.headNode.setHpr(self.headFudgeHpr)
            return Task.again

        hFov = 90
        vFov = 35
        if base.cr.targetMgr:
            allTargets = base.cr.targetMgr.objectDict.values()
        else:
            allTargets = []
        visibleTargets = []
        for target in allTargets:
            if not target.isEmpty():
                if self.isInFov(target):
                    visibleTargets.append(target)

            self.isInFov(target)

        if len(visibleTargets) > 0:
            if random.choice((True, True, False)):
                dummyNode = self.attachNewNode('dummy')
                dummyNode.setZ(self.getHeight())
                dummyNode.lookAt(random.choice(visibleTargets))
                heading = max(-hFov * 0.5, min(hFov * 0.5, dummyNode.getH()))
                pitch = max(-vFov * 0.5, min(vFov * 0.5, dummyNode.getP()))
                newHpr = Vec3(0, heading, -pitch)
                dummyNode.removeNode()
            else:
                newHpr = self.headFudgeHpr
        elif random.choice((True, True, False)):
            newHpr = Vec3(0, random.random() * hFov - hFov * 0.5, random.random() * vFov - vFov * 0.5)
        else:
            newHpr = self.headFudgeHpr
        if self.lerpHeadTrack:
            self.lerpHeadTrack.pause()
            self.lerpHeadTrac3dk = None

        t = 0.20000000000000001 + random.random() * 0.80000000000000004
        self.lerpHeadTrack = LerpHprInterval(self.headNode, t, newHpr, blendType = 'easeInOut')
        self.lerpHeadTrack.start()
        task.delayTime = 3.0 + random.random() * 4.0
        return Task.again


    def initializeNametag3d(self):
        Avatar.initializeNametag3d(self)
        self.nametag3d.setColorScaleOff(100)
        self.nametag3d.setLightOff()
        self.nametag3d.setFogOff()
        self.nametag3d.setZ(self.scale)
        self.nametag3d.setH(self.getGeomNode().getH())
        self.nametag.setFont(PiratesGlobals.getPirateFont())

        return 0 # TODO: fix iconNodePath
        self.iconNodePath = self.nametag.getNameIcon()
        if self.iconNodePath.isEmpty():
            self.notify.warning('empty iconNodePath in initializeNametag3d')
            return 0

        if not self.nameText:
            self.nameText = OnscreenText(fg = Vec4(1, 1, 1, 1), bg = Vec4(0, 0, 0, 0), scale = 1.1000000000000001, align = TextNode.ACenter, mayChange = 1, font = PiratesGlobals.getPirateBoldOutlineFont())
            self.nameText.reparentTo(self.iconNodePath)
            self.nameText.setTransparency(TransparencyAttrib.MDual, 2)
            self.nameText.setColorScaleOff(100)
            self.nameText.setLightOff()
            self.nameText.setFogOff()
            self.nameTag3dInitialized()



    def nameTag3dInitialized(self):
        pass


    def getNameText(self):
        return self.nameText


    def getDeathAnimName(self, animNum = None):
        animStrings = [
            'death',
            'death2',
            'death3',
            'death4']
        if animNum not in range(len(animStrings)):
            animNum = random.choice(range(0, len(animStrings)))

        return animStrings[animNum]


    def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1):
        Avatar.setChatAbsolute(self, chatString, chatFlags, dialogue, interrupt)
        if chatString:
            avId = None
            if hasattr(self, 'doId'):
                avId = self.doId

            base.talkAssistant.receiveOpenTalk(avId, self.getName(), 0, None, chatString)



    def fadeIn(self, time):
        if self.fader:
            self.fader.finish()
            self.fader = None

        self.setTransparency(1)
        self.setColorScale(1, 1, 1, 0)
        self.show()
        self.fader = self.colorScaleInterval(time, Vec4(1, 1, 1, 1), startColorScale = Vec4(1, 1, 1, 0))
        self.fader.start()


    def fadeOut(self, time):
        if self.fader:
            self.fader.finish()
            self.fader = None

        self.setTransparency(1)
        self.setColorScale(1, 1, 1, 1)
        self.fader = Sequence(self.colorScaleInterval(time, Vec4(1, 1, 1, 0), startColorScale = Vec4(1, 1, 1, 1)), Func(self.hide))
        self.fader.start()


    def setupAnimInfoState(cls, state, info):
        if len(info) < len(cls.FailsafeAnims):
            info += cls.FailsafeAnims[len(info) - len(cls.FailsafeAnims):]

        cls.animInfo[state] = info

    setupAnimInfoState = classmethod(setupAnimInfoState)

    def getAnimInfo(self, state):
        return self.animInfo.get(state, self.FailsafeAnims)


    def setRenderReflection(self):
        OTPRender.renderReflection(self.renderReflection, self, 'p_biped', None)


    def setupAnimInfo(cls):
        cls.setupAnimInfoState('LandRoam', (('idle', 1.0), ('walk', 1.0), ('run', 1.0), ('walk', -1.0), ('strafe_left', 1), ('strafe_right', 1), ('run_diagonal_left', 1), ('run_diagonal_right', 1), ('walk_back_diagonal_left', 1), ('walk_back_diagonal_right', 1), ('fall_ground', 1), ('fall_ground', 1), ('spin_left', 1), ('spin_right', 1)))
        cls.setupAnimInfoState('WaterRoam', (('tread_water', 1.0), ('swim', 1.0), ('swim', 1.0), ('swim_back', 1.0), ('swim_left', 1.0), ('swim_right', 1.0), ('swim_left_diagonal', 1.0), ('swim_right_diagonal', 1.0), ('swim_back_diagonal_left', 1.0), ('swim_back_diagonal_right', 1.0), ('fall_ground', 1), ('fall_ground', 1), ('tread_water', 1), ('tread_water', 1)))
        cls.setupAnimInfoState('LandTreasureRoam', (('chest_idle', 1.0), ('chest_walk', 1.0), ('chest_walk', 1.0), ('chest_walk', -1.0), ('chest_strafe_left', 1.0), ('chest_strafe_right', 1.0), ('chest_strafe_left', 1), ('chest_strafe_right', 1), ('chest_strafe_right', -1), ('chest_strafe_left', -1), ('fall_ground', 1), ('fall_ground', 1), ('chest_walk', 1), ('chest_walk', 1)))
        cls.setupAnimInfoState('WaterTreasureRoam', (('tread_water', 1.0), ('swim', 1.0), ('swim', 1.0), ('swim_back', 1.0), ('swim_left', 1.0), ('swim_right', 1.0), ('swim_left_diagonal', 1.0), ('swim_right_diagonal', 1.0), ('swim_back_diagonal_left', 1.0), ('swim_back_diagonal_right', 1.0), ('fall_ground', 1), ('fall_ground', 1), ('walk', 1), ('walk', 1)))
        cls.setupAnimInfoState('BayonetLandRoam', (('bayonet_idle', 1.0), ('bayonet_walk', 1.0), ('bayonet_run', 1.0), ('bayonet_walk', -1.0), ('strafe_left', 1), ('strafe_right', 1), ('run_diagonal_left', 1), ('run_diagonal_right', 1), ('walk_back_diagonal_left', 1), ('walk_back_diagonal_right', 1), ('fall_ground', 1), ('fall_ground', 1), ('spin_left', 1.0), ('spin_right', 1.0)))

    setupAnimInfo = classmethod(setupAnimInfo)

    @classmethod
    def initSfx(cls):
        for (name, effect) in cls.SfxNames.iteritems():
            if name not in cls.sfx:
                sound = SoundGlobals.loadSfx(effect)
                if sound and sound.getActive():
                    cls.sfx[name] = sound
class SeaMonster(UsesAnimationMixer, Avatar.Avatar, UsesEffectNode):
    FailsafeAnims = (('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0))
    SfxNames = {'death': SoundGlobals.SFX_MONSTER_DEATH}
    sfx = {}
    actor = None
    animInfo = {}

    class AnimationMixer(AnimationMixer):
        LOOP = AnimationMixer.LOOP
        ACTION = dict(AnimationMixer.ACTION)
        ACTION['MOVIE'] = AnimationMixer.ACTION_INDEX + 1

    def __init__(self, animationMixer=None):
        Avatar.Avatar.__init__(self)
        UsesEffectNode.__init__(self)
        self.setPickable(0)
        self.shadowFileName = 'models/misc/drop_shadow'
        self.nameText = None
        self.avatarType = None
        if not SeaMonster.sfx:
            for name in SeaMonster.SfxNames:
                SeaMonster.sfx[name] = loadSfx(SeaMonster.SfxNames[name])

        OTPRender.renderReflection(False, self, 'p_creature', None)
        animationMixer = self.AnimationMixer
        UsesAnimationMixer.__init__(self, animationMixer)
        return

    def delete(self):
        Avatar.Avatar.delete(self)

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

    def generateSeaMonster(self):
        if self.actor:
            self.copyActor(self.actor)

    def setAvatarType(self, avatarType):
        if self.avatarType:
            self.initializeNametag3d()
            return
        else:
            self.avatarType = avatarType
            self.height = EnemyGlobals.getHeight(avatarType)
            self.initializeDropShadow()
            self.initializeNametag3d()

    def initializeNametag3d(self):
        if self.avatarType.isA(AvatarType(base=AvatarTypes.Animal)):
            return
        Avatar.Avatar.initializeNametag3d(self)
        self.nametag3d.setH(self.getGeomNode().getH())
        self.nametag3d.setFogOff()
        self.nametag3d.setLightOff()
        self.nametag3d.setColorScaleOff(100)
        self.nametag.setFont(PiratesGlobals.getPirateBoldOutlineFont())
        self.iconNodePath = self.nametag.getNameIcon()
        if self.iconNodePath.isEmpty():
            self.notify.warning('empty iconNodePath in initializeNametag3d')
            return 0
        if not self.nameText:
            self.nameText = OnscreenText(
                fg=Vec4(1, 1, 1, 1),
                bg=Vec4(0, 0, 0, 0),
                scale=1.1,
                align=TextNode.ACenter,
                mayChange=1,
                font=PiratesGlobals.getPirateBoldOutlineFont())
            self.nameText.reparentTo(self.iconNodePath)
            self.nameText.setTransparency(TransparencyAttrib.MDual, 2)
            self.nameText.setColorScaleOff(100)
            self.nameText.setLightOff()
            self.nameText.setFogOff()
            scale = self.nametag3d.getScale(render)
            self.nameText.setScale(1.0 / scale[0])

    def scaleAnimRate(self, forwardSpeed):
        rate = 1.0
        myMaxSpeed = self.getMaxSpeed()
        if myMaxSpeed > 0 and forwardSpeed > 0:
            currTime = globalClockDelta.globalClock.getFrameTime()
            maxSpeed = myMaxSpeed * (currTime - self.prevSpeedClock)
            prevTime = self.prevSpeedClock
            self.prevSpeedClock = currTime
            rate = min(1.25, forwardSpeed / maxSpeed)
        return rate

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

        return joints

    def getAirborneHeight(self):
        return 0.0

    def getMaxSpeed(self):
        return 0

    def getRadius(self):
        return self.battleTubeRadius

    def play(self, *args, **kwArgs):
        UsesAnimationMixer.play(self, *args, **kwArgs)

    def loop(self, *args, **kwArgs):
        UsesAnimationMixer.loop(self, *args, **kwArgs)

    def pingpong(self, *args, **kwArgs):
        UsesAnimationMixer.pingpong(self, *args, **kwArgs)

    def pose(self, *args, **kwArgs):
        UsesAnimationMixer.pose(self, *args, **kwArgs)

    def stop(self, *args, **kwArgs):
        UsesAnimationMixer.stop(self, *args, **kwArgs)

    def getDeathAnimName(self, animNum=None):
        animStrings = ['death']
        if animNum not in range(len(animStrings)):
            animNum = random.choice([0])
        return animStrings[animNum]

    def getAnimInfo(self, state):
        return self.animInfo.get(state, self.FailsafeAnims)

    @classmethod
    def setupAnimInfoState(cls, state, info):
        if len(info) < len(cls.FailsafeAnims):
            info += cls.FailsafeAnims[len(info) - len(cls.FailsafeAnims):]
        cls.animInfo[state] = info

    @classmethod
    def setupAnimInfo(cls):
        cls.setupAnimInfoState('LandRoam', cls.FailsafeAnims)
        cls.setupAnimInfoState('WaterRoam', cls.FailsafeAnims)

    @classmethod
    def setupAnims(cls):
        cls.animInfo = copy.copy(SeaMonster.animInfo)
        cls.setupAnimInfo()
        filePrefix = cls.ModelInfo[1]
        animList = cls.AnimList
        animDict = {}
        for anim in animList:
            animDict[anim[0]] = filePrefix + anim[1]

        cls.animDict = animDict

    def setLODs(self):
        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, 6, 20, 280]
        else:
            raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
        self.addLOD('low', dist[3], dist[2])
        self.addLOD('med', dist[2], dist[1])
        self.addLOD('hi', dist[1], dist[0])

    @classmethod
    def setupAssets(cls):
        cls.animInfo = copy.copy(SeaMonster.animInfo)
        cls.setupAnimInfo()
        filePrefix = cls.ModelInfo[1]
        animList = cls.AnimList
        animDict = {}
        for anim in animList:
            animDict[anim[0]] = filePrefix + anim[1]

        cls.animDict = animDict
        filePrefix = cls.ModelInfo[1]
        for name in cls.SfxNames:
            cls.sfx[name] = loadSfx(cls.SfxNames[name])

        cls.actor = Actor.Actor()
        if loader.loadModel(filePrefix + 'med') != None:
            avatarDetail = base.config.GetString('avatar-detail', 'high')
            if avatarDetail == 'high':
                dist = [0, 200, 800, 2800]
            elif avatarDetail == 'med':
                dist = [0, 100, 400, 2800]
            elif avatarDetail == 'low':
                dist = [0, 60, 200, 2800]
            else:
                raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
            cls.actor.setLODNode()
            cls.actor.addLOD('low', dist[3], dist[2])
            cls.actor.addLOD('med', dist[2], dist[1])
            cls.actor.addLOD('hi', dist[1], dist[0])
            cls.actor.loadModel(filePrefix + 'hi', 'modelRoot', 'hi')
            cls.actor.loadModel(filePrefix + 'med', 'modelRoot', 'med')
            cls.actor.loadModel(filePrefix + 'low', 'modelRoot', 'low')
            cls.actor.loadAnims(cls.animDict, 'modelRoot', 'all')
        else:
            cls.actor.loadModel(cls.ModelInfo[0])
            cls.actor.loadAnims(cls.animDict)
        cls.actor.getGeomNode().setH(180)
        return
class Creature(UsesAnimationMixer, Avatar.Avatar, UsesEffectNode):
    FailsafeAnims = (('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0), ('idle', 1.0), ('idle', 1.0),
                     ('idle', 1.0), ('idle', 1.0))
    SfxNames = {'death': SoundGlobals.SFX_MONSTER_DEATH}
    sfx = {}
    actor = None
    animInfo = {}

    class AnimationMixer(AnimationMixer):
        LOOP = AnimationMixer.LOOP
        ACTION = dict(AnimationMixer.ACTION)
        ACTION['MOVIE'] = AnimationMixer.ACTION_INDEX + 1

    def __init__(self, animationMixer = None):
        Avatar.Avatar.__init__(self)
        UsesEffectNode.__init__(self)
        self.setPickable(0)
        self.shadowFileName = 'models/misc/drop_shadow'
        self.dimensions = VBase3(0.0, 0.0, 0.0)
        self.nameText = None
        self.avatarType = None
        self.level = None
        self.nametagOffset = 2.0
        self.headNode = self.find('**/def_head')
        if not Creature.sfx:
            for name in Creature.SfxNames:
                Creature.sfx[name] = loadSfx(Creature.SfxNames[name])

        self.setupReflection()

        if not animationMixer:
            pass

        animationMixer = self.AnimationMixer
        UsesAnimationMixer.__init__(self, animationMixer)

        self.deathEffect = None
        self.shockwaveRingIval = None
        self.shockwaveRingEffect = None
        self.spiritIval = None
        self.spiritEffect = None

    def delete(self):
        if self.deathEffect:
            self.deathEffect.stop()
            self.deathEffect = None
        if self.shockwaveRingIval:
            self.shockwaveRingIval.pause()
            self.shockwaveRingIval = None
        if self.shockwaveRingEffect:
            self.shockwaveRingEffect.stop()
            self.shockwaveRingEffect = None
        if self.spiritIval:
            self.spiritIval.pause()
            self.spiritIval = None
        if self.spiritEffect:
            self.spiritEffect.stop()
            self.spiritEffect = None
        Avatar.Avatar.delete(self)
        UsesAnimationMixer.delete(self)
        UsesEffectNode.delete(self)

    def setupReflection(self):
        OTPRender.renderReflection(False, self, 'p_creature', None)

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

    def generateCreature(self):
        if self.actor:
            self.copyActor(self.actor)
        self.headNode = self.find('**/def_head')
        if base.options.character_detail_level == PiratesGlobals.CD_LOW:
            self.setLODAnimation(100, 5, 0.10000000000000001)
        self.enableMixing()

    def setAvatarType(self, avatarType):
        self.avatarType = avatarType
        self.height = EnemyGlobals.getHeight(avatarType)
        self.initializeDropShadow()
        if base.options.terrain_detail_level == PiratesGlobals.CD_LOW:
            self.shadowPlacer.off()
        self.initializeNametag3d()

    setAvatarType = report(types=['module','args'],
                           dConfigParam='nametag')(setAvatarType)

    def setLevel(self, level):
        self.level = level

    def getLevel(self):
        return self.level

    def initializeNametag3d(self):
        Avatar.Avatar.initializeNametag3d(self)
        self.nametag3d.setFogOff()
        self.nametag3d.setLightOff()
        self.nametag3d.setColorScaleOff(100)
        self.nametag3d.setH(self.getGeomNode().getH())
        self.nametag.setFont(PiratesGlobals.getPirateBoldOutlineFont())
        self.iconNodePath = self.nametag.getNameIcon()
        if self.iconNodePath.isEmpty():
            self.notify.warning('empty iconNodePath in initializeNametag3d')
            return False
        if not self.nameText:
            self.nameText = OnscreenText(fg = Vec4(1, 1, 1, 1), bg = Vec4(0, 0, 0, 0), scale = 1.1000000000000001, align = TextNode.ACenter, mayChange = 1, font = PiratesGlobals.getPirateBoldOutlineFont())
            self.nameText.reparentTo(self.iconNodePath)
            self.nameText.setTransparency(TransparencyAttrib.MDual, 2)
            self.nameText.setColorScaleOff(100)
            self.nameText.setLightOff()
            self.nameText.setFogOff()

    initializeNametag3d = report(types=['module','args'],
                                 dConfigParam='nametag')(initializeNametag3d)

    def getNameText(self):
        return self.nameText

    def scaleAnimRate(self, forwardSpeed):
        rate = 1.0
        myMaxSpeed = self.getMaxSpeed()
        if myMaxSpeed > 0 and forwardSpeed > 0:
            currTime = globalClockDelta.globalClock.getFrameTime()
            maxSpeed = myMaxSpeed * (currTime - self.prevSpeedClock)
            prevTime = self.prevSpeedClock
            self.prevSpeedClock = currTime
            rate = min(1.25, forwardSpeed / maxSpeed)
        return rate

    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

    getNametagJoints = report(types=['module','args'],
                              dConfigParam='nametag')(getNametagJoints)

    def adjustNametag3d(self, parentScale = 1.0):
        self.nametag3d.setZ(self.scale * parentScale * self.nametagOffset - self.nametagOffset)

    def getAirborneHeight(self):
        return 0.0

    def getMaxSpeed(self):
        return 0

    def getRadius(self):
        return self.battleTubeRadius

    def play(self, *args, **kwArgs):
        UsesAnimationMixer.play(self, *args, **kwArgs)

    def loop(self, *args, **kwArgs):
        UsesAnimationMixer.loop(self, *args, **kwArgs)

    def pingpong(self, *args, **kwArgs):
        UsesAnimationMixer.pingpong(self, *args, **kwArgs)

    def pose(self, *args, **kwArgs):
        UsesAnimationMixer.pose(self, *args, **kwArgs)

    def stop(self, *args, **kwArgs):
        UsesAnimationMixer.stop(self, *args, **kwArgs)

    def getDeathAnimName(self, animNum = None):
        animStrings = ['death']
        if animNum not in range(len(animStrings)):
            animNum = random.choice([0])
        return animStrings[animNum]

    def getAnimInfo(self, state):
        return self.animInfo.get(state, self.FailsafeAnims)

    def setupAnimInfoState(cls, state, info):
        if len(info) < len(cls.FailsafeAnims):
            info += cls.FailsafeAnims[len(info) - len(cls.FailsafeAnims):]
        cls.animInfo[state] = info

    setupAnimInfoState = classmethod(setupAnimInfoState)

    def setupAnimInfo(cls):
        cls.setupAnimInfoState('LandRoam', cls.FailsafeAnims)
        cls.setupAnimInfoState('WaterRoam', cls.FailsafeAnims)

    setupAnimInfo = classmethod(setupAnimInfo)

    def setLODs(self):
        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('hi', dist[1], dist[0])
        self.addLOD('med', dist[2], dist[1])
        self.addLOD('low', dist[3], dist[2])


    def setupAssets(cls):
        cls.animInfo = Creature.animInfo.copy()
        cls.setupAnimInfo()
        filePrefix = cls.ModelInfo[1]
        animList = cls.AnimList
        animDict = { }
        for anim in animList:
            animDict[anim[0]] = filePrefix + anim[1]

        cls.animDict = animDict
        filePrefix = cls.ModelInfo[1]
        for name in cls.SfxNames:
            cls.sfx[name] = loadSfx(cls.SfxNames[name])

        cls.actor = Actor.Actor()
        if loader.loadModel(filePrefix + 'med') != None:
            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,
                    6,
                    20,
                    280]
            else:
                raise StandardError, 'Invalid avatar-detail: %s' % avatarDetail
            cls.actor.setLODNode()
            cls.actor.addLOD('hi', dist[1], dist[0])
            cls.actor.addLOD('med', dist[2], dist[1])
            cls.actor.addLOD('low', dist[3], dist[2])
            creatureDetail = base.config.GetBool('want-high-creature-detail', 0)
            if creatureDetail:
                cls.actor.loadModel(filePrefix + 'hi', 'modelRoot', 'hi')
                cls.actor.loadModel(filePrefix + 'med', 'modelRoot', 'med')
                cls.actor.loadModel(filePrefix + 'low', 'modelRoot', 'low')
            else:
                cls.actor.loadModel(filePrefix + 'med', 'modelRoot', 'hi')
                cls.actor.loadModel(filePrefix + 'low', 'modelRoot', 'med')
                cls.actor.loadModel(filePrefix + 'super', 'modelRoot', 'low')
            cls.actor.loadAnims(cls.animDict, 'modelRoot', 'all')
        else:
            cls.actor.loadModel(cls.ModelInfo[0])
            cls.actor.loadAnims(cls.animDict)
        cls.actor.getGeomNode().setH(180)

    setupAssets = classmethod(setupAssets)

    def getSfx(self, name):
        return self.sfx.get(name)

    def shouldNotice(self):
        return True

    def endShuffle(self):
        idleAnimInfo = self.animInfo['LandRoam'][PiratesGlobals.STAND_INDEX]
        try:
            self.loop(idleAnimInfo[0], blendDelay = 0.29999999999999999, rate = idleAnimInfo[1])
        except TypeError:
            e = None
            self.notify.error('Invalid animation %s for %s' % (idleAnimInfo, self))

    def getSplashOverride(self):
        pass