Beispiel #1
0
class Avatar(Actor, ShadowCaster):
    """
    Avatar class: contains methods for making actors that walk
    and talk
    """
    notify = directNotify.newCategory("Avatar")

    # This is the list of Avatars that are currently known
    # to the player--all those that have been recently generated, and not
    # yet deleted or disabled.
    ActiveAvatars = []

    # by default Avatar listens for nametagAmbientLightChanged events starting at __init__
    # and stops when delete is called
    # classes that want to override this behavior and handle accepting and ignoring of
    # nametagAmbientLightChanged events should set this to True
    ManagesNametagAmbientLightChanged = False

    # special methods

    def __init__(self, other=None):
        """
        Create the toon, suit, or char specified by the dna array
        """
        self._name = ""  # name is used in debugPrint.
        assert self.debugPrint("Avatar()")
        try:
            self.Avatar_initialized
            return
        except:
            self.Avatar_initialized = 1

        # create an empty actor to add parts to
        Actor.__init__(self, None, None, other, flattenable=0, setFinal=1)
        ShadowCaster.__init__(self)

        # The default font.
        self.__font = OTPGlobals.getInterfaceFont()

        self.soundChatBubble = None

        # Holds Type of Avatar
        self.avatarType = ""

        self.nametagNodePath = None

        # Set up a nametag (actually, a group of nametags,
        # including a Nametag2d and a Nametag3d) for the avatar.
        # The nametag won't be visible until it is managed, which
        # will happen during addActive().
        self.__nameVisible = 1
        self.nametag = NametagGroup()
        self.nametag.setAvatar(self)
        self.nametag.setFont(OTPGlobals.getInterfaceFont())
        self.nametag2dContents = Nametag.CName | Nametag.CSpeech
        # nametag2dDist is changed only by DistributedAvatar.
        self.nametag2dDist = Nametag.CName | Nametag.CSpeech
        self.nametag2dNormalContents = Nametag.CName | Nametag.CSpeech

        self.nametag3d = self.attachNewNode('nametag3d')
        self.nametag3d.setTag('cam', 'nametag')
        self.nametag3d.setLightOff()

        # Accept ambient lighting changes
        if self.ManagesNametagAmbientLightChanged:
            self.acceptNametagAmbientLightChange()

        # do not display in reflections
        OTPRender.renderReflection(False, self.nametag3d, 'otp_avatar_nametag',
                                   None)

        # But do show in shadows, except for the nametag.
        self.getGeomNode().showThrough(OTPRender.ShadowCameraBitmask)
        self.nametag3d.hide(OTPRender.ShadowCameraBitmask)

        self.collTube = None
        self.battleTube = None

        # set some initial values
        self.scale = 1.0
        self.nametagScale = 1.0
        self.height = 0.0
        self.battleTubeHeight = 0.0
        self.battleTubeRadius = 0.0
        self.style = None

        # commonChatFlags is a bitmask that may include the CommonChat
        # and SuperChat bits.
        self.commonChatFlags = 0

        # This is either CCNonPlayer, CCSuit, or CCNormal,
        # according to whether there's a human behind the avatar
        # or not.  This determines the color nametag that is
        # assigned, as well as whether chat messages from this
        # avatar will be garbled.
        self.understandable = 1
        self.setPlayerType(NametagGroup.CCNormal)

        self.ghostMode = 0

        # Page chat private vars
        self.__chatParagraph = None
        self.__chatMessage = None
        self.__chatFlags = 0
        self.__chatPageNumber = None
        self.__chatAddressee = None
        self.__chatDialogueList = []
        self.__chatSet = 0
        self.__chatLocal = 0
        # Record current dialogue so it can be interrupted the
        # next time the avatar talks
        self.__currentDialogue = None

        # since whiteListChatFlags is not a required field, init it just in case
        self.whitelistChatFlags = 0

    def delete(self):
        try:
            self.Avatar_deleted
        except:
            # masad: delete nametag before actor removes me
            self.deleteNametag3d()
            Actor.cleanup(self)
            if self.ManagesNametagAmbientLightChanged:
                self.ignoreNametagAmbientLightChange()
            self.Avatar_deleted = 1
            del self.__font
            del self.style
            del self.soundChatBubble
            del self.nametag
            self.nametag3d.removeNode()
            ShadowCaster.delete(self)
            Actor.delete(self)

    def acceptNametagAmbientLightChange(self):
        self.accept("nametagAmbientLightChanged",
                    self.nametagAmbientLightChanged)

    def ignoreNametagAmbientLightChange(self):
        self.ignore("nametagAmbientLightChanged")

    def isLocal(self):
        return 0

    def isPet(self):
        return False

    def isProxy(self):
        return False

    def setPlayerType(self, playerType):
        """
        setPlayerType(self, NametagGroup.ColorCode playerType)

        Indicates whether the avatar is a human player
        (NametagGroup.CCNormal), a friendly non-player character
        (NametagGroup.CCNonPlayer), or a suit (NametagGroup.CCSuit).
        This determines the color of the nametag, as well as whether
        chat messages from this avatar should be garbled.
        """
        self.playerType = playerType

        if not hasattr(self, 'nametag'):
            self.notify.warning(
                'no nametag attributed, but would have been used.')
            return
        if self.isUnderstandable():
            self.nametag.setColorCode(self.playerType)
            # self.nametag.setColorCode(NametagGroup.CCFreeChat)
        else:
            self.nametag.setColorCode(NametagGroup.CCNoChat)

    def setCommonChatFlags(self, commonChatFlags):
        """setCommonChatFlags(self, uint8)
        Reset the common chat flags.
        """

        self.commonChatFlags = commonChatFlags
        self.considerUnderstandable()

        if self == base.localAvatar:
            # If we change the common chat flags on localtoon, that
            # affects everyone.
            reconsiderAllUnderstandable()

    def setWhitelistChatFlags(self, whitelistChatFlags):
        """setCommonChatFlags(self, uint8)
        Reset the common chat flags.
        """
        self.whitelistChatFlags = whitelistChatFlags
        self.considerUnderstandable()

        if self == base.localAvatar:
            # If we change the common chat flags on localtoon, that
            # affects everyone.
            reconsiderAllUnderstandable()

    def considerUnderstandable(self):
        """
        Updates the "understandable" flag according to whether the
        local toon has permission to hear this avatar's chat messages
        or not (and vice-versa).

        Some of this code is duplicated in FriendHandle.isUnderstandable().
        """
        speed = 0
        if self.playerType in (NametagGroup.CCNormal, NametagGroup.CCFreeChat,
                               NametagGroup.CCSpeedChat):
            self.setPlayerType(NametagGroup.CCSpeedChat)
            speed = 1
        if hasattr(base, 'localAvatar') and self == base.localAvatar:
            # This *is* the local toon.  OK, one can always understand
            # oneself.
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif self.playerType == NametagGroup.CCSuit:
            # It's a suit!
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCSuit)
        elif self.playerType not in (NametagGroup.CCNormal,
                                     NametagGroup.CCFreeChat,
                                     NametagGroup.CCSpeedChat):
            # It's not a player character.
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCNoChat)
        elif hasattr(base, 'localAvatar') and (
                self.commonChatFlags & base.localAvatar.commonChatFlags
                & OTPGlobals.CommonChat):
            # Both this avatar and the local toon have common chat
            # permission.  OK.
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif self.commonChatFlags & OTPGlobals.SuperChat:
            # This avatar has "super chat" permission, so anyone
            # can understand him.  OK.
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif hasattr(base, 'localAvatar') and (base.localAvatar.commonChatFlags
                                               & OTPGlobals.SuperChat):
            # Local toon has "super chat" permission, so we can
            # understand everyone.  OK.
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif base.cr.getFriendFlags(self.doId) & OTPGlobals.FriendChat:
            # This avatar is a special friend of the local toon.  OK.
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif base.cr.playerFriendsManager.findPlayerIdFromAvId(
                self.doId) is not None:
            # This is the avatar of my player friend.  Is the player friendship open chat?
            playerInfo = base.cr.playerFriendsManager.findPlayerInfoFromAvId(
                self.doId)
            if playerInfo.openChatFriendshipYesNo:
                self.understandable = 1
                self.nametag.setColorCode(NametagGroup.CCFreeChat)
            elif playerInfo.isUnderstandable():
                self.understandable = 1
            else:
                self.understandable = 0
        elif hasattr(base, 'localAvatar') and (
                self.whitelistChatFlags & base.localAvatar.whitelistChatFlags):
            # Both this avatar and the local toon have whitelist chat
            # permission.  OK.
            self.understandable = 1
        else:
            # Too bad.
            self.understandable = 0

        # if self.understandable:
        if not hasattr(self, 'nametag'):
            self.notify.warning(
                'no nametag attributed, but would have been used')
        else:
            self.nametag.setColorCode(self.playerType)
        # else:
        #    self.nametag.setColorCode(NametagGroup.CCNoChat)

    def isUnderstandable(self):
        """
        Returns true if this avatar can chat freely with localtoon,
        false otherwise.
        """
        return self.understandable

    # These need to be defined in child class for each type of avatar
    def setDNAString(self, dnaString):
        assert self.notify.error("called setDNAString on parent class")

    def setDNA(self, dna):
        assert self.notify.error("called setDNA on parent class")

    # accessing

    def getAvatarScale(self):
        """
        Return the avatar's scale
        """
        return self.scale

    def setAvatarScale(self, scale):
        """
        Set the avatar's scale.  This both sets the scale on the
        NodePath, and also stores it for later retrieval, not to
        mention fiddling with the nametag to keep everything
        consistent.  You should use this call to adjust the avatar's
        scale, instead of adjusting it directly.
        """
        if self.scale != scale:
            self.scale = scale
            self.getGeomNode().setScale(scale)
            self.setHeight(self.height)

    def getNametagScale(self):
        """
        Return the nametag's overall scale.  This value does not
        change in response to camera position.
        """
        return self.nametagScale

    def setNametagScale(self, scale):
        """
        Sets the scale of the 3-d nametag floating over the avatar's
        head.  The nametags will also be scaled in response to the
        camera position, but this gives us an overall scale.
        """
        self.nametagScale = scale
        self.nametag3d.setScale(scale)

    def adjustNametag3d(self, parentScale=1.0):
        """adjustNametag3d(self)
        Adjust nametag according to the height
        """
        self.nametag3d.setPos(0, 0, self.height + 0.5)

    def getHeight(self):
        """
        Return the avatar's height
        """
        return self.height

    def setHeight(self, height):
        """setHeight(self, float)
        Set the avatar's height.
        """
        # The height as it is currently designed has already been
        # scaled by the avatar's scale, so we have to compensate for
        # this.
        self.height = height
        self.adjustNametag3d()
        if self.collTube:
            self.collTube.setPointB(0, 0, height - self.getRadius())
            if self.collNodePath:
                self.collNodePath.forceRecomputeBounds()
        if self.battleTube:
            self.battleTube.setPointB(0, 0, height - self.getRadius())

    def getRadius(self):
        """
        Returns the radius of the avatar's collision tube.
        """
        return OTPGlobals.AvatarDefaultRadius

    def getName(self):
        """
        Return the avatar's name
        """
        return self._name

    def getType(self):
        """
        Return the avatar's Type
        """
        return self.avatarType

    def setName(self, name):
        """
        name is a string
        
        Set the avatar's name
        """
        # if we are disguised, don't mess up our custom nametag
        if hasattr(self, "isDisguised"):
            if self.isDisguised:
                return

        self._name = name
        if hasattr(self, "nametag"):
            self.nametag.setName(name)

    def setDisplayName(self, str):
        # Sets the name that is displayed in the 3-d and 2-d nametags,
        # but not the name that is used to prefix chat messages.

        # if we are disguised, don't mess up our custom nametag
        if hasattr(self, "isDisguised"):
            if self.isDisguised:
                return

        self.nametag.setDisplayName(str)

    def getFont(self):
        """
        Returns the font used to display the avatar's name and chat
        messages.
        """
        return self.__font

    def setFont(self, font):
        """
        Changes the font used to display the avatar's name and chat
        messages.
        """
        self.__font = font
        self.nametag.setFont(font)

    def getStyle(self):
        """
        Return the dna string for the avatar
        """
        return self.style

    def setStyle(self, style):
        """setStyle(self, AvatarDNA)
        Set the dna string for the avatar
        """
        self.style = style

    ### play dialog sounds ###

    def getDialogueArray(self):
        # Inheritors should override
        return None

    def playCurrentDialogue(self, dialogue, chatFlags, interrupt=1):
        if interrupt and (self.__currentDialogue is not None):
            self.__currentDialogue.stop()
        self.__currentDialogue = dialogue
        # If an AudioSound has been passed in, play that for dialog to
        # go along with the chat.  Interrupt any sound effect currently playing
        if dialogue:
            base.playSfx(dialogue, node=self)
        # If it is a speech-type chat message, and the avatar isn't
        # too far away to hear, play the appropriate sound effect.
        elif ((chatFlags & CFSpeech) != 0
              and self.nametag.getNumChatPages() > 0):
            # play the dialogue sample

            # We use getChat() instead of chatString, which
            # returns just the current page of a multi-page chat
            # message.  This way we aren't fooled by long pages
            # that end in question marks.
            self.playDialogueForString(self.nametag.getChat())
            if (self.soundChatBubble != None):
                base.playSfx(self.soundChatBubble, node=self)

    def playDialogueForString(self, chatString):
        """
        Play dialogue samples to match the given chat string
        """
        # use only lower case for searching
        searchString = chatString.lower()
        # determine the statement type
        if (searchString.find(OTPLocalizer.DialogSpecial) >= 0):
            # special sound
            type = "special"
        elif (searchString.find(OTPLocalizer.DialogExclamation) >= 0):
            # exclamation
            type = "exclamation"
        elif (searchString.find(OTPLocalizer.DialogQuestion) >= 0):
            # question
            type = "question"
        else:
            # statement (use two for variety)
            if random.randint(0, 1):
                type = "statementA"
            else:
                type = "statementB"

        # determine length
        stringLength = len(chatString)
        if (stringLength <= OTPLocalizer.DialogLength1):
            length = 1
        elif (stringLength <= OTPLocalizer.DialogLength2):
            length = 2
        elif (stringLength <= OTPLocalizer.DialogLength3):
            length = 3
        else:
            length = 4

        self.playDialogue(type, length)

    def playDialogue(self, type, length):
        """playDialogue(self, string, int)
        Play the specified type of dialogue for the specified time
        """

        # Inheritors may override this function or getDialogueArray(),
        # above.

        # Choose the appropriate sound effect.
        dialogueArray = self.getDialogueArray()
        if dialogueArray == None:
            return

        sfxIndex = None
        if (type == "statementA" or type == "statementB"):
            if (length == 1):
                sfxIndex = 0
            elif (length == 2):
                sfxIndex = 1
            elif (length >= 3):
                sfxIndex = 2
        elif (type == "question"):
            sfxIndex = 3
        elif (type == "exclamation"):
            sfxIndex = 4
        elif (type == "special"):
            sfxIndex = 5
        else:
            notify.error("unrecognized dialogue type: ", type)

        if sfxIndex != None and sfxIndex < len(dialogueArray) and \
                dialogueArray[sfxIndex] != None:
            base.playSfx(dialogueArray[sfxIndex], node=self)

    def getDialogueSfx(self, type, length):
        """Return the correspoinding AudioSound to type and length, None if error."""
        retval = None
        # Choose the appropriate sound effect.
        dialogueArray = self.getDialogueArray()
        if dialogueArray == None:
            return None

        sfxIndex = None
        if (type == "statementA" or type == "statementB"):
            if (length == 1):
                sfxIndex = 0
            elif (length == 2):
                sfxIndex = 1
            elif (length >= 3):
                sfxIndex = 2
        elif (type == "question"):
            sfxIndex = 3
        elif (type == "exclamation"):
            sfxIndex = 4
        elif (type == "special"):
            sfxIndex = 5
        else:
            notify.error("unrecognized dialogue type: ", type)

        if sfxIndex != None and sfxIndex < len(dialogueArray) and \
                dialogueArray[sfxIndex] != None:
            retval = dialogueArray[sfxIndex]

        return retval

    def setChatAbsolute(self,
                        chatString,
                        chatFlags,
                        dialogue=None,
                        interrupt=1):
        """
        Receive the chat string, play dialogue if in range, display
        the chat message and spawn task to reset the chat message
        """
        self.nametag.setChat(chatString, chatFlags)

        # Update current dialogue, first making sure and active dialogue
        # is stopped first
        self.playCurrentDialogue(dialogue, chatFlags, interrupt)

    def setChatMuted(self,
                     chatString,
                     chatFlags,
                     dialogue=None,
                     interrupt=1,
                     quiet=0):
        """
        This method is a modification of setChatAbsolute in Toontown in which
        just the text of the chat is displayed on the nametag.
        No animal sound is played along with it.
        This method is defined in toontown/src/toon/DistributedToon.
        """
        pass

    def displayTalk(self, chatString):
        if not base.cr.avatarFriendsManager.checkIgnored(self.doId):
            if base.talkAssistant.isThought(chatString):
                self.nametag.setChat(
                    base.talkAssistant.removeThoughtPrefix(chatString),
                    CFThought)
            else:
                self.nametag.setChat(chatString, CFSpeech | CFTimeout)

    def clearChat(self):
        """
        Clears the last chat message
        """
        self.nametag.clearChat()

    # util

    def isInView(self):
        """
        Check to see if avatar is in view. Use a point near the eye height
        to perform the test
        """
        pos = self.getPos(camera)
        eyePos = Point3(pos[0], pos[1], pos[2] + self.getHeight())
        return base.camNode.isInView(eyePos)

    # name methods

    def getNameVisible(self):
        return self.__nameVisible

    def setNameVisible(self, bool):
        self.__nameVisible = bool

        if bool:
            self.showName()
        if not (bool):
            self.hideName()

    def hideName(self):
        # Hiding the name only means hiding the 3-d name from the
        # nametag.  Speech balloons, and the 2-d nametag, remain.
        self.nametag.getNametag3d().setContents(Nametag.CSpeech
                                                | Nametag.CThought)

    def showName(self):
        if self.__nameVisible and not self.ghostMode:
            self.nametag.getNametag3d().setContents(Nametag.CName
                                                    | Nametag.CSpeech
                                                    | Nametag.CThought)

    def hideNametag2d(self):
        """
        Temporarily hides the onscreen 2-d nametag.
        """
        self.nametag2dContents = 0
        self.nametag.getNametag2d().setContents(self.nametag2dContents
                                                & self.nametag2dDist)

    def showNametag2d(self):
        """
        Reveals the onscreen 2-d nametag after a previous call to
        hideNametag2d.
        """
        self.nametag2dContents = self.nametag2dNormalContents
        if self.ghostMode:
            self.nametag2dContents = Nametag.CSpeech

        self.nametag.getNametag2d().setContents(self.nametag2dContents
                                                & self.nametag2dDist)

    def hideNametag3d(self):
        """
        Temporarily hides the 3-d nametag.
        """
        self.nametag.getNametag3d().setContents(0)

    def showNametag3d(self):
        """
        Reveals the 3-d nametag after a previous call to
        hideNametag3d.
        """
        if self.__nameVisible and not self.ghostMode:
            self.nametag.getNametag3d().setContents(Nametag.CName
                                                    | Nametag.CSpeech
                                                    | Nametag.CThought)
        else:
            self.nametag.getNametag3d().setContents(0)

    def setPickable(self, flag):
        """
        Indicates whether the avatar can be picked by clicking on him
        or his nametag.
        """
        self.nametag.setActive(flag)

    def clickedNametag(self):
        """
        This hook is called whenever the user clicks on the nametag
        associated with this particular avatar (or, rather, clicks on
        the avatar itself).  It simply maps that C++-generated event
        into a Python event that includes the avatar as a parameter.
        """
        # If we have a button, we don't generate the normal clicked
        # nametag event; instead, we just advance the page (or clear
        # the chat on the last page).
        if self.nametag.hasButton():
            self.advancePageNumber()
        # Only throw the click event if the nametag is active. This
        # prevents a subtle error when double-clicking on a nametag
        # with a button in the same frame.
        elif self.nametag.isActive():
            # No page button, so just click on the nametag normally.
            messenger.send("clickedNametag", [self])
        else:
            pass

    def setPageChat(self,
                    addressee,
                    paragraph,
                    message,
                    quitButton,
                    extraChatFlags=None,
                    dialogueList=[],
                    pageButton=True):
        """
        setPageChat(self, int addressee, int paragraph, string message, bool quitButton, list dialogueList)

        The NPC is giving instruction or quest information to a
        particular Toon, which may involve multiple pages of text that
        the user must click through.

        The paragraph number indicates a unique number for the
        particular paragraph that is being spoken, and the addressee
        is the particular Toon that is being addressed.  Only the
        indicated Toon will be presented with the click-through
        buttons.

        This is normally called by the client from within a movie; it
        is not a message in its own right.
        """
        self.__chatAddressee = addressee
        self.__chatPageNumber = None
        self.__chatParagraph = paragraph
        self.__chatMessage = message
        if extraChatFlags is None:
            self.__chatFlags = CFSpeech
        else:
            self.__chatFlags = CFSpeech | extraChatFlags
        self.__chatDialogueList = dialogueList
        self.__chatSet = 0
        self.__chatLocal = 0
        self.__updatePageChat()

        if addressee == base.localAvatar.doId:
            # The chat message is addressed to us.
            if (pageButton):
                self.__chatFlags |= CFPageButton
            if quitButton == None:
                self.__chatFlags |= CFNoQuitButton
            elif quitButton:
                self.__chatFlags |= CFQuitButton

            # Since this is our own message, start out at the first
            # page.
            self.b_setPageNumber(self.__chatParagraph, 0)

    def setLocalPageChat(self,
                         message,
                         quitButton,
                         extraChatFlags=None,
                         dialogueList=[]):
        """
        setLocalPageChat(self, string message, bool quitButton, list dialogueList)

        Locally sets up a multiple-page chat message.  This is
        intended for use when the NPC is giving advice to the toon in
        a local context, e.g. in the Tutorial.

        If quitButton is 1, a red cancel button will be drawn in the
        place of the page advance arrow on the last page.  If
        quitButton is 0, a page advance arrow will be drawn on the
        last page.  If quitButton is None, no button at all will be
        drawn on the last page.
        """
        self.__chatAddressee = base.localAvatar.doId
        self.__chatPageNumber = None
        self.__chatParagraph = None
        self.__chatMessage = message
        if extraChatFlags is None:
            self.__chatFlags = CFSpeech
        else:
            self.__chatFlags = CFSpeech | extraChatFlags
        self.__chatDialogueList = dialogueList
        self.__chatSet = 1
        self.__chatLocal = 1

        self.__chatFlags |= CFPageButton
        if quitButton == None:
            self.__chatFlags |= CFNoQuitButton
        elif quitButton:
            self.__chatFlags |= CFQuitButton

        if len(dialogueList) > 0:
            dialogue = dialogueList[0]
        else:
            dialogue = None

        self.setChatAbsolute(message, self.__chatFlags, dialogue)
        self.setPageNumber(None, 0)

    def setPageNumber(self, paragraph, pageNumber, timestamp=None):
        """
        setPageNumber(self, int paragraph, int pageNumber)

        This message is generated by the client when the advance-page
        button is clicked.  All clients also receive this message.
        When the pageNumber is -1, the last page has been cleared.
        """
        if timestamp == None:
            elapsed = 0.0
        else:
            elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp)

        self.__chatPageNumber = [paragraph, pageNumber]
        self.__updatePageChat()

        if hasattr(self, "uniqueName"):
            # If you are derived from DistributedObjectAI
            if pageNumber >= 0:
                messenger.send(self.uniqueName("nextChatPage"),
                               [pageNumber, elapsed])
            else:
                messenger.send(self.uniqueName("doneChatPage"), [elapsed])
        else:
            # If you are not derived from DistributedObjectAI
            if pageNumber >= 0:
                messenger.send("nextChatPage", [pageNumber, elapsed])
            else:
                messenger.send("doneChatPage", [elapsed])

    def advancePageNumber(self):
        """
        Advances the page for the previously-spoken pageChat message.
        This is a distributed call.  This is normally called only in
        response to the user clicking on the next-page button for the
        message directed to himself.
        """
        if self.__chatAddressee == base.localAvatar.doId and \
                self.__chatPageNumber != None and \
                self.__chatPageNumber[0] == self.__chatParagraph:
            pageNumber = self.__chatPageNumber[1]
            if pageNumber >= 0:
                pageNumber += 1
                if pageNumber >= self.nametag.getNumChatPages():
                    # Last page; clear the chat.
                    pageNumber = -1

                if self.__chatLocal:
                    # If it's a local chat, just set the page number locally.
                    self.setPageNumber(self.__chatParagraph, pageNumber)
                else:
                    # Otherwise, distribute the page number.
                    self.b_setPageNumber(self.__chatParagraph, pageNumber)

    def __updatePageChat(self):
        """
        Updates the nametag to display the appropriate paging chat
        message, if all parameters are now available.
        """
        if (self.__chatPageNumber != None
                and self.__chatPageNumber[0] == self.__chatParagraph):
            pageNumber = self.__chatPageNumber[1]
            if pageNumber >= 0:
                if not self.__chatSet:
                    # First time around use setChatAbsolute to play dialogue
                    # if specified, otherwise pass in None so that appropriate
                    # default dialogue sfx is used
                    if len(self.__chatDialogueList) > 0:
                        dialogue = self.__chatDialogueList[0]
                    else:
                        dialogue = None
                    self.setChatAbsolute(self.__chatMessage, self.__chatFlags,
                                         dialogue)
                    self.__chatSet = 1
                if pageNumber < self.nametag.getNumChatPages():
                    self.nametag.setPageNumber(pageNumber)
                    # For all chat pages beyond the first one, play
                    # appropriate dialogue sfx
                    if (pageNumber > 0):
                        if (len(self.__chatDialogueList) > pageNumber):
                            dialogue = self.__chatDialogueList[pageNumber]
                        else:
                            dialogue = None
                        self.playCurrentDialogue(dialogue, self.__chatFlags)
                else:
                    self.clearChat()
            else:
                self.clearChat()

    def getAirborneHeight(self):
        """
        Get  the avatar height from the ground. 
        """
        assert self.shadowPlacer
        height = self.getPos(self.shadowPlacer.shadowNodePath)
        # If the shadow where not pointed strait down, we would need to
        # get magnitude of the vector.  Since it is strait down, we'll
        # just get the z:
        # spammy --> assert self.debugPrint("getAirborneHeight() returning %s"%(height.getZ(),))
        return height.getZ() + 0.025

    def initializeNametag3d(self):
        """
        Put the 3-d nametag in the right place over the avatar's head.
        This is normally done at some point after initialization,
        after the NametagGroup in self.nametag has already been
        created.  This is mainly just responsible for finding the
        right node or nodes to parent the 3-d nametag to.
        """
        # Protect this function from being called twice by removing
        # the old ones first.
        self.deleteNametag3d()

        # Nowadays, there is only one nametag3d, and it is a direct
        # child of the Avatar node.  (For a while, we had a separate
        # nametag for each LOD, parented deep within the hierarchy.)
        nametagNode = self.nametag.getNametag3d()
        self.nametagNodePath = self.nametag3d.attachNewNode(nametagNode)
        iconNodePath = self.nametag.getNameIcon()

        # We also want to animate the nametag appropriately.  This
        # means we grab the appropriate CharacterJoint object for each
        # LOD and point it at this node (instead of wherever it was
        # pointed before).
        for cJoint in self.getNametagJoints():
            cJoint.clearNetTransforms()
            cJoint.addNetTransform(nametagNode)

    def nametagAmbientLightChanged(self, newlight):
        """
        Get new ambient light when this avatar has changed locations/TODmanagers
        """
        self.nametag3d.setLightOff()
        if newlight:
            self.nametag3d.setLight(newlight)

    ##     def deleteNametag3d(self):
    ##         """
    ##         Lose the 3-d nametag
    ##         """
    ##         children = self.nametag3d.getChildren()
    ##         for i in range(children.getNumPaths()):
    ##             children[i].removeNode()

    def deleteNametag3d(self):
        """
        Lose the 3-d nametag
        """
        if (self.nametagNodePath):
            self.nametagNodePath.removeNode()
            self.nametagNodePath = None

    def initializeBodyCollisions(self, collIdStr):
        # Nowadays the body collisions for avatars other than
        # localToon are a tube.
        self.collTube = CollisionTube(0, 0, 0.5, 0, 0,
                                      self.height - self.getRadius(),
                                      self.getRadius())
        self.collNode = CollisionNode(collIdStr)
        self.collNode.addSolid(self.collTube)
        self.collNodePath = self.attachNewNode(self.collNode)

        if self.ghostMode:
            self.collNode.setCollideMask(OTPGlobals.GhostBitmask)
        else:
            self.collNode.setCollideMask(OTPGlobals.WallBitmask)

    def stashBodyCollisions(self):
        if hasattr(self, "collNodePath"):
            self.collNodePath.stash()

    def unstashBodyCollisions(self):
        if hasattr(self, "collNodePath"):
            self.collNodePath.unstash()

    def disableBodyCollisions(self):
        if hasattr(self, "collNodePath"):
            self.collNodePath.removeNode()
            del self.collNodePath

        self.collTube = None

    def addActive(self):
        """
        Adds the avatar to the list of currently-active avatars.
        """
        if (base.wantNametags):
            assert self.notify.debug('Adding avatar %s' % self.getName())

            # Just in case it was already there through some screw-up.
            try:
                Avatar.ActiveAvatars.remove(self)
            except ValueError:
                pass

            Avatar.ActiveAvatars.append(self)
            self.nametag.manage(base.marginManager)

            # Generate a useful event when someone clicks on our nametag.
            self.accept(self.nametag.getUniqueId(), self.clickedNametag)

    def removeActive(self):
        """
        Removes the avatar from the list of currently-active avatars.
        """
        if (base.wantNametags):
            assert self.notify.debug('Removing avatar %s' % self.getName())
            try:
                Avatar.ActiveAvatars.remove(self)
            except ValueError:
                assert self.notify.warning("%s was not present..." %
                                           self.getName())

            self.nametag.unmanage(base.marginManager)
            self.ignore(self.nametag.getUniqueId())

    if __debug__:

        def debugPrint(self, message):
            """for debugging"""
            return self.notify.debug("%s %s %s" %
                                     (id(self), self._name, message))

    def loop(self,
             animName,
             restart=1,
             partName=None,
             fromFrame=None,
             toFrame=None):
        return Actor.loop(self, animName, restart, partName, fromFrame,
                          toFrame)
Beispiel #2
0
class Avatar(Actor, ShadowCaster):
    __module__ = __name__
    notify = directNotify.newCategory('Avatar')
    ActiveAvatars = []
    ManagesNametagAmbientLightChanged = False

    def __init__(self, other = None):
        self.name = ''
        try:
            self.Avatar_initialized
            return
        except:
            self.Avatar_initialized = 1

        Actor.__init__(self, None, None, other, flattenable=0, setFinal=1)
        ShadowCaster.__init__(self)
        self.__font = OTPGlobals.getInterfaceFont()
        self.soundChatBubble = None
        self.avatarType = ''
        self.nametagNodePath = None
        self.__nameVisible = 1
        self.nametag = NametagGroup()
        self.nametag.setAvatar(self)
        self.nametag.setFont(OTPGlobals.getInterfaceFont())
        self.nametag2dContents = Nametag.CName | Nametag.CSpeech
        self.nametag2dDist = Nametag.CName | Nametag.CSpeech
        self.nametag2dNormalContents = Nametag.CName | Nametag.CSpeech
        self.nametag3d = self.attachNewNode('nametag3d')
        self.nametag3d.setTag('cam', 'nametag')
        self.nametag3d.setLightOff()
        if self.ManagesNametagAmbientLightChanged:
            self.acceptNametagAmbientLightChange()
        OTPRender.renderReflection(False, self.nametag3d, 'otp_avatar_nametag', None)
        self.getGeomNode().showThrough(OTPRender.ShadowCameraBitmask)
        self.nametag3d.hide(OTPRender.ShadowCameraBitmask)
        self.collTube = None
        self.battleTube = None
        self.scale = 1.0
        self.nametagScale = 1.0
        self.height = 0.0
        self.battleTubeHeight = 0.0
        self.battleTubeRadius = 0.0
        self.style = None
        self.commonChatFlags = 0
        self.understandable = 1
        self.setPlayerType(NametagGroup.CCNormal)
        self.ghostMode = 0
        self.__chatParagraph = None
        self.__chatMessage = None
        self.__chatFlags = 0
        self.__chatPageNumber = None
        self.__chatAddressee = None
        self.__chatDialogueList = []
        self.__chatSet = 0
        self.__chatLocal = 0
        self.__currentDialogue = None
        self.whitelistChatFlags = 0
        return

    def delete(self):
        try:
            self.Avatar_deleted
        except:
            self.deleteNametag3d()
            Actor.cleanup(self)
            if self.ManagesNametagAmbientLightChanged:
                self.ignoreNametagAmbientLightChange()
            self.Avatar_deleted = 1
            del self.__font
            del self.style
            del self.soundChatBubble
            del self.nametag
            self.nametag3d.removeNode()
            ShadowCaster.delete(self)
            Actor.delete(self)

    def acceptNametagAmbientLightChange(self):
        self.accept('nametagAmbientLightChanged', self.nametagAmbientLightChanged)

    def ignoreNametagAmbientLightChange(self):
        self.ignore('nametagAmbientLightChanged')

    def isLocal(self):
        return 0

    def isPet(self):
        return False

    def isProxy(self):
        return False

    def setPlayerType(self, playerType):
        self.playerType = playerType
        if not hasattr(self, 'nametag'):
            self.notify.warning('no nametag attributed, but would have been used.')
            return
        if self.isUnderstandable():
            self.nametag.setColorCode(self.playerType)
        else:
            self.nametag.setColorCode(NametagGroup.CCNoChat)

    def setCommonChatFlags(self, commonChatFlags):
        self.commonChatFlags = commonChatFlags
        self.considerUnderstandable()
        if self == base.localAvatar:
            reconsiderAllUnderstandable()

    def setWhitelistChatFlags(self, whitelistChatFlags):
        self.whitelistChatFlags = whitelistChatFlags
        self.considerUnderstandable()
        if self == base.localAvatar:
            reconsiderAllUnderstandable()

    def considerUnderstandable(self):
        speed = 0
        if self.playerType in (NametagGroup.CCNormal, NametagGroup.CCFreeChat, NametagGroup.CCSpeedChat):
            self.setPlayerType(NametagGroup.CCSpeedChat)
            speed = 1
        if hasattr(base, 'localAvatar') and self == base.localAvatar:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif self.playerType == NametagGroup.CCSuit:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCSuit)
        elif self.playerType not in (NametagGroup.CCNormal, NametagGroup.CCFreeChat, NametagGroup.CCSpeedChat):
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCNoChat)
        elif hasattr(base, 'localAvatar') and self.commonChatFlags & base.localAvatar.commonChatFlags & OTPGlobals.CommonChat:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif self.commonChatFlags & OTPGlobals.SuperChat:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif hasattr(base, 'localAvatar') and base.localAvatar.commonChatFlags & OTPGlobals.SuperChat:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif base.cr.getFriendFlags(self.doId) & OTPGlobals.FriendChat:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif base.cr.playerFriendsManager.findPlayerIdFromAvId(self.doId) is not None:
            playerInfo = base.cr.playerFriendsManager.findPlayerInfoFromAvId(self.doId)
            if playerInfo.openChatFriendshipYesNo:
                self.understandable = 1
                self.nametag.setColorCode(NametagGroup.CCFreeChat)
            elif playerInfo.isUnderstandable():
                self.understandable = 1
            else:
                self.understandable = 0
        elif hasattr(base, 'localAvatar') and self.whitelistChatFlags & base.localAvatar.whitelistChatFlags:
            self.understandable = 1
        else:
            self.understandable = 0
        if not hasattr(self, 'nametag'):
            self.notify.warning('no nametag attributed, but would have been used')
        else:
            self.nametag.setColorCode(self.playerType)
        return

    def isUnderstandable(self):
        return self.understandable

    def setDNAString(self, dnaString):
        pass

    def setDNA(self, dna):
        pass

    def getAvatarScale(self):
        return self.scale

    def setAvatarScale(self, scale):
        if self.scale != scale:
            self.scale = scale
            self.getGeomNode().setScale(scale)
            self.setHeight(self.height)

    def getNametagScale(self):
        return self.nametagScale

    def setNametagScale(self, scale):
        self.nametagScale = scale
        self.nametag3d.setScale(scale)

    def adjustNametag3d(self, parentScale = 1.0):
        self.nametag3d.setPos(0, 0, self.height + 0.5)

    def getHeight(self):
        return self.height

    def setHeight(self, height):
        self.height = height
        self.adjustNametag3d()
        if self.collTube:
            self.collTube.setPointB(0, 0, height - self.getRadius())
            if self.collNodePath:
                self.collNodePath.forceRecomputeBounds()
        if self.battleTube:
            self.battleTube.setPointB(0, 0, height - self.getRadius())

    def getRadius(self):
        return OTPGlobals.AvatarDefaultRadius

    def getName(self):
        return self.name

    def getType(self):
        return self.avatarType

    def setName(self, name):
        if hasattr(self, 'isDisguised'):
            if self.isDisguised:
                return
        self.name = name
        if hasattr(self, 'nametag'):
            self.nametag.setName(name)

    def setDisplayName(self, str):
        if hasattr(self, 'isDisguised'):
            if self.isDisguised:
                return
        self.nametag.setDisplayName(str)

    def getFont(self):
        return self.__font

    def setFont(self, font):
        self.__font = font
        self.nametag.setFont(font)

    def getStyle(self):
        return self.style

    def setStyle(self, style):
        self.style = style

    def getDialogueArray(self):
        return None

    def playCurrentDialogue(self, dialogue, chatFlags, interrupt = 1):
        if interrupt and self.__currentDialogue is not None:
            self.__currentDialogue.stop()
        self.__currentDialogue = dialogue
        if dialogue:
            base.playSfx(dialogue, node=self)
        elif chatFlags & CFSpeech != 0 and self.nametag.getNumChatPages() > 0:
            self.playDialogueForString(self.nametag.getChat())
            if self.soundChatBubble != None:
                base.playSfx(self.soundChatBubble, node=self)
        return

    def playDialogueForString(self, chatString):
        searchString = chatString.lower()
        if searchString.find(OTPLocalizer.DialogSpecial) >= 0:
            type = 'special'
        elif searchString.find(OTPLocalizer.DialogExclamation) >= 0:
            type = 'exclamation'
        elif searchString.find(OTPLocalizer.DialogQuestion) >= 0:
            type = 'question'
        elif random.randint(0, 1):
            type = 'statementA'
        else:
            type = 'statementB'
        stringLength = len(chatString)
        if stringLength <= OTPLocalizer.DialogLength1:
            length = 1
        elif stringLength <= OTPLocalizer.DialogLength2:
            length = 2
        elif stringLength <= OTPLocalizer.DialogLength3:
            length = 3
        else:
            length = 4
        self.playDialogue(type, length)

    def playDialogue(self, type, length):
        dialogueArray = self.getDialogueArray()
        if dialogueArray == None:
            return
        sfxIndex = None
        if type == 'statementA' or type == 'statementB':
            if length == 1:
                sfxIndex = 0
            elif length == 2:
                sfxIndex = 1
            elif length >= 3:
                sfxIndex = 2
        elif type == 'question':
            sfxIndex = 3
        elif type == 'exclamation':
            sfxIndex = 4
        elif type == 'special':
            sfxIndex = 5
        else:
            notify.error('unrecognized dialogue type: ', type)
        if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None:
            base.playSfx(dialogueArray[sfxIndex], node=self)
        return

    def getDialogueSfx(self, type, length):
        retval = None
        dialogueArray = self.getDialogueArray()
        if dialogueArray == None:
            return
        sfxIndex = None
        if type == 'statementA' or type == 'statementB':
            if length == 1:
                sfxIndex = 0
            elif length == 2:
                sfxIndex = 1
            elif length >= 3:
                sfxIndex = 2
        elif type == 'question':
            sfxIndex = 3
        elif type == 'exclamation':
            sfxIndex = 4
        elif type == 'special':
            sfxIndex = 5
        else:
            notify.error('unrecognized dialogue type: ', type)
        if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None:
            retval = dialogueArray[sfxIndex]
        return retval

    def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1):
        self.nametag.setChat(chatString, chatFlags)
        self.playCurrentDialogue(dialogue, chatFlags, interrupt)

    def setChatMuted(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0):
        pass

    def displayTalk(self, chatString):
        if not base.cr.avatarFriendsManager.checkIgnored(self.doId):
            if base.talkAssistant.isThought(chatString):
                self.nametag.setChat(base.talkAssistant.removeThoughtPrefix(chatString), CFThought)
            else:
                self.nametag.setChat(chatString, CFSpeech | CFTimeout)

    def clearChat(self):
        self.nametag.clearChat()

    def isInView(self):
        pos = self.getPos(camera)
        eyePos = Point3(pos[0], pos[1], pos[2] + self.getHeight())
        return base.camNode.isInView(eyePos)

    def getNameVisible(self):
        return self.__nameVisible

    def setNameVisible(self, bool):
        self.__nameVisible = bool
        if bool:
            self.showName()
        if not bool:
            self.hideName()

    def hideName(self):
        self.nametag.getNametag3d().setContents(Nametag.CSpeech | Nametag.CThought)

    def showName(self):
        if self.__nameVisible and not self.ghostMode:
            self.nametag.getNametag3d().setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought)

    def hideNametag2d(self):
        self.nametag2dContents = 0
        self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist)

    def showNametag2d(self):
        self.nametag2dContents = self.nametag2dNormalContents
        if self.ghostMode:
            self.nametag2dContents = Nametag.CSpeech
        self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist)

    def hideNametag3d(self):
        self.nametag.getNametag3d().setContents(0)

    def showNametag3d(self):
        if self.__nameVisible and not self.ghostMode:
            self.nametag.getNametag3d().setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought)
        else:
            self.nametag.getNametag3d().setContents(0)

    def setPickable(self, flag):
        self.nametag.setActive(flag)

    def clickedNametag(self):
        if self.nametag.hasButton():
            self.advancePageNumber()
        elif self.nametag.isActive():
            messenger.send('clickedNametag', [self])

    def setPageChat(self, addressee, paragraph, message, quitButton, extraChatFlags = None, dialogueList = [], pageButton = True):
        self.__chatAddressee = addressee
        self.__chatPageNumber = None
        self.__chatParagraph = paragraph
        self.__chatMessage = message
        if extraChatFlags is None:
            self.__chatFlags = CFSpeech
        else:
            self.__chatFlags = CFSpeech | extraChatFlags
        self.__chatDialogueList = dialogueList
        self.__chatSet = 0
        self.__chatLocal = 0
        self.__updatePageChat()
        if addressee == base.localAvatar.doId:
            if pageButton:
                self.__chatFlags |= CFPageButton
            if quitButton == None:
                self.__chatFlags |= CFNoQuitButton
            elif quitButton:
                self.__chatFlags |= CFQuitButton
            self.b_setPageNumber(self.__chatParagraph, 0)
        return

    def setLocalPageChat(self, message, quitButton, extraChatFlags = None, dialogueList = []):
        self.__chatAddressee = base.localAvatar.doId
        self.__chatPageNumber = None
        self.__chatParagraph = None
        self.__chatMessage = message
        if extraChatFlags is None:
            self.__chatFlags = CFSpeech
        else:
            self.__chatFlags = CFSpeech | extraChatFlags
        self.__chatDialogueList = dialogueList
        self.__chatSet = 1
        self.__chatLocal = 1
        self.__chatFlags |= CFPageButton
        if quitButton == None:
            self.__chatFlags |= CFNoQuitButton
        elif quitButton:
            self.__chatFlags |= CFQuitButton
        if len(dialogueList) > 0:
            dialogue = dialogueList[0]
        else:
            dialogue = None
        self.clearChat()
        self.setChatAbsolute(message, self.__chatFlags, dialogue)
        self.setPageNumber(None, 0)
        return

    def setPageNumber(self, paragraph, pageNumber, timestamp = None):
        if timestamp == None:
            elapsed = 0.0
        else:
            elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp)
        self.__chatPageNumber = [paragraph, pageNumber]
        self.__updatePageChat()
        if hasattr(self, 'uniqueName'):
            if pageNumber >= 0:
                messenger.send(self.uniqueName('nextChatPage'), [pageNumber, elapsed])
            else:
                messenger.send(self.uniqueName('doneChatPage'), [elapsed])
        elif pageNumber >= 0:
            messenger.send('nextChatPage', [pageNumber, elapsed])
        else:
            messenger.send('doneChatPage', [elapsed])
        return

    def advancePageNumber(self):
        if self.__chatAddressee == base.localAvatar.doId and self.__chatPageNumber != None and self.__chatPageNumber[0] == self.__chatParagraph:
            pageNumber = self.__chatPageNumber[1]
            if pageNumber >= 0:
                pageNumber += 1
                if pageNumber >= self.nametag.getNumChatPages():
                    pageNumber = -1
                if self.__chatLocal:
                    self.setPageNumber(self.__chatParagraph, pageNumber)
                else:
                    self.b_setPageNumber(self.__chatParagraph, pageNumber)
        return

    def __updatePageChat(self):
        if self.__chatPageNumber != None and self.__chatPageNumber[0] == self.__chatParagraph:
            pageNumber = self.__chatPageNumber[1]
            if pageNumber >= 0:
                if not self.__chatSet:
                    if len(self.__chatDialogueList) > 0:
                        dialogue = self.__chatDialogueList[0]
                    else:
                        dialogue = None
                    self.setChatAbsolute(self.__chatMessage, self.__chatFlags, dialogue)
                    self.__chatSet = 1
                if pageNumber < self.nametag.getNumChatPages():
                    self.nametag.setPageNumber(pageNumber)
                    if pageNumber > 0:
                        if len(self.__chatDialogueList) > pageNumber:
                            dialogue = self.__chatDialogueList[pageNumber]
                        else:
                            dialogue = None
                        self.playCurrentDialogue(dialogue, self.__chatFlags)
                else:
                    self.clearChat()
            else:
                self.clearChat()
        return

    def getAirborneHeight(self):
        height = self.getPos(self.shadowPlacer.shadowNodePath)
        return height.getZ() + 0.025

    def initializeNametag3d(self):
        self.deleteNametag3d()
        nametagNode = self.nametag.getNametag3d()
        self.nametagNodePath = self.nametag3d.attachNewNode(nametagNode)
        iconNodePath = self.nametag.getNameIcon()
        for cJoint in self.getNametagJoints():
            cJoint.clearNetTransforms()
            cJoint.addNetTransform(nametagNode)

    def nametagAmbientLightChanged(self, newlight):
        self.nametag3d.setLightOff()
        if newlight:
            self.nametag3d.setLight(newlight)

    def deleteNametag3d(self):
        if self.nametagNodePath:
            self.nametagNodePath.removeNode()
            self.nametagNodePath = None
        return

    def initializeBodyCollisions(self, collIdStr):
        self.collTube = CollisionTube(0, 0, 0.5, 0, 0, self.height - self.getRadius(), self.getRadius())
        self.collNode = CollisionNode(collIdStr)
        self.collNode.addSolid(self.collTube)
        self.collNodePath = self.attachNewNode(self.collNode)
        if self.ghostMode:
            self.collNode.setCollideMask(OTPGlobals.GhostBitmask)
        else:
            self.collNode.setCollideMask(OTPGlobals.WallBitmask)

    def stashBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.stash()

    def unstashBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.unstash()

    def disableBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.removeNode()
            del self.collNodePath
        self.collTube = None
        return

    def addActive(self):
        if base.wantNametags:
            try:
                Avatar.ActiveAvatars.remove(self)
            except ValueError:
                pass

            Avatar.ActiveAvatars.append(self)
            self.nametag.manage(base.marginManager)
            self.accept(self.nametag.getUniqueId(), self.clickedNametag)

    def removeActive(self):
        if base.wantNametags:
            try:
                Avatar.ActiveAvatars.remove(self)
            except ValueError:
                pass

            self.nametag.unmanage(base.marginManager)
            self.ignore(self.nametag.getUniqueId())

    def loop(self, animName, restart = 1, partName = None, fromFrame = None, toFrame = None):
        return Actor.loop(self, animName, restart, partName, fromFrame, toFrame)
Beispiel #3
0
class Avatar(Actor, ShadowCaster):
    notify = directNotify.newCategory('Avatar')
    ActiveAvatars = []
    ManagesNametagAmbientLightChanged = False

    def __init__(self, other=None):
        self.name = ''
        try:
            self.Avatar_initialized
            return
        except:
            self.Avatar_initialized = 1

        Actor.__init__(self, None, None, other, flattenable=0, setFinal=1)
        ShadowCaster.__init__(self)
        self.__font = OTPGlobals.getInterfaceFont()
        self.soundChatBubble = None
        self.avatarType = ''
        self.nametagNodePath = None
        self.__nameVisible = 1
        self.nametag = NametagGroup()
        self.nametag.setAvatar(self)
        self.nametag.setFont(OTPGlobals.getInterfaceFont())
        self.nametag2dContents = Nametag.CName | Nametag.CSpeech
        self.nametag2dDist = Nametag.CName | Nametag.CSpeech
        self.nametag2dNormalContents = Nametag.CName | Nametag.CSpeech
        self.nametag3d = self.attachNewNode('nametag3d')
        self.nametag3d.setTag('cam', 'nametag')
        self.nametag3d.setLightOff()
        if self.ManagesNametagAmbientLightChanged:
            self.acceptNametagAmbientLightChange()
        OTPRender.renderReflection(False, self.nametag3d, 'otp_avatar_nametag',
                                   None)
        self.getGeomNode().showThrough(OTPRender.ShadowCameraBitmask)
        self.nametag3d.hide(OTPRender.ShadowCameraBitmask)
        self.collTube = None
        self.battleTube = None
        self.scale = 1.0
        self.nametagScale = 1.0
        self.height = 0.0
        self.battleTubeHeight = 0.0
        self.battleTubeRadius = 0.0
        self.style = None
        self.commonChatFlags = 0
        self.understandable = 1
        self.setPlayerType(NametagGroup.CCNormal)
        self.ghostMode = 0
        self.__chatParagraph = None
        self.__chatMessage = None
        self.__chatFlags = 0
        self.__chatPageNumber = None
        self.__chatAddressee = None
        self.__chatDialogueList = []
        self.__chatSet = 0
        self.__chatLocal = 0
        self.__currentDialogue = None
        self.whitelistChatFlags = 0
        return

    def delete(self):
        try:
            self.Avatar_deleted
        except:
            self.deleteNametag3d()
            Actor.cleanup(self)
            if self.ManagesNametagAmbientLightChanged:
                self.ignoreNametagAmbientLightChange()
            self.Avatar_deleted = 1
            del self.__font
            del self.style
            del self.soundChatBubble
            del self.nametag
            self.nametag3d.removeNode()
            ShadowCaster.delete(self)
            Actor.delete(self)

    def acceptNametagAmbientLightChange(self):
        self.accept('nametagAmbientLightChanged',
                    self.nametagAmbientLightChanged)

    def ignoreNametagAmbientLightChange(self):
        self.ignore('nametagAmbientLightChanged')

    def isLocal(self):
        return 0

    def isPet(self):
        return False

    def isProxy(self):
        return False

    def setPlayerType(self, playerType):
        self.playerType = playerType
        if not hasattr(self, 'nametag'):
            self.notify.warning(
                'no nametag attributed, but would have been used.')
            return
        if self.isUnderstandable():
            self.nametag.setColorCode(self.playerType)
        else:
            self.nametag.setColorCode(NametagGroup.CCNoChat)

    def setCommonChatFlags(self, commonChatFlags):
        self.commonChatFlags = commonChatFlags
        self.considerUnderstandable()
        if self == base.localAvatar:
            reconsiderAllUnderstandable()

    def setWhitelistChatFlags(self, whitelistChatFlags):
        self.whitelistChatFlags = whitelistChatFlags
        self.considerUnderstandable()
        if self == base.localAvatar:
            reconsiderAllUnderstandable()

    def considerUnderstandable(self):
        speed = 0
        if self.playerType in (NametagGroup.CCNormal, NametagGroup.CCFreeChat,
                               NametagGroup.CCSpeedChat):
            self.setPlayerType(NametagGroup.CCSpeedChat)
            speed = 1
        if hasattr(base, 'localAvatar') and self == base.localAvatar:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif self.playerType == NametagGroup.CCSuit:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCSuit)
        elif self.playerType not in (NametagGroup.CCNormal,
                                     NametagGroup.CCFreeChat,
                                     NametagGroup.CCSpeedChat):
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCNoChat)
        elif hasattr(
                base, 'localAvatar'
        ) and self.commonChatFlags & base.localAvatar.commonChatFlags & OTPGlobals.CommonChat:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif self.commonChatFlags & OTPGlobals.SuperChat:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif hasattr(
                base, 'localAvatar'
        ) and base.localAvatar.commonChatFlags & OTPGlobals.SuperChat:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif base.cr.getFriendFlags(self.doId) & OTPGlobals.FriendChat:
            self.understandable = 1
            self.setPlayerType(NametagGroup.CCFreeChat)
        elif base.cr.playerFriendsManager.findPlayerIdFromAvId(
                self.doId) is not None:
            playerInfo = base.cr.playerFriendsManager.findPlayerInfoFromAvId(
                self.doId)
            if playerInfo.openChatFriendshipYesNo:
                self.understandable = 1
                self.nametag.setColorCode(NametagGroup.CCFreeChat)
            elif playerInfo.isUnderstandable():
                self.understandable = 1
            else:
                self.understandable = 0
        elif hasattr(
                base, 'localAvatar'
        ) and self.whitelistChatFlags & base.localAvatar.whitelistChatFlags:
            self.understandable = 1
        else:
            self.understandable = 0
        if not hasattr(self, 'nametag'):
            self.notify.warning(
                'no nametag attributed, but would have been used')
        else:
            self.nametag.setColorCode(self.playerType)
        return

    def isUnderstandable(self):
        return self.understandable

    def setDNAString(self, dnaString):
        pass

    def setDNA(self, dna):
        pass

    def getAvatarScale(self):
        return self.scale

    def setAvatarScale(self, scale):
        if self.scale != scale:
            self.scale = scale
            self.getGeomNode().setScale(scale)
            self.setHeight(self.height)

    def getNametagScale(self):
        return self.nametagScale

    def setNametagScale(self, scale):
        self.nametagScale = scale
        self.nametag3d.setScale(scale)

    def adjustNametag3d(self, parentScale=1.0):
        self.nametag3d.setPos(0, 0, self.height + 0.5)

    def getHeight(self):
        return self.height

    def setHeight(self, height):
        self.height = height
        self.adjustNametag3d()
        if self.collTube:
            self.collTube.setPointB(0, 0, height - self.getRadius())
            if self.collNodePath:
                self.collNodePath.forceRecomputeBounds()
        if self.battleTube:
            self.battleTube.setPointB(0, 0, height - self.getRadius())

    def getRadius(self):
        return OTPGlobals.AvatarDefaultRadius

    def getName(self):
        return self.name

    def getType(self):
        return self.avatarType

    def setName(self, name):
        if hasattr(self, 'isDisguised'):
            if self.isDisguised:
                return
        self.name = name
        if hasattr(self, 'nametag'):
            self.nametag.setName(name)

    def setDisplayName(self, str):
        if hasattr(self, 'isDisguised'):
            if self.isDisguised:
                return
        self.nametag.setDisplayName(str)

    def getFont(self):
        return self.__font

    def setFont(self, font):
        self.__font = font
        self.nametag.setFont(font)

    def getStyle(self):
        return self.style

    def setStyle(self, style):
        self.style = style

    def getDialogueArray(self):
        return None

    def playCurrentDialogue(self, dialogue, chatFlags, interrupt=1):
        if interrupt and self.__currentDialogue is not None:
            self.__currentDialogue.stop()
        self.__currentDialogue = dialogue
        if dialogue:
            base.playSfx(dialogue, node=self)
        elif chatFlags & CFSpeech != 0 and self.nametag.getNumChatPages() > 0:
            self.playDialogueForString(self.nametag.getChat())
            if self.soundChatBubble != None:
                base.playSfx(self.soundChatBubble, node=self)
        return

    def playDialogueForString(self, chatString):
        searchString = chatString.lower()
        if searchString.find(OTPLocalizer.DialogSpecial) >= 0:
            type = 'special'
        elif searchString.find(OTPLocalizer.DialogExclamation) >= 0:
            type = 'exclamation'
        elif searchString.find(OTPLocalizer.DialogQuestion) >= 0:
            type = 'question'
        elif random.randint(0, 1):
            type = 'statementA'
        else:
            type = 'statementB'
        stringLength = len(chatString)
        if stringLength <= OTPLocalizer.DialogLength1:
            length = 1
        elif stringLength <= OTPLocalizer.DialogLength2:
            length = 2
        elif stringLength <= OTPLocalizer.DialogLength3:
            length = 3
        else:
            length = 4
        self.playDialogue(type, length)

    def playDialogue(self, type, length):
        dialogueArray = self.getDialogueArray()
        if dialogueArray == None:
            return
        sfxIndex = None
        if type == 'statementA' or type == 'statementB':
            if length == 1:
                sfxIndex = 0
            elif length == 2:
                sfxIndex = 1
            elif length >= 3:
                sfxIndex = 2
        elif type == 'question':
            sfxIndex = 3
        elif type == 'exclamation':
            sfxIndex = 4
        elif type == 'special':
            sfxIndex = 5
        else:
            notify.error('unrecognized dialogue type: ', type)
        if sfxIndex != None and sfxIndex < len(
                dialogueArray) and dialogueArray[sfxIndex] != None:
            base.playSfx(dialogueArray[sfxIndex], node=self)
        return

    def getDialogueSfx(self, type, length):
        retval = None
        dialogueArray = self.getDialogueArray()
        if dialogueArray == None:
            return
        sfxIndex = None
        if type == 'statementA' or type == 'statementB':
            if length == 1:
                sfxIndex = 0
            elif length == 2:
                sfxIndex = 1
            elif length >= 3:
                sfxIndex = 2
        elif type == 'question':
            sfxIndex = 3
        elif type == 'exclamation':
            sfxIndex = 4
        elif type == 'special':
            sfxIndex = 5
        else:
            notify.error('unrecognized dialogue type: ', type)
        if sfxIndex != None and sfxIndex < len(
                dialogueArray) and dialogueArray[sfxIndex] != None:
            retval = dialogueArray[sfxIndex]
        return retval

    def setChatAbsolute(self,
                        chatString,
                        chatFlags,
                        dialogue=None,
                        interrupt=1):
        self.nametag.setChat(chatString, chatFlags)
        self.playCurrentDialogue(dialogue, chatFlags, interrupt)

    def setChatMuted(self,
                     chatString,
                     chatFlags,
                     dialogue=None,
                     interrupt=1,
                     quiet=0):
        pass

    def displayTalk(self, chatString):
        if not base.cr.avatarFriendsManager.checkIgnored(self.doId):
            if base.talkAssistant.isThought(chatString):
                self.nametag.setChat(
                    base.talkAssistant.removeThoughtPrefix(chatString),
                    CFThought)
            else:
                self.nametag.setChat(chatString, CFSpeech | CFTimeout)

    def clearChat(self):
        self.nametag.clearChat()

    def isInView(self):
        pos = self.getPos(camera)
        eyePos = Point3(pos[0], pos[1], pos[2] + self.getHeight())
        return base.camNode.isInView(eyePos)

    def getNameVisible(self):
        return self.__nameVisible

    def setNameVisible(self, bool):
        self.__nameVisible = bool
        if bool:
            self.showName()
        if not bool:
            self.hideName()

    def hideName(self):
        self.nametag.getNametag3d().setContents(Nametag.CSpeech
                                                | Nametag.CThought)

    def showName(self):
        if self.__nameVisible and not self.ghostMode:
            self.nametag.getNametag3d().setContents(Nametag.CName
                                                    | Nametag.CSpeech
                                                    | Nametag.CThought)

    def hideNametag2d(self):
        self.nametag2dContents = 0
        self.nametag.getNametag2d().setContents(self.nametag2dContents
                                                & self.nametag2dDist)

    def showNametag2d(self):
        self.nametag2dContents = self.nametag2dNormalContents
        if self.ghostMode:
            self.nametag2dContents = Nametag.CSpeech
        self.nametag.getNametag2d().setContents(self.nametag2dContents
                                                & self.nametag2dDist)

    def hideNametag3d(self):
        self.nametag.getNametag3d().setContents(0)

    def showNametag3d(self):
        if self.__nameVisible and not self.ghostMode:
            self.nametag.getNametag3d().setContents(Nametag.CName
                                                    | Nametag.CSpeech
                                                    | Nametag.CThought)
        else:
            self.nametag.getNametag3d().setContents(0)

    def setPickable(self, flag):
        self.nametag.setActive(flag)

    def clickedNametag(self):
        if self.nametag.hasButton():
            self.advancePageNumber()
        elif self.nametag.isActive():
            messenger.send('clickedNametag', [self])

    def setPageChat(self,
                    addressee,
                    paragraph,
                    message,
                    quitButton,
                    extraChatFlags=None,
                    dialogueList=[],
                    pageButton=True):
        self.__chatAddressee = addressee
        self.__chatPageNumber = None
        self.__chatParagraph = paragraph
        self.__chatMessage = message
        if extraChatFlags is None:
            self.__chatFlags = CFSpeech
        else:
            self.__chatFlags = CFSpeech | extraChatFlags
        self.__chatDialogueList = dialogueList
        self.__chatSet = 0
        self.__chatLocal = 0
        self.__updatePageChat()
        if addressee == base.localAvatar.doId:
            if pageButton:
                self.__chatFlags |= CFPageButton
            if quitButton == None:
                self.__chatFlags |= CFNoQuitButton
            elif quitButton:
                self.__chatFlags |= CFQuitButton
            self.b_setPageNumber(self.__chatParagraph, 0)
        return

    def setLocalPageChat(self,
                         message,
                         quitButton,
                         extraChatFlags=None,
                         dialogueList=[]):
        self.__chatAddressee = base.localAvatar.doId
        self.__chatPageNumber = None
        self.__chatParagraph = None
        self.__chatMessage = message
        if extraChatFlags is None:
            self.__chatFlags = CFSpeech
        else:
            self.__chatFlags = CFSpeech | extraChatFlags
        self.__chatDialogueList = dialogueList
        self.__chatSet = 1
        self.__chatLocal = 1
        self.__chatFlags |= CFPageButton
        if quitButton == None:
            self.__chatFlags |= CFNoQuitButton
        elif quitButton:
            self.__chatFlags |= CFQuitButton
        if len(dialogueList) > 0:
            dialogue = dialogueList[0]
        else:
            dialogue = None
        self.clearChat()
        self.setChatAbsolute(message, self.__chatFlags, dialogue)
        self.setPageNumber(None, 0)
        return

    def setPageNumber(self, paragraph, pageNumber, timestamp=None):
        if timestamp == None:
            elapsed = 0.0
        else:
            elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp)
        self.__chatPageNumber = [paragraph, pageNumber]
        self.__updatePageChat()
        if hasattr(self, 'uniqueName'):
            if pageNumber >= 0:
                messenger.send(self.uniqueName('nextChatPage'),
                               [pageNumber, elapsed])
            else:
                messenger.send(self.uniqueName('doneChatPage'), [elapsed])
        elif pageNumber >= 0:
            messenger.send('nextChatPage', [pageNumber, elapsed])
        else:
            messenger.send('doneChatPage', [elapsed])
        return

    def advancePageNumber(self):
        if self.__chatAddressee == base.localAvatar.doId and self.__chatPageNumber != None and self.__chatPageNumber[
                0] == self.__chatParagraph:
            pageNumber = self.__chatPageNumber[1]
            if pageNumber >= 0:
                pageNumber += 1
                if pageNumber >= self.nametag.getNumChatPages():
                    pageNumber = -1
                if self.__chatLocal:
                    self.setPageNumber(self.__chatParagraph, pageNumber)
                else:
                    self.b_setPageNumber(self.__chatParagraph, pageNumber)
        return

    def __updatePageChat(self):
        if self.__chatPageNumber != None and self.__chatPageNumber[
                0] == self.__chatParagraph:
            pageNumber = self.__chatPageNumber[1]
            if pageNumber >= 0:
                if not self.__chatSet:
                    if len(self.__chatDialogueList) > 0:
                        dialogue = self.__chatDialogueList[0]
                    else:
                        dialogue = None
                    self.setChatAbsolute(self.__chatMessage, self.__chatFlags,
                                         dialogue)
                    self.__chatSet = 1
                if pageNumber < self.nametag.getNumChatPages():
                    self.nametag.setPageNumber(pageNumber)
                    if pageNumber > 0:
                        if len(self.__chatDialogueList) > pageNumber:
                            dialogue = self.__chatDialogueList[pageNumber]
                        else:
                            dialogue = None
                        self.playCurrentDialogue(dialogue, self.__chatFlags)
                else:
                    self.clearChat()
            else:
                self.clearChat()
        return

    def getAirborneHeight(self):
        height = self.getPos(self.shadowPlacer.shadowNodePath)
        return height.getZ() + 0.025

    def initializeNametag3d(self):
        self.deleteNametag3d()
        nametagNode = self.nametag.getNametag3d()
        self.nametagNodePath = self.nametag3d.attachNewNode(nametagNode)
        iconNodePath = self.nametag.getNameIcon()
        for cJoint in self.getNametagJoints():
            cJoint.clearNetTransforms()
            cJoint.addNetTransform(nametagNode)

    def nametagAmbientLightChanged(self, newlight):
        self.nametag3d.setLightOff()
        if newlight:
            self.nametag3d.setLight(newlight)

    def deleteNametag3d(self):
        if self.nametagNodePath:
            self.nametagNodePath.removeNode()
            self.nametagNodePath = None
        return

    def initializeBodyCollisions(self, collIdStr):
        self.collTube = CollisionTube(0, 0, 0.5, 0, 0,
                                      self.height - self.getRadius(),
                                      self.getRadius())
        self.collNode = CollisionNode(collIdStr)
        self.collNode.addSolid(self.collTube)
        self.collNodePath = self.attachNewNode(self.collNode)
        if self.ghostMode:
            self.collNode.setCollideMask(OTPGlobals.GhostBitmask)
        else:
            self.collNode.setCollideMask(OTPGlobals.WallBitmask)

    def stashBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.stash()

    def unstashBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.unstash()

    def disableBodyCollisions(self):
        if hasattr(self, 'collNodePath'):
            self.collNodePath.removeNode()
            del self.collNodePath
        self.collTube = None
        return

    def addActive(self):
        if base.wantNametags:
            try:
                Avatar.ActiveAvatars.remove(self)
            except ValueError:
                pass

            Avatar.ActiveAvatars.append(self)
            self.nametag.manage(base.marginManager)
            self.accept(self.nametag.getUniqueId(), self.clickedNametag)

    def removeActive(self):
        if base.wantNametags:
            try:
                Avatar.ActiveAvatars.remove(self)
            except ValueError:
                pass

            self.nametag.unmanage(base.marginManager)
            self.ignore(self.nametag.getUniqueId())

    def loop(self,
             animName,
             restart=1,
             partName=None,
             fromFrame=None,
             toFrame=None):
        return Actor.loop(self, animName, restart, partName, fromFrame,
                          toFrame)