def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(background[0], background[1], background[2],
                                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(model,
                                       modelWidth,
                                       modelHeight,
                                       self.chatTextNode,
                                       foreground=foreground,
                                       background=background,
                                       reversed=self.chatReversed,
                                       button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)

        # Chat balloon Popup Effect
        # TODO: Anim toggle
        self.chatBalloon.setScale(0, 0, 0)
        Sequence(
            self.chatBalloon.scaleInterval(.2,
                                           VBase3(1.1, 1.1, 1.1),
                                           blendType='easeInOut'),
            self.chatBalloon.scaleInterval(.09,
                                           VBase3(1, 1, 1),
                                           blendType='easeInOut')).start()
예제 #2
0
 def draw(self):
     if self.isClickable():
         foreground, background = self.whisperColor[self.clickState]
     else:
         foreground, background = self.whisperColor[PGButton.SInactive]
     self.chatBalloon = ChatBalloon(NametagGlobals.chatBalloon2dModel, NametagGlobals.chatBalloon2dWidth, NametagGlobals.chatBalloon2dHeight, self.textNode, foreground=foreground, background=background)
     self.chatBalloon.reparentTo(self.contents)
     left, right, bottom, top = self.textNode.getFrameActual()
     center = self.contents.getRelativePoint(self.chatBalloon.textNodePath, ((left + right) / 2.0, 0, (bottom + top) / 2.0))
     self.chatBalloon.setPos(self.chatBalloon, -center)
     self.quitButton = WhisperQuitButton(self)
     quitButtonNodePath = self.contents.attachNewNode(self.quitButton)
     quitButtonNodePath.setPos(self.contents.getRelativePoint(self.chatBalloon.textNodePath, (right, 0, top)))
     quitButtonNodePath.setPos(quitButtonNodePath, self.QUIT_BUTTON_SHIFT)
     self.quitButton.setClickEvent(self.quitEvent)
     Parallel(LerpColorScaleInterval(self.contents, 0.2, VBase4(1, 1, 1, 1), VBase4(1, 1, 1, 0)), Sequence(LerpScaleInterval(self.contents, 0.2, self.CONTENTS_SCALE + 0.01, 0), LerpScaleInterval(self.contents, 0.09, self.CONTENTS_SCALE))).start()
예제 #3
0
    def draw(self):
        if self.isClickable():
            foreground, background = self.whisperColor[self.clickState]
        else:
            foreground, background = self.whisperColor[PGButton.SInactive]
        self.chatBalloon = ChatBalloon(
            NametagGlobals.chatBalloon2dModel,
            NametagGlobals.chatBalloon2dWidth,
            NametagGlobals.chatBalloon2dHeight, self.textNode,
            foreground=foreground, background=background
        )
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.textNode.getFrameActual()
        center = self.contents.getRelativePoint(
            self.chatBalloon.textNodePath,
            ((left+right) / 2.0, 0, (bottom+top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)

        # Draw the quit button:
        self.quitButton = WhisperQuitButton(self)
        quitButtonNodePath = self.contents.attachNewNode(self.quitButton)

        # Move the quit button to the top right of the TextNode:
        quitButtonNodePath.setPos(self.contents.getRelativePoint(
            self.chatBalloon.textNodePath, (right, 0, top)))

        # Apply the quit button shift:
        quitButtonNodePath.setPos(quitButtonNodePath, self.QUIT_BUTTON_SHIFT)

        # Allow the quit button to close this whisper:
        self.quitButton.setClickEvent(self.quitEvent)
예제 #4
0
    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        # Prefix the nametag text:
        self.chatTextNode.setText(self.getText() + ': ' + self.actualChatText)

        # Set our priority in the margin system:
        self.setPriority(MarginGlobals.MP_normal)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(background[0], background[1], background[2],
                                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(model,
                                       modelWidth,
                                       modelHeight,
                                       self.chatTextNode,
                                       foreground=foreground,
                                       background=background,
                                       reversed=self.chatReversed,
                                       button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.chatTextNode.getFrameActual()
        center = self.contents.getRelativePoint(self.chatBalloon.textNodePath,
                                                ((left + right) / 2.0, 0,
                                                 (bottom + top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)
예제 #5
0
    def draw(self):
        if self.isClickable():
            foreground, background = self.whisperColor[self.clickState]
        else:
            foreground, background = self.whisperColor[PGButton.SInactive]
        self.chatBalloon = ChatBalloon(NametagGlobals.chatBalloon2dModel,
                                       NametagGlobals.chatBalloon2dWidth,
                                       NametagGlobals.chatBalloon2dHeight,
                                       self.textNode,
                                       foreground=foreground,
                                       background=background)
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.textNode.getFrameActual()
        center = self.contents.getRelativePoint(self.chatBalloon.textNodePath,
                                                ((left + right) / 2.0, 0,
                                                 (bottom + top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)

        # Draw the quit button:
        self.quitButton = WhisperQuitButton(self)
        quitButtonNodePath = self.contents.attachNewNode(self.quitButton)

        # Move the quit button to the top right of the TextNode:
        quitButtonNodePath.setPos(
            self.contents.getRelativePoint(self.chatBalloon.textNodePath,
                                           (right, 0, top)))

        # Apply the quit button shift:
        quitButtonNodePath.setPos(quitButtonNodePath, self.QUIT_BUTTON_SHIFT)

        # Allow the quit button to close this whisper:
        self.quitButton.setClickEvent(self.quitEvent)

        Parallel(
            LerpColorScaleInterval(self.contents, .2, VBase4(1, 1, 1, 1),
                                   VBase4(1, 1, 1, 0)),
            Sequence(
                LerpScaleInterval(self.contents, .2,
                                  (self.CONTENTS_SCALE + 0.01), (0)),
                LerpScaleInterval(self.contents, .09,
                                  (self.CONTENTS_SCALE)))).start()
예제 #6
0
    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(
                background[0], background[1], background[2],
                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(
            model, modelWidth, modelHeight, self.chatTextNode,
            foreground=foreground, background=background,
            reversed=self.chatReversed,
            button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)
    def draw(self):
        if self.isClickable():
            foreground, background = self.whisperColor[self.clickState]
        else:
            foreground, background = self.whisperColor[PGButton.SInactive]
        self.chatBalloon = ChatBalloon(NametagGlobals.chatBalloon2dModel,
                                       NametagGlobals.chatBalloon2dWidth,
                                       NametagGlobals.chatBalloon2dHeight,
                                       self.textNode,
                                       foreground=foreground,
                                       background=background)
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.textNode.getFrameActual()
        center = self.contents.getRelativePoint(self.chatBalloon.textNodePath,
                                                ((left + right) / 2.0, 0,
                                                 (bottom + top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)
    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        # Prefix the nametag text:
        self.chatTextNode.setText(self.getText() + ': ' + self.actualChatText)

        # Set our priority in the margin system:
        self.setPriority(MarginGlobals.MP_normal)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(
                background[0], background[1], background[2],
                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(
            model, modelWidth, modelHeight, self.chatTextNode,
            foreground=foreground, background=background,
            reversed=self.chatReversed,
            button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.chatTextNode.getFrameActual()
        center = self.contents.getRelativePoint(
            self.chatBalloon.textNodePath,
            ((left+right) / 2.0, 0, (bottom+top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)
예제 #9
0
    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(
                background[0], background[1], background[2],
                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(
            model, modelWidth, modelHeight, self.chatTextNode,
            foreground=foreground, background=background,
            reversed=self.chatReversed,
            button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)
예제 #10
0
class Nametag3d(Nametag, Clickable3d):
    SCALING_MIN_DISTANCE = 1
    SCALING_MAX_DISTANCE = 200
    SCALING_FACTOR = 0.064

    def __init__(self):
        Nametag.__init__(self)
        Clickable3d.__init__(self, 'Nametag3d')

        self.distance = 0

        self.billboardOffset = 3
        self.doBillboardEffect()

    def destroy(self):
        self.ignoreAll()

        Nametag.destroy(self)
        Clickable3d.destroy(self)

    def getUniqueName(self):
        return 'Nametag3d-' + str(id(self))

    def getChatBalloonModel(self):
        return NametagGlobals.chatBalloon3dModel

    def getChatBalloonWidth(self):
        return NametagGlobals.chatBalloon3dWidth

    def getChatBalloonHeight(self):
        return NametagGlobals.chatBalloon3dHeight

    def setBillboardOffset(self, billboardOffset):
        self.billboardOffset = billboardOffset
        self.doBillboardEffect()

    def getBillboardOffset(self):
        return self.billboardOffset

    def doBillboardEffect(self):
        billboardEffect = BillboardEffect.make(
            Vec3(0, 0, 1), True, False, self.billboardOffset, base.cam,
            Point3(0, 0, 0))
        self.contents.setEffect(billboardEffect)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            left = self.chatBalloon.center[0] - (self.chatBalloon.width/2)
            right = left + self.chatBalloon.width

            # Calculate the bottom of the region based on constants.
            # 2.4 is the padded height of a single-line message:
            bottom = NametagGlobals.chatBalloon3dHeight - 2.4
            top = bottom + self.chatBalloon.height

            self.setClickRegionFrame(left, right, bottom, top)
        elif self.panel is not None:
            centerX = (self.textNode.getLeft()+self.textNode.getRight()) / 2.0
            centerY = (self.textNode.getBottom()+self.textNode.getTop()) / 2.0

            left = centerX - (self.panelWidth/2.0)
            right = centerX + (self.panelWidth/2.0)
            bottom = centerY - (self.panelHeight/2.0)
            top = centerY + (self.panelHeight/2.0)

            self.setClickRegionFrame(left, right, bottom, top)

    def isClickable(self):
        if self.getChatText() and self.hasChatButton():
            return True
        return NametagGlobals.wantActiveNametags and Clickable3d.isClickable(self)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)

        Clickable3d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and (self.lastClickState != PGButton.SDepressed):
            base.playSfx(NametagGlobals.rolloverSound)

    def update(self):
        self.contents.node().removeAllChildren()

        Nametag.update(self)

    def tick(self, task):
        distance = self.contents.getPos(base.cam).length()

        extraScale = 1.0
        if distance < self.SCALING_MIN_DISTANCE:
            distance = self.SCALING_MIN_DISTANCE
        elif distance > self.SCALING_MAX_DISTANCE:
            extraScale = 1.5
            distance = self.SCALING_MAX_DISTANCE

        if distance != self.distance:
            self.contents.setScale(math.sqrt(distance) * self.SCALING_FACTOR * extraScale)
            self.distance = distance

        self.updateClickRegion()

        return Task.cont

    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(
                background[0], background[1], background[2],
                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(
            model, modelWidth, modelHeight, self.chatTextNode,
            foreground=foreground, background=background,
            reversed=self.chatReversed,
            button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)

    def drawNametag(self):
        if self.font is None:
            # We can't draw this without a font.
            return

        # Attach the icon:
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)

        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]

        # Set the color of the TextNode:
        self.textNode.setTextColor(foreground)

        # Attach the TextNode:
        textNodePath = self.contents.attachNewNode(self.textNode, 1)
        textNodePath.setTransparency(foreground[3] < 1)
        textNodePath.setAttrib(DepthWriteAttrib.make(0))
        textNodePath.setY(self.TEXT_Y_OFFSET)

        # Attach a panel behind the TextNode:
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)

        # Reposition the panel:
        x = (self.textNode.getLeft()+self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom()+self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)

        # Resize the panel:
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)
예제 #11
0
class WhisperPopup(Clickable2d, MarginVisible):
    CONTENTS_SCALE = 0.25

    TEXT_MAX_ROWS = 6
    TEXT_WORD_WRAP = 8

    QUIT_BUTTON_SHIFT = (0.42, 0, 0.42)

    WHISPER_TIMEOUT_MIN = 10
    WHISPER_TIMEOUT_MAX = 20

    def __init__(self, text, font, whisperType, timeout=30):
        Clickable2d.__init__(self, 'WhisperPopup')
        MarginVisible.__init__(self)

        self.text = text
        self.font = font
        self.whisperType = whisperType
        if timeout is None:
            self.timeout = len(text) * 0.33
            if self.timeout < self.WHISPER_TIMEOUT_MIN:
                self.timeout = self.WHISPER_TIMEOUT_MIN
            elif self.timeout > self.WHISPER_TIMEOUT_MAX:
                self.timeout = self.WHISPER_TIMEOUT_MAX
        else:
            self.timeout = timeout

        self.active = False

        self.senderName = ''
        self.fromId = 0
        self.isPlayer = 0

        self.contents.setScale(self.CONTENTS_SCALE)

        self.whisperColor = ChatGlobals.WhisperColors[self.whisperType]

        self.textNode = TextNode('text')
        self.textNode.setWordwrap(self.TEXT_WORD_WRAP)
        self.textNode.setTextColor(self.whisperColor[PGButton.SInactive][0])
        self.textNode.setFont(self.font)
        self.textNode.setText(self.text)

        self.chatBalloon = None
        self.quitButton = None

        self.timeoutTaskName = self.getUniqueName() + '-timeout'
        self.timeoutTask = None

        self.quitEvent = self.getUniqueName() + '-quit'
        self.accept(self.quitEvent, self.destroy)

        self.setPriority(MarginGlobals.MP_high)
        self.setVisible(True)

        self.update()

        self.accept('MarginVisible-update', self.update)

    def destroy(self):
        self.ignoreAll()

        if self.timeoutTask is not None:
            taskMgr.remove(self.timeoutTask)
            self.timeoutTask = None

        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.textNode = None

        Clickable2d.destroy(self)

    def getUniqueName(self):
        return 'WhisperPopup-' + str(id(self))

    def update(self):
        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.contents.node().removeAllChildren()

        self.draw()

        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def draw(self):
        if self.isClickable():
            foreground, background = self.whisperColor[self.clickState]
        else:
            foreground, background = self.whisperColor[PGButton.SInactive]
        self.chatBalloon = ChatBalloon(NametagGlobals.chatBalloon2dModel,
                                       NametagGlobals.chatBalloon2dWidth,
                                       NametagGlobals.chatBalloon2dHeight,
                                       self.textNode,
                                       foreground=foreground,
                                       background=background)
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.textNode.getFrameActual()
        center = self.contents.getRelativePoint(self.chatBalloon.textNodePath,
                                                ((left + right) / 2.0, 0,
                                                 (bottom + top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)

        # Draw the quit button:
        self.quitButton = WhisperQuitButton(self)
        quitButtonNodePath = self.contents.attachNewNode(self.quitButton)

        # Move the quit button to the top right of the TextNode:
        quitButtonNodePath.setPos(
            self.contents.getRelativePoint(self.chatBalloon.textNodePath,
                                           (right, 0, top)))

        # Apply the quit button shift:
        quitButtonNodePath.setPos(quitButtonNodePath, self.QUIT_BUTTON_SHIFT)

        # Allow the quit button to close this whisper:
        self.quitButton.setClickEvent(self.quitEvent)

    def manage(self, marginManager):
        MarginVisible.manage(self, marginManager)

        self.timeoutTask = taskMgr.doMethodLater(self.timeout, self.unmanage,
                                                 self.timeoutTaskName,
                                                 [marginManager])

    def unmanage(self, marginManager):
        MarginVisible.unmanage(self, marginManager)

        self.destroy()

    def setClickable(self, senderName, fromId, isPlayer=0):
        self.senderName = senderName
        self.fromId = fromId
        self.isPlayer = isPlayer
        self.setClickEvent('clickedWhisper', extraArgs=[fromId, isPlayer])
        self.setActive(True)

    def applyClickState(self, clickState):
        if self.chatBalloon is not None:
            foreground, background = self.whisperColor[clickState]
            self.chatBalloon.setForeground(foreground)
            self.chatBalloon.setBackground(background)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)

        if self.isHovering() or self.quitButton.isHovering():
            self.quitButton.contents.show()
        elif self.quitButton.getClickState() == PGButton.SDepressed:
            self.quitButton.contents.show()
        else:
            self.quitButton.contents.hide()

        Clickable2d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and (self.lastClickState != PGButton.SDepressed):
            base.playSfx(NametagGlobals.rolloverSound)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            right = self.chatBalloon.width / 2.0
            left = -right
            top = self.chatBalloon.height / 2.0
            bottom = -top

            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        else:
            if self.region is not None:
                self.region.setActive(False)

        if self.quitButton is not None:
            self.quitButton.updateClickRegion()

    def marginVisibilityChanged(self):
        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def reposition(self):
        if self.contents is None:
            return

        origin = Point3()

        self.contents.setPos(origin)

        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.contents.node().removeAllChildren()

        if (self.cell in base.leftCells) or (self.cell in base.rightCells):
            text = self.text.replace('\x01WLDisplay\x01',
                                     '').replace('\x02', '')
            textWidth = self.textNode.calcWidth(text)
            if (textWidth / self.TEXT_WORD_WRAP) > self.TEXT_MAX_ROWS:
                self.textNode.setWordwrap(textWidth /
                                          (self.TEXT_MAX_ROWS - 0.5))
        else:
            self.textNode.setWordwrap(self.TEXT_WORD_WRAP)

        self.draw()

        left, right, bottom, top = self.textNode.getFrameActual()
        left -= self.chatBalloon.BALLOON_X_PADDING
        right += self.chatBalloon.BALLOON_X_PADDING
        bottom -= self.chatBalloon.BALLOON_Z_PADDING
        top += self.chatBalloon.BALLOON_Z_PADDING

        if self.cell in base.bottomCells:
            # Move the origin to the bottom center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath,
                ((left + right) / 2.0, 0, bottom))
        elif self.cell in base.leftCells:
            # Move the origin to the left center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath, (left, 0, (bottom + top) / 2.0))
        elif self.cell in base.rightCells:
            # Move the origin to the right center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath,
                (right, 0, (bottom + top) / 2.0))

        self.contents.setPos(self.contents, -origin)
예제 #12
0
class Nametag2d(Nametag, Clickable2d, MarginVisible):
    CONTENTS_SCALE = 0.25
    CHAT_TEXT_MAX_ROWS = 6
    CHAT_TEXT_WORD_WRAP = 8
    CHAT_BALLOON_ALPHA = 0.4
    ARROW_OFFSET = -1.0
    ARROW_SCALE = 1.5

    def __init__(self):
        Nametag.__init__(self)
        Clickable2d.__init__(self, 'Nametag2d')
        MarginVisible.__init__(self)
        self.actualChatText = ''
        self.arrow = None
        self.textNodePath = None
        self.contents.setScale(self.CONTENTS_SCALE)
        self.hideThought()
        self.accept('MarginVisible-update', self.update)

    def destroy(self):
        self.ignoreAll()
        Nametag.destroy(self)
        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None
        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None
        Clickable2d.destroy(self)

    def getUniqueName(self):
        return 'Nametag2d-%s' % id(self)

    def getChatBalloonModel(self):
        return NametagGlobals.chatBalloon2dModel

    def getChatBalloonWidth(self):
        return NametagGlobals.chatBalloon2dWidth

    def getChatBalloonHeight(self):
        return NametagGlobals.chatBalloon2dHeight

    def setChatText(self, chatText):
        self.actualChatText = chatText
        Nametag.setChatText(self, chatText)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            right = self.chatBalloon.width / 2.0
            left = -right
            top = self.chatBalloon.height / 2.0
            bottom = -top
            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        elif self.panel is not None:
            centerX = (self.textNode.getLeft() + self.textNode.getRight()) / 2.0
            centerY = (self.textNode.getBottom() + self.textNode.getTop()) / 2.0
            left = centerX - self.panelWidth / 2.0
            right = centerX + self.panelWidth / 2.0
            bottom = centerY - self.panelHeight / 2.0
            top = centerY + self.panelHeight / 2.0
            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        elif self.region is not None:
            self.region.setActive(False)

    def isClickable(self):
        if self.getChatText() and self.hasChatButton():
            return True
        return NametagGlobals.wantActiveNametags and Clickable2d.isClickable(self)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)
        Clickable2d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and self.lastClickState != PGButton.SDepressed:
            base.playSfx(NametagGlobals.rolloverSound)

    def update(self):
        self.contents.node().removeAllChildren()
        Nametag.update(self)
        if self.cell is not None:
            self.reposition()
            self.updateClickRegion()
        elif self.region is not None:
            self.region.setActive(False)

    def tick(self, task):
        if self.avatar is None or self.avatar.isEmpty():
            return Task.cont
        if self.cell is None or self.arrow is None:
            return Task.cont
        location = self.avatar.getPos(NametagGlobals.me)
        rotation = NametagGlobals.me.getQuat(base.cam)
        camSpacePos = rotation.xform(location)
        arrowRadians = math.atan2(camSpacePos[0], camSpacePos[1])
        arrowDegrees = arrowRadians / math.pi * 180
        self.arrow.setR(arrowDegrees - 90)
        return Task.cont

    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            return
        self.chatTextNode.setText(self.getText() + ': ' + self.actualChatText)
        self.setPriority(MarginGlobals.MP_normal)
        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None
        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None
        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(background[0], background[1], background[2], self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(model, modelWidth, modelHeight, self.chatTextNode, foreground=foreground, background=background, reversed=self.chatReversed, button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)
        left, right, bottom, top = self.chatTextNode.getFrameActual()
        center = self.contents.getRelativePoint(self.chatBalloon.textNodePath, ((left + right) / 2.0, 0, (bottom + top) / 2.0))
        self.chatBalloon.setPos(self.chatBalloon, -center)

    def drawNametag(self):
        self.setPriority(MarginGlobals.MP_low)
        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None
        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None
        if self.font is None:
            return
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)
        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]
        self.textNode.setTextColor(foreground)
        self.textNodePath = self.contents.attachNewNode(self.textNode, 1)
        self.textNodePath.setTransparency(foreground[3] < 1)
        self.textNodePath.setAttrib(DepthWriteAttrib.make(0))
        self.textNodePath.setY(self.TEXT_Y_OFFSET)
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)
        x = (self.textNode.getLeft() + self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom() + self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)
        self.arrow = NametagGlobals.arrowModel.copyTo(self.contents)
        self.arrow.setZ(self.ARROW_OFFSET + self.textNode.getBottom())
        self.arrow.setScale(self.ARROW_SCALE)
        self.arrow.setColor(self.nametagColor[4])

    def marginVisibilityChanged(self):
        if self.cell is not None:
            self.reposition()
            self.updateClickRegion()
        elif self.region is not None:
            self.region.setActive(False)

    def reposition(self):
        if self.contents is None:
            return
        origin = Point3()
        self.contents.setPos(origin)
        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None
            self.contents.node().removeAllChildren()
            if self.cell in base.leftCells or self.cell in base.rightCells:
                text = self.getChatText().replace('\x01WLDisplay\x01', '').replace('\x02', '')
                textWidth = self.chatTextNode.calcWidth(text)
                if textWidth / self.CHAT_TEXT_WORD_WRAP > self.CHAT_TEXT_MAX_ROWS:
                    self.chatTextNode.setWordwrap(textWidth / (self.CHAT_TEXT_MAX_ROWS - 0.5))
            else:
                self.chatTextNode.setWordwrap(self.CHAT_TEXT_WORD_WRAP)
            model = self.getChatBalloonModel()
            modelWidth = self.getChatBalloonWidth()
            modelHeight = self.getChatBalloonHeight()
            self.drawChatBalloon(model, modelWidth, modelHeight)
            nodePath = self.chatBalloon.textNodePath
            left, right, bottom, top = self.chatTextNode.getFrameActual()
        elif self.panel is not None:
            nodePath = self.textNodePath
            left, right, bottom, top = self.textNode.getFrameActual()
            bottom -= self.ARROW_SCALE
        else:
            return
        if self.cell in base.bottomCells:
            origin = self.contents.getRelativePoint(nodePath, ((left + right) / 2.0, 0, bottom))
        elif self.cell in base.leftCells:
            origin = self.contents.getRelativePoint(nodePath, (left, 0, (bottom + top) / 2.0))
        elif self.cell in base.rightCells:
            origin = self.contents.getRelativePoint(nodePath, (right, 0, (bottom + top) / 2.0))
        self.contents.setPos(self.contents, -origin)
예제 #13
0
class WhisperPopup(Clickable2d, MarginVisible):
    CONTENTS_SCALE = 0.25
    TEXT_MAX_ROWS = 6
    TEXT_WORD_WRAP = 8
    QUIT_BUTTON_SHIFT = (-0.42, 0, -0.42)
    WHISPER_TIMEOUT_MIN = 10
    WHISPER_TIMEOUT_MAX = 20

    def __init__(self, text, font, whisperType, timeout = None):
        Clickable2d.__init__(self, 'WhisperPopup')
        MarginVisible.__init__(self)
        self.text = text
        self.font = font
        self.whisperType = whisperType
        if timeout is None:
            self.timeout = len(text) * 0.33
            if self.timeout < self.WHISPER_TIMEOUT_MIN:
                self.timeout = self.WHISPER_TIMEOUT_MIN
            elif self.timeout > self.WHISPER_TIMEOUT_MAX:
                self.timeout = self.WHISPER_TIMEOUT_MAX
        else:
            self.timeout = timeout
        self.active = False
        self.senderName = ''
        self.fromId = 0
        self.isPlayer = 0
        self.contents.setScale(self.CONTENTS_SCALE)
        self.whisperColor = ChatGlobals.WhisperColors[self.whisperType]
        self.textNode = TextNode('text')
        self.textNode.setWordwrap(self.TEXT_WORD_WRAP)
        self.textNode.setTextColor(self.whisperColor[PGButton.SInactive][0])
        self.textNode.setFont(self.font)
        self.textNode.setText(self.text)
        self.chatBalloon = None
        self.quitButton = None
        self.timeoutTaskName = self.getUniqueName() + '-timeout'
        self.timeoutTask = None
        self.quitEvent = self.getUniqueName() + '-quit'
        self.accept(self.quitEvent, self.destroyAnimation)
        self.setPriority(MarginGlobals.MP_high)
        self.setVisible(True)
        self.update()
        self.accept('MarginVisible-update', self.update)

    def destroyAnimation(self):
        self.ignoreAll()
        LerpColorScaleInterval(self.contents, 0.15, VBase4(1, 1, 1, 0), VBase4(1, 1, 1, 1)).start()
        Sequence(self.contents.scaleInterval(0.15, 0, blendType='easeInOut'), Func(self.destroy)).start()

    def destroy(self):
        self.ignoreAll()
        if self.timeoutTask is not None:
            taskMgr.remove(self.timeoutTask)
            self.timeoutTask = None
        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None
        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None
        self.textNode = None
        Clickable2d.destroy(self)

    def getUniqueName(self):
        return 'WhisperPopup-' + str(id(self))

    def update(self):
        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None
        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None
        self.contents.node().removeAllChildren()
        self.draw()
        if self.cell is not None:
            self.reposition()
            self.updateClickRegion()
        elif self.region is not None:
            self.region.setActive(False)

    def draw(self):
        if self.isClickable():
            foreground, background = self.whisperColor[self.clickState]
        else:
            foreground, background = self.whisperColor[PGButton.SInactive]
        self.chatBalloon = ChatBalloon(NametagGlobals.chatBalloon2dModel, NametagGlobals.chatBalloon2dWidth, NametagGlobals.chatBalloon2dHeight, self.textNode, foreground=foreground, background=background)
        self.chatBalloon.reparentTo(self.contents)
        left, right, bottom, top = self.textNode.getFrameActual()
        center = self.contents.getRelativePoint(self.chatBalloon.textNodePath, ((left + right) / 2.0, 0, (bottom + top) / 2.0))
        self.chatBalloon.setPos(self.chatBalloon, -center)
        self.quitButton = WhisperQuitButton(self)
        quitButtonNodePath = self.contents.attachNewNode(self.quitButton)
        quitButtonNodePath.setPos(self.contents.getRelativePoint(self.chatBalloon.textNodePath, (right, 0, top)))
        quitButtonNodePath.setPos(quitButtonNodePath, self.QUIT_BUTTON_SHIFT)
        self.quitButton.setClickEvent(self.quitEvent)
        Parallel(LerpColorScaleInterval(self.contents, 0.2, VBase4(1, 1, 1, 1), VBase4(1, 1, 1, 0)), Sequence(LerpScaleInterval(self.contents, 0.2, self.CONTENTS_SCALE + 0.01, 0), LerpScaleInterval(self.contents, 0.09, self.CONTENTS_SCALE))).start()

    def manage(self, marginManager):
        MarginVisible.manage(self, marginManager)
        self.timeoutTask = taskMgr.doMethodLater(self.timeout, self.unmanage, self.timeoutTaskName, [marginManager])

    def unmanage(self, marginManager):
        MarginVisible.unmanage(self, marginManager)
        self.destroyAnimation()

    def setClickable(self, senderName, fromId, isPlayer = 0):
        self.senderName = senderName
        self.fromId = fromId
        self.isPlayer = isPlayer
        self.setClickEvent('clickedWhisper', extraArgs=[fromId, isPlayer])
        self.setActive(True)

    def applyClickState(self, clickState):
        if self.chatBalloon is not None:
            foreground, background = self.whisperColor[clickState]
            self.chatBalloon.setForeground(foreground)
            self.chatBalloon.setBackground(background)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)
        if self.isHovering() or self.quitButton.isHovering():
            self.quitButton.contents.show()
        elif self.quitButton.getClickState() == PGButton.SDepressed:
            self.quitButton.contents.show()
        else:
            self.quitButton.contents.hide()
        Clickable2d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and self.lastClickState != PGButton.SDepressed:
            base.playSfx(NametagGlobals.rolloverSound)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            right = self.chatBalloon.width / 2.0
            left = -right
            top = self.chatBalloon.height / 2.0
            bottom = -top
            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        elif self.region is not None:
            self.region.setActive(False)
        if self.quitButton is not None:
            self.quitButton.updateClickRegion()

    def marginVisibilityChanged(self):
        if self.cell is not None:
            self.reposition()
            self.updateClickRegion()
        elif self.region is not None:
            self.region.setActive(False)

    def reposition(self):
        if self.contents is None:
            return
        origin = Point3()
        self.contents.setPos(origin)
        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None
        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None
        self.contents.node().removeAllChildren()
        if self.cell in base.leftCells or self.cell in base.rightCells:
            text = self.text.replace('\x01WLDisplay\x01', '').replace('\x02', '')
            textWidth = self.textNode.calcWidth(text)
            if textWidth / self.TEXT_WORD_WRAP > self.TEXT_MAX_ROWS:
                self.textNode.setWordwrap(textWidth / (self.TEXT_MAX_ROWS - 0.5))
        else:
            self.textNode.setWordwrap(self.TEXT_WORD_WRAP)
        self.draw()
        left, right, bottom, top = self.textNode.getFrameActual()
        if self.cell in base.bottomCells:
            origin = self.contents.getRelativePoint(self.chatBalloon.textNodePath, ((left + right) / 2.0, 0, bottom))
        elif self.cell in base.leftCells:
            origin = self.contents.getRelativePoint(self.chatBalloon.textNodePath, (left, 0, (bottom + top) / 2.0))
        elif self.cell in base.rightCells:
            origin = self.contents.getRelativePoint(self.chatBalloon.textNodePath, (right, 0, (bottom + top) / 2.0))
        self.contents.setPos(self.contents, -origin)
예제 #14
0
class Nametag2d(Nametag, Clickable2d, MarginVisible):
    CONTENTS_SCALE = 0.25

    CHAT_TEXT_MAX_ROWS = 6
    CHAT_TEXT_WORD_WRAP = 8

    CHAT_BALLOON_ALPHA = 0.4

    ARROW_OFFSET = -1.0
    ARROW_SCALE = 1.5

    def __init__(self):
        Nametag.__init__(self)
        Clickable2d.__init__(self, 'Nametag2d')
        MarginVisible.__init__(self)

        self.actualChatText = ''

        self.arrow = None
        self.textNodePath = None

        self.contents.setScale(self.CONTENTS_SCALE)
        self.hideThought()

        self.accept('MarginVisible-update', self.update)

    def destroy(self):
        self.ignoreAll()

        Nametag.destroy(self)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        Clickable2d.destroy(self)

    def getUniqueName(self):
        return 'Nametag2d-' + str(id(self))

    def getChatBalloonModel(self):
        return NametagGlobals.chatBalloon2dModel

    def getChatBalloonWidth(self):
        return NametagGlobals.chatBalloon2dWidth

    def getChatBalloonHeight(self):
        return NametagGlobals.chatBalloon2dHeight

    def setChatText(self, chatText):
        self.actualChatText = chatText

        Nametag.setChatText(self, chatText)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            right = self.chatBalloon.width / 2.0
            left = -right
            top = self.chatBalloon.height / 2.0
            bottom = -top

            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        elif self.panel is not None:
            centerX = (self.textNode.getLeft() +
                       self.textNode.getRight()) / 2.0
            centerY = (self.textNode.getBottom() +
                       self.textNode.getTop()) / 2.0

            left = centerX - (self.panelWidth / 2.0)
            right = centerX + (self.panelWidth / 2.0)
            bottom = centerY - (self.panelHeight / 2.0)
            top = centerY + (self.panelHeight / 2.0)

            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        else:
            if self.region is not None:
                self.region.setActive(False)

    def isClickable(self):
        if self.getChatText() and self.hasChatButton():
            return True
        return NametagGlobals.wantActiveNametags and Clickable2d.isClickable(
            self)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)

        Clickable2d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and (self.lastClickState != PGButton.SDepressed):
            base.playSfx(NametagGlobals.rolloverSound)

    def update(self):
        self.contents.node().removeAllChildren()

        Nametag.update(self)

        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def tick(self, task):
        if (self.avatar is None) or self.avatar.isEmpty():
            return Task.cont

        if (self.cell is None) or (self.arrow is None):
            return Task.cont

        location = self.avatar.getPos(NametagGlobals.me)
        rotation = NametagGlobals.me.getQuat(base.cam)
        camSpacePos = rotation.xform(location)

        arrowRadians = math.atan2(camSpacePos[0], camSpacePos[1])
        arrowDegrees = (arrowRadians / math.pi) * 180
        self.arrow.setR(arrowDegrees - 90)

        return Task.cont

    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        # Prefix the nametag text:
        self.chatTextNode.setText(self.getText() + ': ' + self.actualChatText)

        # Set our priority in the margin system:
        self.setPriority(MarginGlobals.MP_normal)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(background[0], background[1], background[2],
                                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(model,
                                       modelWidth,
                                       modelHeight,
                                       self.chatTextNode,
                                       foreground=foreground,
                                       background=background,
                                       reversed=self.chatReversed,
                                       button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.chatTextNode.getFrameActual()
        center = self.contents.getRelativePoint(self.chatBalloon.textNodePath,
                                                ((left + right) / 2.0, 0,
                                                 (bottom + top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)

    def drawNametag(self):
        # Set our priority in the margin system:
        self.setPriority(MarginGlobals.MP_low)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        if self.font is None:
            # We can't draw this without a font.
            return

        # Attach the icon:
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)

        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]

        # Set the color of the TextNode:
        self.textNode.setTextColor(foreground)

        # Attach the TextNode:
        self.textNodePath = self.contents.attachNewNode(self.textNode, 1)
        self.textNodePath.setTransparency(foreground[3] < 1)
        self.textNodePath.setAttrib(DepthWriteAttrib.make(0))
        self.textNodePath.setY(self.TEXT_Y_OFFSET)

        # Attach a panel behind the TextNode:
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)

        # Reposition the panel:
        x = (self.textNode.getLeft() + self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom() + self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)

        # Resize the panel:
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)

        # Add an arrow:
        self.arrow = NametagGlobals.arrowModel.copyTo(self.contents)
        self.arrow.setZ(self.ARROW_OFFSET + self.textNode.getBottom())
        self.arrow.setScale(self.ARROW_SCALE)
        self.arrow.setColor(self.nametagColor[4] if len(self.nametagColor) >= 5
                            else self.nametagColor[0][0])

    def marginVisibilityChanged(self):
        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def reposition(self):
        if self.contents is None:
            return

        origin = Point3()

        self.contents.setPos(origin)

        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

            self.contents.node().removeAllChildren()

            if (self.cell in base.leftCells) or (self.cell in base.rightCells):
                text = self.getChatText().replace('\x01WLDisplay\x01',
                                                  '').replace('\x02', '')
                textWidth = self.chatTextNode.calcWidth(text)
                if (textWidth /
                        self.CHAT_TEXT_WORD_WRAP) > self.CHAT_TEXT_MAX_ROWS:
                    self.chatTextNode.setWordwrap(
                        textWidth / (self.CHAT_TEXT_MAX_ROWS - 0.5))
            else:
                self.chatTextNode.setWordwrap(self.CHAT_TEXT_WORD_WRAP)

            model = self.getChatBalloonModel()
            modelWidth = self.getChatBalloonWidth()
            modelHeight = self.getChatBalloonHeight()
            self.drawChatBalloon(model, modelWidth, modelHeight)

            nodePath = self.chatBalloon.textNodePath

            left, right, bottom, top = self.chatTextNode.getFrameActual()
            left -= self.chatBalloon.BALLOON_X_PADDING
            right += self.chatBalloon.BALLOON_X_PADDING
            bottom -= self.chatBalloon.BALLOON_Z_PADDING
            top += self.chatBalloon.BALLOON_Z_PADDING
        elif self.panel is not None:
            nodePath = self.textNodePath

            left, right, bottom, top = self.textNode.getFrameActual()
            left -= self.PANEL_X_PADDING
            right += self.PANEL_X_PADDING
            bottom -= self.PANEL_Z_PADDING
            top += self.PANEL_Z_PADDING

            # Compensate for the arrow:
            bottom -= self.ARROW_SCALE
        else:
            return

        if self.cell in base.bottomCells:
            # Move the origin to the bottom center of the node path:
            origin = self.contents.getRelativePoint(
                nodePath, ((left + right) / 2.0, 0, bottom))
        elif self.cell in base.leftCells:
            # Move the origin to the left center of the node path:
            origin = self.contents.getRelativePoint(nodePath,
                                                    (left, 0,
                                                     (bottom + top) / 2.0))
        elif self.cell in base.rightCells:
            # Move the origin to the right center of the node path:
            origin = self.contents.getRelativePoint(nodePath,
                                                    (right, 0,
                                                     (bottom + top) / 2.0))

        self.contents.setPos(self.contents, -origin)
예제 #15
0
class Nametag3d(Nametag, Clickable3d):
    SCALING_MIN_DISTANCE = 1
    SCALING_MAX_DISTANCE = 200
    SCALING_FACTOR = 0.085

    def __init__(self):
        Nametag.__init__(self)
        Clickable3d.__init__(self, 'Nametag3d')

        self.distance = 0

        self.billboardOffset = 3
        self.doBillboardEffect()

    def destroy(self):
        self.ignoreAll()

        Nametag.destroy(self)
        Clickable3d.destroy(self)

    def getUniqueName(self):
        return 'Nametag3d-' + str(id(self))

    def getChatBalloonModel(self):
        return NametagGlobals.chatBalloon3dModel

    def getChatBalloonWidth(self):
        return NametagGlobals.chatBalloon3dWidth

    def getChatBalloonHeight(self):
        return NametagGlobals.chatBalloon3dHeight

    def setBillboardOffset(self, billboardOffset):
        self.billboardOffset = billboardOffset
        self.doBillboardEffect()

    def getBillboardOffset(self):
        return self.billboardOffset

    def doBillboardEffect(self):
        billboardEffect = BillboardEffect.make(
            Vec3(0, 0, 1), True, False, self.billboardOffset, base.cam,
            Point3(0, 0, 0))
        self.contents.setEffect(billboardEffect)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            left = self.chatBalloon.center[0] - (self.chatBalloon.width/2)
            right = left + self.chatBalloon.width

            # Calculate the bottom of the region based on constants.
            # 2.4 is the padded height of a single-line message:
            bottom = NametagGlobals.chatBalloon3dHeight - 2.4
            top = bottom + self.chatBalloon.height

            self.setClickRegionFrame(left, right, bottom, top)
        elif self.panel is not None:
            centerX = (self.textNode.getLeft()+self.textNode.getRight()) / 2.0
            centerY = (self.textNode.getBottom()+self.textNode.getTop()) / 2.0

            left = centerX - (self.panelWidth/2.0)
            right = centerX + (self.panelWidth/2.0)
            bottom = centerY - (self.panelHeight/2.0)
            top = centerY + (self.panelHeight/2.0)

            self.setClickRegionFrame(left, right, bottom, top)

    def isClickable(self):
        if self.getChatText() and self.hasChatButton():
            return True
        return NametagGlobals.wantActiveNametags and Clickable3d.isClickable(self)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)

        Clickable3d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and (self.lastClickState != PGButton.SDepressed):
            base.playSfx(NametagGlobals.rolloverSound)

    def update(self):
        self.contents.node().removeAllChildren()

        Nametag.update(self)

    def tick(self, task):
        distance = self.contents.getPos(base.cam).length()

        extraScale = 1.0
        if distance < self.SCALING_MIN_DISTANCE:
            distance = self.SCALING_MIN_DISTANCE
        elif distance > self.SCALING_MAX_DISTANCE:
            extraScale = 1.5
            distance = self.SCALING_MAX_DISTANCE

        if distance != self.distance:
            self.contents.setScale(math.sqrt(distance) * self.SCALING_FACTOR * extraScale)
            self.distance = distance

        self.updateClickRegion()

        return Task.cont

    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(
                background[0], background[1], background[2],
                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(
            model, modelWidth, modelHeight, self.chatTextNode,
            foreground=foreground, background=background,
            reversed=self.chatReversed,
            button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)

    def drawNametag(self):
        if self.font is None:
            # We can't draw this without a font.
            return

        # Attach the icon:
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)

        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]

        # Set the color of the TextNode:
        self.textNode.setTextColor(foreground)

        # Attach the TextNode:
        textNodePath = self.contents.attachNewNode(self.textNode, 1)
        textNodePath.setTransparency(foreground[3] < 1)
        textNodePath.setAttrib(DepthWriteAttrib.make(0))
        textNodePath.setY(self.TEXT_Y_OFFSET)

        # Attach a panel behind the TextNode:
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)

        # Reposition the panel:
        x = (self.textNode.getLeft()+self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom()+self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)

        # Resize the panel:
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)
class Nametag2d(Nametag, Clickable2d, MarginVisible):
    CONTENTS_SCALE = 0.25

    CHAT_TEXT_WORD_WRAP = 8

    CHAT_BALLOON_ALPHA = 0.4

    ARROW_OFFSET = -1.0
    ARROW_SCALE = 1.5

    def __init__(self):
        Nametag.__init__(self)
        Clickable2d.__init__(self, 'Nametag2d')
        MarginVisible.__init__(self)

        self.actualChatText = ''

        self.arrow = None
        self.textNodePath = None

        self.contents.setScale(self.CONTENTS_SCALE)
        self.hideThought()

        self.accept('MarginVisible-update', self.update)

    def destroy(self):
        self.ignoreAll()

        Nametag.destroy(self)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        Clickable2d.destroy(self)

    def getUniqueName(self):
        return 'Nametag2d-' + str(id(self))

    def getChatBalloonModel(self):
        return NametagGlobals.chatBalloon2dModel

    def getChatBalloonWidth(self):
        return NametagGlobals.chatBalloon2dWidth

    def getChatBalloonHeight(self):
        return NametagGlobals.chatBalloon2dHeight

    def setChatText(self, chatText):
        self.actualChatText = chatText

        Nametag.setChatText(self, chatText)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            right = self.chatBalloon.width / 2.0
            left = -right
            top = self.chatBalloon.height / 2.0
            bottom = -top

            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        elif self.panel is not None:
            centerX = (self.textNode.getLeft()+self.textNode.getRight()) / 2.0
            centerY = (self.textNode.getBottom()+self.textNode.getTop()) / 2.0

            left = centerX - (self.panelWidth/2.0)
            right = centerX + (self.panelWidth/2.0)
            bottom = centerY - (self.panelHeight/2.0)
            top = centerY + (self.panelHeight/2.0)

            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        else:
            if self.region is not None:
                self.region.setActive(False)

    def isClickable(self):
        if self.getChatText() and self.hasChatButton():
            return True
        return NametagGlobals.wantActiveNametags and Clickable2d.isClickable(self)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)

        Clickable2d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and (self.lastClickState != PGButton.SDepressed):
            base.playSfx(NametagGlobals.rolloverSound)

    def update(self):
        self.contents.node().removeAllChildren()

        Nametag.update(self)

        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def tick(self, task):
        if (self.avatar is None) or self.avatar.isEmpty():
            return Task.cont

        if (self.cell is None) or (self.arrow is None):
            return Task.cont

        location = self.avatar.getPos(NametagGlobals.me)
        rotation = NametagGlobals.me.getQuat(base.cam)
        camSpacePos = rotation.xform(location)

        arrowRadians = math.atan2(camSpacePos[0], camSpacePos[1])
        arrowDegrees = (arrowRadians/math.pi) * 180
        self.arrow.setR(arrowDegrees - 90)

        return Task.cont

    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            # We can't draw this without a font.
            return

        # Prefix the nametag text:
        self.chatTextNode.setText(self.getText() + ': ' + self.actualChatText)

        # Set our priority in the margin system:
        self.setPriority(MarginGlobals.MP_normal)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(
                background[0], background[1], background[2],
                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(
            model, modelWidth, modelHeight, self.chatTextNode,
            foreground=foreground, background=background,
            reversed=self.chatReversed,
            button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.chatTextNode.getFrameActual()
        center = self.contents.getRelativePoint(
            self.chatBalloon.textNodePath,
            ((left+right) / 2.0, 0, (bottom+top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)

    def drawNametag(self):
        # Set our priority in the margin system:
        self.setPriority(MarginGlobals.MP_low)

        if self.textNodePath is not None:
            self.textNodePath.removeNode()
            self.textNodePath = None

        if self.arrow is not None:
            self.arrow.removeNode()
            self.arrow = None

        if self.font is None:
            # We can't draw this without a font.
            return

        # Attach the icon:
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)

        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]

        # Set the color of the TextNode:
        self.textNode.setTextColor(foreground)

        # Attach the TextNode:
        self.textNodePath = self.contents.attachNewNode(self.textNode, 1)
        self.textNodePath.setTransparency(foreground[3] < 1)
        self.textNodePath.setAttrib(DepthWriteAttrib.make(0))
        self.textNodePath.setY(self.TEXT_Y_OFFSET)

        # Attach a panel behind the TextNode:
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)

        # Reposition the panel:
        x = (self.textNode.getLeft()+self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom()+self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)

        # Resize the panel:
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)

        # Add an arrow:
        self.arrow = NametagGlobals.arrowModel.copyTo(self.contents)
        self.arrow.setZ(self.ARROW_OFFSET + self.textNode.getBottom())
        self.arrow.setScale(self.ARROW_SCALE)
        self.arrow.setColor(self.nametagColor[0][0])

    def marginVisibilityChanged(self):
        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def reposition(self):
        if self.contents is None:
            return

        origin = Point3()

        self.contents.setPos(origin)

        if self.chatBalloon is not None:
            nodePath = self.chatBalloon.textNodePath

            left, right, bottom, top = self.chatTextNode.getFrameActual()
        elif self.panel is not None:
            nodePath = self.textNodePath

            left, right, bottom, top = self.textNode.getFrameActual()

            # Compensate for the arrow:
            bottom -= self.ARROW_SCALE
        else:
            return

        if self.cell in base.bottomCells:
            # Move the origin to the bottom center of the node path:
            origin = self.contents.getRelativePoint(
                nodePath, ((left+right) / 2.0, 0, bottom))
        elif self.cell in base.leftCells:
            # Move the origin to the left center of the node path:
            origin = self.contents.getRelativePoint(
                nodePath, (left, 0, (bottom+top) / 2.0))
        elif self.cell in base.rightCells:
            # Move the origin to the right center of the node path:
            origin = self.contents.getRelativePoint(
                nodePath, (right, 0, (bottom+top) / 2.0))

        self.contents.setPos(self.contents, -origin)
예제 #17
0
class WhisperPopup(Clickable2d, MarginVisible):
    CONTENTS_SCALE = 0.25

    TEXT_MAX_ROWS = 6
    TEXT_WORD_WRAP = 8

    QUIT_BUTTON_SHIFT = (0.42, 0, 0.42)

    WHISPER_TIMEOUT_MIN = 10
    WHISPER_TIMEOUT_MAX = 20

    def __init__(self, text, font, whisperType, timeout=None):
        Clickable2d.__init__(self, 'WhisperPopup')
        MarginVisible.__init__(self)

        self.text = text
        self.font = font
        self.whisperType = whisperType
        if timeout is None:
            self.timeout = len(text) * 0.33
            if self.timeout < self.WHISPER_TIMEOUT_MIN:
                self.timeout = self.WHISPER_TIMEOUT_MIN
            elif self.timeout > self.WHISPER_TIMEOUT_MAX:
                self.timeout = self.WHISPER_TIMEOUT_MAX
        else:
            self.timeout = timeout

        self.active = False

        self.senderName = ''
        self.fromId = 0
        self.isPlayer = 0

        self.contents.setScale(self.CONTENTS_SCALE)

        self.whisperColor = ChatGlobals.WhisperColors[self.whisperType]

        self.textNode = TextNode('text')
        self.textNode.setWordwrap(self.TEXT_WORD_WRAP)
        self.textNode.setTextColor(self.whisperColor[PGButton.SInactive][0])
        self.textNode.setFont(self.font)
        self.textNode.setText(self.text)

        self.chatBalloon = None
        self.quitButton = None

        self.timeoutTaskName = self.getUniqueName() + '-timeout'
        self.timeoutTask = None

        self.quitEvent = self.getUniqueName() + '-quit'
        self.accept(self.quitEvent, self.destroy)

        self.setPriority(MarginGlobals.MP_high)
        self.setVisible(True)

        self.update()

        self.accept('MarginVisible-update', self.update)

    def destroy(self):
        self.ignoreAll()

        if self.timeoutTask is not None:
            taskMgr.remove(self.timeoutTask)
            self.timeoutTask = None

        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.textNode = None

        Clickable2d.destroy(self)

    def getUniqueName(self):
        return 'WhisperPopup-' + str(id(self))

    def update(self):
        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.contents.node().removeAllChildren()

        self.draw()

        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def draw(self):
        if self.isClickable():
            foreground, background = self.whisperColor[self.clickState]
        else:
            foreground, background = self.whisperColor[PGButton.SInactive]
        self.chatBalloon = ChatBalloon(
            NametagGlobals.chatBalloon2dModel,
            NametagGlobals.chatBalloon2dWidth,
            NametagGlobals.chatBalloon2dHeight, self.textNode,
            foreground=foreground, background=background
        )
        self.chatBalloon.reparentTo(self.contents)

        # Calculate the center of the TextNode:
        left, right, bottom, top = self.textNode.getFrameActual()
        center = self.contents.getRelativePoint(
            self.chatBalloon.textNodePath,
            ((left+right) / 2.0, 0, (bottom+top) / 2.0))

        # Translate the chat balloon along the inverse:
        self.chatBalloon.setPos(self.chatBalloon, -center)

        # Draw the quit button:
        self.quitButton = WhisperQuitButton(self)
        quitButtonNodePath = self.contents.attachNewNode(self.quitButton)

        # Move the quit button to the top right of the TextNode:
        quitButtonNodePath.setPos(self.contents.getRelativePoint(
            self.chatBalloon.textNodePath, (right, 0, top)))

        # Apply the quit button shift:
        quitButtonNodePath.setPos(quitButtonNodePath, self.QUIT_BUTTON_SHIFT)

        # Allow the quit button to close this whisper:
        self.quitButton.setClickEvent(self.quitEvent)

    def manage(self, marginManager):
        MarginVisible.manage(self, marginManager)

        self.timeoutTask = taskMgr.doMethodLater(
            self.timeout, self.unmanage, self.timeoutTaskName, [marginManager])

    def unmanage(self, marginManager):
        MarginVisible.unmanage(self, marginManager)

        self.destroy()

    def setClickable(self, senderName, fromId, isPlayer=0):
        self.senderName = senderName
        self.fromId = fromId
        self.isPlayer = isPlayer
        self.setClickEvent('clickedWhisper', extraArgs=[fromId, isPlayer])
        self.setActive(True)

    def applyClickState(self, clickState):
        if self.chatBalloon is not None:
            foreground, background = self.whisperColor[clickState]
            self.chatBalloon.setForeground(foreground)
            self.chatBalloon.setBackground(background)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)

        if self.isHovering() or self.quitButton.isHovering():
            self.quitButton.contents.show()
        elif self.quitButton.getClickState() == PGButton.SDepressed:
            self.quitButton.contents.show()
        else:
            self.quitButton.contents.hide()

        Clickable2d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and (self.lastClickState != PGButton.SDepressed):
            base.playSfx(NametagGlobals.rolloverSound)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            right = self.chatBalloon.width / 2.0
            left = -right
            top = self.chatBalloon.height / 2.0
            bottom = -top

            self.setClickRegionFrame(left, right, bottom, top)
            self.region.setActive(True)
        else:
            if self.region is not None:
                self.region.setActive(False)

        if self.quitButton is not None:
            self.quitButton.updateClickRegion()

    def marginVisibilityChanged(self):
        if self.cell is not None:
            # We're in the margin display. Reposition our content, and update
            # the click region:
            self.reposition()
            self.updateClickRegion()
        else:
            # We aren't in the margin display. Disable the click region if one
            # is present:
            if self.region is not None:
                self.region.setActive(False)

    def reposition(self):
        if self.contents is None:
            return

        origin = Point3()

        self.contents.setPos(origin)

        if self.chatBalloon is not None:
            self.chatBalloon.removeNode()
            self.chatBalloon = None

        if self.quitButton is not None:
            self.quitButton.destroy()
            self.quitButton = None

        self.contents.node().removeAllChildren()

        if (self.cell in base.leftCells) or (self.cell in base.rightCells):
            text = self.text.replace('\x01WLDisplay\x01', '').replace('\x02', '')
            textWidth = self.textNode.calcWidth(text)
            if (textWidth / self.TEXT_WORD_WRAP) > self.TEXT_MAX_ROWS:
                self.textNode.setWordwrap(textWidth / (self.TEXT_MAX_ROWS-0.5))
        else:
            self.textNode.setWordwrap(self.TEXT_WORD_WRAP)

        self.draw()

        left, right, bottom, top = self.textNode.getFrameActual()
        if self.cell in base.bottomCells:
            # Move the origin to the bottom center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath, ((left+right) / 2.0, 0, bottom))
        elif self.cell in base.leftCells:
            # Move the origin to the left center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath, (left, 0, (bottom+top) / 2.0))
        elif self.cell in base.rightCells:
            # Move the origin to the right center of the chat balloon:
            origin = self.contents.getRelativePoint(
                self.chatBalloon.textNodePath, (right, 0, (bottom+top) / 2.0))

        self.contents.setPos(self.contents, -origin)
예제 #18
0
class Nametag3d(Nametag, Clickable3d):
    SCALING_MIN_DISTANCE = 1
    SCALING_MAX_DISTANCE = 200
    SCALING_FACTOR = 0.065

    def __init__(self):
        Nametag.__init__(self)
        Clickable3d.__init__(self, 'Nametag3d', self)
        self.distance = 0
        self.billboardOffset = 3
        self.doBillboardEffect()

    def destroy(self):
        self.ignoreAll()
        Nametag.destroy(self)
        Clickable3d.destroy(self)

    def getUniqueName(self):
        return 'Nametag3d-%s' % id(self)

    def getChatBalloonModel(self):
        return NametagGlobals.chatBalloon3dModel

    def getChatBalloonWidth(self):
        return NametagGlobals.chatBalloon3dWidth

    def getChatBalloonHeight(self):
        return NametagGlobals.chatBalloon3dHeight

    def setBillboardOffset(self, billboardOffset):
        self.billboardOffset = billboardOffset
        self.doBillboardEffect()

    def getBillboardOffset(self):
        return self.billboardOffset

    def setAvatar(self, avatar):
        Nametag.setAvatar(self, avatar)
        Clickable3d.setAvatar(self, avatar)

    def doBillboardEffect(self):
        billboardEffect = BillboardEffect.make(Vec3(0, 0, 1), True, False,
                                               self.billboardOffset, base.cam,
                                               Point3(0, 0, 0))
        self.contents.setEffect(billboardEffect)

    def updateClickRegion(self):
        if self.chatBalloon is not None:
            left = self.chatBalloon.center[0] - self.chatBalloon.width / 2
            right = left + self.chatBalloon.width
            bottom = NametagGlobals.chatBalloon3dHeight - 2.4
            top = bottom + self.chatBalloon.height
            self.setClickRegionFrame(left, right, bottom, top)
        elif self.panel is not None:
            centerX = (self.textNode.getLeft() +
                       self.textNode.getRight()) / 2.0
            centerY = (self.textNode.getBottom() +
                       self.textNode.getTop()) / 2.0
            left = centerX - self.panelWidth / 2.0
            right = centerX + self.panelWidth / 2.0
            bottom = centerY - self.panelHeight / 2.0
            top = centerY + self.panelHeight / 2.0
            self.setClickRegionFrame(left, right, bottom, top)

    def isClickable(self):
        if self.getChatText() and self.hasChatButton():
            return True
        return NametagGlobals.wantActiveNametags and Clickable3d.isClickable(
            self)

    def setClickState(self, clickState):
        if self.isClickable():
            self.applyClickState(clickState)
        else:
            self.applyClickState(PGButton.SInactive)
        Clickable3d.setClickState(self, clickState)

    def enterDepressed(self):
        if self.isClickable():
            base.playSfx(NametagGlobals.clickSound)

    def enterRollover(self):
        if self.isClickable() and self.lastClickState != PGButton.SDepressed:
            base.playSfx(NametagGlobals.rolloverSound)

    def update(self):
        self.contents.node().removeAllChildren()
        Nametag.update(self)

    def tick(self, task):
        distance = self.contents.getPos(base.cam).length()
        if distance < self.SCALING_MIN_DISTANCE:
            distance = self.SCALING_MIN_DISTANCE
        elif distance > self.SCALING_MAX_DISTANCE:
            distance = self.SCALING_MAX_DISTANCE
        if distance != self.distance:
            self.contents.setScale(math.sqrt(distance) * self.SCALING_FACTOR)
            self.distance = distance
        self.updateClickRegion()
        return Task.cont

    def drawChatBalloon(self, model, modelWidth, modelHeight):
        if self.chatFont is None:
            return
        if self.isClickable():
            foreground, background = self.chatColor[self.clickState]
        else:
            foreground, background = self.chatColor[PGButton.SInactive]
        if self.chatType == NametagGlobals.SPEEDCHAT:
            background = self.speedChatColor
        if background[3] > self.CHAT_BALLOON_ALPHA:
            background = VBase4(background[0], background[1], background[2],
                                self.CHAT_BALLOON_ALPHA)
        self.chatBalloon = ChatBalloon(model,
                                       modelWidth,
                                       modelHeight,
                                       self.chatTextNode,
                                       foreground=foreground,
                                       background=background,
                                       reversed=self.chatReversed,
                                       button=self.chatButton[self.clickState])
        self.chatBalloon.reparentTo(self.contents)
        self.chatBalloon.setScale(0, 0, 0)
        Sequence(
            self.chatBalloon.scaleInterval(0.2,
                                           VBase3(1.1, 1.1, 1.1),
                                           blendType='easeInOut'),
            self.chatBalloon.scaleInterval(0.09,
                                           VBase3(1, 1, 1),
                                           blendType='easeInOut')).start()

    def drawNametag(self):
        if self.font is None:
            return
        if self.icon is not None:
            self.contents.attachNewNode(self.icon)
        if self.isClickable():
            foreground, background = self.nametagColor[self.clickState]
        else:
            foreground, background = self.nametagColor[PGButton.SInactive]
        self.textNode.setTextColor(foreground)
        textNodePath = self.contents.attachNewNode(self.textNode, 1)
        textNodePath.setTransparency(foreground[3] < 1)
        textNodePath.setAttrib(DepthWriteAttrib.make(0))
        textNodePath.setY(-0.1)
        self.panel = NametagGlobals.cardModel.copyTo(self.contents, 0)
        self.panel.setColor(background)
        self.panel.setTransparency(background[3] < 1)
        x = (self.textNode.getLeft() + self.textNode.getRight()) / 2.0
        z = (self.textNode.getBottom() + self.textNode.getTop()) / 2.0
        self.panel.setPos(x, 0, z)
        self.panelWidth = self.textNode.getWidth() + self.PANEL_X_PADDING
        self.panelHeight = self.textNode.getHeight() + self.PANEL_Z_PADDING
        self.panel.setScale(self.panelWidth, 1, self.panelHeight)