Пример #1
0
class Nametag:
    TEXT_WORD_WRAP = 8
    TEXT_Y_OFFSET = -0.05

    CHAT_TEXT_WORD_WRAP = 12

    PANEL_X_PADDING = 0.2
    PANEL_Z_PADDING = 0.2

    CHAT_BALLOON_ALPHA = 1

    def __init__(self):
        self.avatar = None

        self.panel = None
        self.icon = None
        self.chatBalloon = None

        self.chatButton = NametagGlobals.noButton
        self.chatReversed = False

        self.font = None
        self.chatFont = None

        self.chatType = NametagGlobals.CHAT
        self.chatBalloonType = NametagGlobals.CHAT_BALLOON

        self.nametagColor = NametagGlobals.NametagColors[
            NametagGlobals.CCOtherPlayer]
        self.chatColor = NametagGlobals.ChatColors[
            NametagGlobals.CCOtherPlayer]
        self.speedChatColor = self.chatColor[0][1]

        self.nametagHidden = False
        self.chatHidden = False
        self.thoughtHidden = False

        # Create our TextNodes:
        self.textNode = TextNode('text')
        self.textNode.setWordwrap(self.TEXT_WORD_WRAP)
        self.textNode.setAlign(TextNode.ACenter)

        self.chatTextNode = TextNode('chatText')
        self.chatTextNode.setWordwrap(self.CHAT_TEXT_WORD_WRAP)
        self.chatTextNode.setGlyphScale(ChatBalloon.TEXT_GLYPH_SCALE)
        self.chatTextNode.setGlyphShift(ChatBalloon.TEXT_GLYPH_SHIFT)

        # Add the tick task:
        self.tickTaskName = self.getUniqueName() + '-tick'
        self.tickTask = taskMgr.add(self.tick, self.tickTaskName, sort=45)

    def destroy(self):
        if self.tickTask is not None:
            taskMgr.remove(self.tickTask)
            self.tickTask = None

        self.chatTextNode = None
        self.textNode = None

        self.chatFont = None
        self.font = None

        self.chatButton = NametagGlobals.noButton

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

        if self.icon is not None:
            self.icon.removeAllChildren()
            self.icon = None

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

        self.avatar = None

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

    def getChatBalloonModel(self):
        pass  # Inheritors should override this method.

    def getChatBalloonWidth(self):
        pass  # Inheritors should override this method.

    def getChatBalloonHeight(self):
        pass  # Inheritors should override this method.

    def tick(self, task):
        return Task.done  # Inheritors should override this method.

    def updateClickRegion(self):
        pass  # Inheritors should override this method.

    def drawChatBalloon(self, model, modelWidth, modelHeight):
        pass  # Inheritors should override this method.

    def drawNametag(self):
        pass  # Inheritors should override this method.

    def setAvatar(self, avatar):
        self.avatar = avatar

    def getAvatar(self):
        return self.avatar

    def setIcon(self, icon):
        self.icon = icon

    def getIcon(self):
        return self.icon

    def setChatButton(self, chatButton):
        self.chatButton = chatButton

    def getChatButton(self):
        return self.chatButton

    def hasChatButton(self):
        if (self.chatBalloonType
                == NametagGlobals.CHAT_BALLOON) and self.chatHidden:
            return False
        if (self.chatBalloonType
                == NametagGlobals.THOUGHT_BALLOON) and self.thoughtHidden:
            return False
        return self.chatButton != NametagGlobals.noButton

    def setChatReversed(self, chatReversed):
        self.chatReversed = chatReversed

    def getChatReversed(self):
        return self.chatReversed

    def setFont(self, font):
        self.font = font
        if self.font is not None:
            self.textNode.setFont(self.font)
        self.update()

    def getFont(self):
        return self.font

    def setChatFont(self, chatFont):
        self.chatFont = chatFont
        if self.chatFont is not None:
            self.chatTextNode.setFont(self.chatFont)
        self.update()

    def getChatFont(self):
        return self.chatFont

    def setChatType(self, chatType):
        self.chatType = chatType

    def getChatType(self):
        return self.chatType

    def setChatBalloonType(self, chatBalloonType):
        self.chatBalloonType = chatBalloonType

    def getChatBalloonType(self):
        return self.chatBalloonType

    def setNametagColor(self, nametagColor):
        self.nametagColor = nametagColor

    def getNametagColor(self):
        return self.nametagColor

    def setChatColor(self, chatColor):
        self.chatColor = chatColor

    def getChatColor(self):
        return self.chatColor

    def setSpeedChatColor(self, speedChatColor):
        self.speedChatColor = speedChatColor

    def getSpeedChatColor(self):
        return self.speedChatColor

    def hideNametag(self):
        self.nametagHidden = True

    def showNametag(self):
        self.nametagHidden = False

    def hideChat(self):
        self.chatHidden = True

    def showChat(self):
        self.chatHidden = False

    def hideThought(self):
        self.thoughtHidden = True

    def showThought(self):
        self.thoughtHidden = False

    def applyClickState(self, clickState):
        if self.chatBalloon is not None:
            foreground, background = self.chatColor[clickState]
            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.setForeground(foreground)
            self.chatBalloon.setBackground(background)
            self.chatBalloon.setButton(self.chatButton[clickState])
        elif self.panel is not None:
            foreground, background = self.nametagColor[clickState]
            self.setForeground(foreground)
            self.setBackground(background)

    def setText(self, text):
        self.textNode.setText(text)

    def getText(self):
        return self.textNode.getText()

    def setChatText(self, chatText):
        self.chatTextNode.setText(chatText)

    def getChatText(self):
        return self.chatTextNode.getText()

    def setWordWrap(self, wordWrap):
        if wordWrap is None:
            wordWrap = self.TEXT_WORD_WRAP
        self.textNode.setWordwrap(wordWrap)
        self.update()

    def getWordWrap(self):
        return self.textNode.getWordwrap()

    def setChatWordWrap(self, chatWordWrap):
        if (chatWordWrap is None) or (chatWordWrap > self.CHAT_TEXT_WORD_WRAP):
            chatWordWrap = self.CHAT_TEXT_WORD_WRAP
        self.chatTextNode.setWordwrap(chatWordWrap)
        self.update()

    def getChatWordWrap(self):
        return self.chatTextNode.getWordwrap()

    def setForeground(self, foreground):
        self.textNode.setTextColor(foreground)

    def setBackground(self, background):
        if self.panel is not None:
            self.panel.setColor(background)

    def setShadow(self, shadow):
        self.textNode.setShadow(shadow)

    def getShadow(self):
        return self.textNode.getShadow()

    def clearShadow(self):
        self.textNode.clearShadow()

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

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

        if self.getChatText():
            if self.chatBalloonType == NametagGlobals.CHAT_BALLOON:
                if not self.chatHidden:
                    model = self.getChatBalloonModel()
                    modelWidth = self.getChatBalloonWidth()
                    modelHeight = self.getChatBalloonHeight()
                    self.drawChatBalloon(model, modelWidth, modelHeight)
                    return
            elif self.chatBalloonType == NametagGlobals.THOUGHT_BALLOON:
                if not self.thoughtHidden:
                    model = NametagGlobals.thoughtBalloonModel
                    modelWidth = NametagGlobals.thoughtBalloonWidth
                    modelHeight = NametagGlobals.thoughtBalloonHeight
                    self.drawChatBalloon(model, modelWidth, modelHeight)
                    return

        if self.getText() and (not self.nametagHidden):
            self.drawNametag()
Пример #2
0
class Avatar(Actor, ShadowCaster):
    notify = DirectNotifyGlobal.directNotify.newCategory('Avatar')

    def __init__(self, other=None):
        Actor.__init__(self, None, None, other, flattenable=0, setFinal=1)
        ShadowCaster.__init__(self)
        self.collTube = None
        self.scale = 1.0
        self.height = 0.0
        self.style = None
        self.hpText = None
        self.hpTextGenerator = TextNode('HpTextGenerator')

    def delete(self):
        try:
            self.Avatar_deleted
        except:
            Actor.cleanup(self)
            self.Avatar_deleted = 1
            self.style = None
            self.collTube = None
            self.hpText = None
            self.hpTextGenerator = None
            ShadowCaster.delete(self)
            Actor.delete(self)

    def uniqueName(self, name):
        return 'Avatar-{0}-{1}'.format(id(self), name)

    def getCollisionId(self):
        return self.uniqueName('bodyColl')

    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 getHeight(self):
        return self.height

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

    def getRadius(self):
        return GameGlobals.AvatarDefaultRadius

    def getStyle(self):
        return self.style

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

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

    def initializeBodyCollisions(self):
        self.collTube = CollisionTube(0, 0, 0.5, 0, 0,
                                      self.height - self.getRadius(),
                                      self.getRadius())
        self.collNode = CollisionNode(self.getCollisionId())
        self.collNode.addSolid(self.collTube)
        self.collNodePath = self.attachNewNode(self.collNode)
        self.collNode.setCollideMask(BitmaskGlobals.WallBitmask)

    def showNodePathColl(self):
        self.collNodePath.show()

    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 showHpText(self, number, bonus=0, scale=1.75):
        if number == 0:
            return

        if self.hpText:
            self.hideHpText()

        self.hpTextGenerator.setFont(GameGlobals.getSignFont())

        if number < 0:
            text = str(number)

            if random.randrange(0, 100) < SILLY_SURGE_CHANCE:
                text += '\n'
                text += random.choice(GameLocalizer.SillySurgeTerms)
        else:
            text = '+' + str(number)

        self.hpTextGenerator.setText(text)
        self.hpTextGenerator.clearShadow()
        self.hpTextGenerator.setAlign(TextNode.ACenter)

        if bonus == 1:
            color = [1, 1, 0, 1]
        elif bonus == 2:
            color = [1, 0.5, 0, 1]
        elif number < 0:
            color = [0.9, 0, 0, 1]
        else:
            color = [0, 0.9, 0, 1]

        self.hpTextGenerator.setTextColor(*color)
        self.hpTextNode = self.hpTextGenerator.generate()
        self.hpText = self.attachNewNode(self.hpTextNode)
        self.hpText.setScale(scale)
        self.hpText.setBillboardPointEye()
        self.hpText.setBin('fixed', 100)

        self.hpText.setPos(0, 0, self.height / 2)

        color[3] = 0

        Sequence(
            self.hpText.posInterval(1.0, (0, 0, self.height + 0.75),
                                    blendType='easeOut'), Wait(0.85),
            self.hpText.colorInterval(0.1, Vec4(*color), 0.1),
            Func(self.hideHpText)).start()

    def hideHpText(self):
        if self.hpText:
            taskMgr.remove(self.uniqueName('hpText'))
            self.hpText.removeNode()
            self.hpText = None
Пример #3
0
class Nametag:
    TEXT_WORD_WRAP = 8
    TEXT_Y_OFFSET = -0.05

    CHAT_TEXT_WORD_WRAP = 12

    PANEL_X_PADDING = 0.2
    PANEL_Z_PADDING = 0.2

    CHAT_BALLOON_ALPHA = 1

    def __init__(self):
        self.avatar = None

        self.panel = None
        self.icon = None
        self.chatBalloon = None

        self.chatButton = NametagGlobals.noButton
        self.chatReversed = False

        self.font = None
        self.chatFont = None

        self.chatType = NametagGlobals.CHAT
        self.chatBalloonType = NametagGlobals.CHAT_BALLOON

        self.nametagColor = NametagGlobals.NametagColors[NametagGlobals.CCNormal]
        self.arrowColor = NametagGlobals.NametagColors[NametagGlobals.CCNormal]
        self.chatColor = NametagGlobals.ChatColors[NametagGlobals.CCNormal]
        self.speedChatColor = self.chatColor[0][1]

        self.nametagHidden = False
        self.chatHidden = False
        self.thoughtHidden = False

        # Create our TextNodes:
        self.textNode = TextNode('text')
        self.textNode.setWordwrap(self.TEXT_WORD_WRAP)
        self.textNode.setAlign(TextNode.ACenter)

        self.chatTextNode = TextNode('chatText')
        self.chatTextNode.setWordwrap(self.CHAT_TEXT_WORD_WRAP)
        self.chatTextNode.setGlyphScale(ChatBalloon.TEXT_GLYPH_SCALE)
        self.chatTextNode.setGlyphShift(ChatBalloon.TEXT_GLYPH_SHIFT)

        # Add the tick task:
        self.tickTaskName = self.getUniqueName() + '-tick'
        self.tickTask = taskMgr.add(self.tick, self.tickTaskName, sort=45)

    def destroy(self):
        if self.tickTask is not None:
            taskMgr.remove(self.tickTask)
            self.tickTask = None

        self.chatTextNode = None
        self.textNode = None

        self.chatFont = None
        self.font = None

        self.chatButton = NametagGlobals.noButton

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

        if self.icon is not None:
            self.icon.removeAllChildren()
            self.icon = None

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

        self.avatar = None

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

    def getChatBalloonModel(self):
        pass  # Inheritors should override this method.

    def getChatBalloonWidth(self):
        pass  # Inheritors should override this method.

    def getChatBalloonHeight(self):
        pass  # Inheritors should override this method.

    def tick(self, task):
        return Task.done  # Inheritors should override this method.

    def updateClickRegion(self):
        pass  # Inheritors should override this method.

    def drawChatBalloon(self, model, modelWidth, modelHeight):
        pass  # Inheritors should override this method.

    def drawNametag(self):
        pass  # Inheritors should override this method.

    def setAvatar(self, avatar):
        self.avatar = avatar

    def getAvatar(self):
        return self.avatar

    def setIcon(self, icon):
        self.icon = icon

    def getIcon(self):
        return self.icon

    def setChatButton(self, chatButton):
        self.chatButton = chatButton

    def getChatButton(self):
        return self.chatButton

    def hasChatButton(self):
        if (self.chatBalloonType == NametagGlobals.CHAT_BALLOON) and self.chatHidden:
            return False
        if (self.chatBalloonType == NametagGlobals.THOUGHT_BALLOON) and self.thoughtHidden:
            return False
        return self.chatButton != NametagGlobals.noButton

    def setChatReversed(self, chatReversed):
        self.chatReversed = chatReversed

    def getChatReversed(self):
        return self.chatReversed

    def setFont(self, font):
        self.font = font
        if self.font is not None:
            self.textNode.setFont(self.font)
        self.update()

    def getFont(self):
        return self.font

    def setChatFont(self, chatFont):
        self.chatFont = chatFont
        if self.chatFont is not None:
            self.chatTextNode.setFont(self.chatFont)
        self.update()

    def getChatFont(self):
        return self.chatFont

    def setChatType(self, chatType):
        self.chatType = chatType

    def getChatType(self):
        return self.chatType

    def setChatBalloonType(self, chatBalloonType):
        self.chatBalloonType = chatBalloonType

    def getChatBalloonType(self):
        return self.chatBalloonType

    def setNametagColor(self, nametagColor):
        self.nametagColor = nametagColor

    def getNametagColor(self):
        return self.nametagColor

    def setArrowColor(self, arrowColor):
        self.arrowColor = arrowColor

    def getArrowColor(self):
        return self.arrowColor

    def setChatColor(self, chatColor):
        self.chatColor = chatColor

    def getChatColor(self):
        return self.chatColor

    def setSpeedChatColor(self, speedChatColor):
        self.speedChatColor = speedChatColor

    def getSpeedChatColor(self):
        return self.speedChatColor

    def hideNametag(self):
        self.nametagHidden = True

    def showNametag(self):
        self.nametagHidden = False

    def hideChat(self):
        self.chatHidden = True

    def showChat(self):
        self.chatHidden = False

    def hideThought(self):
        self.thoughtHidden = True

    def showThought(self):
        self.thoughtHidden = False

    def applyClickState(self, clickState):
        if self.chatBalloon is not None:
            foreground, background = self.chatColor[clickState]
            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.setForeground(foreground)
            self.chatBalloon.setBackground(background)
            self.chatBalloon.setButton(self.chatButton[clickState])
        elif self.panel is not None:
            foreground, background = self.nametagColor[clickState]
            self.setForeground(foreground)
            self.setBackground(background)

    def setText(self, text):
        self.textNode.setText(text)

    def getText(self):
        return self.textNode.getText()

    def setChatText(self, chatText):
        self.chatTextNode.setText(chatText)

    def getChatText(self):
        return self.chatTextNode.getText()

    def setWordWrap(self, wordWrap):
        if wordWrap is None:
            wordWrap = self.TEXT_WORD_WRAP
        self.textNode.setWordwrap(wordWrap)
        self.update()

    def getWordWrap(self):
        return self.textNode.getWordwrap()

    def setChatWordWrap(self, chatWordWrap):
        if (chatWordWrap is None) or (chatWordWrap > self.CHAT_TEXT_WORD_WRAP):
            chatWordWrap = self.CHAT_TEXT_WORD_WRAP
        self.chatTextNode.setWordwrap(chatWordWrap)
        self.update()

    def getChatWordWrap(self):
        return self.chatTextNode.getWordwrap()

    def setForeground(self, foreground):
        self.textNode.setTextColor(foreground)

    def setBackground(self, background):
        if self.panel is not None:
            self.panel.setColor(background)

    def setShadow(self, shadow):
        self.textNode.setShadow(shadow)

    def getShadow(self):
        return self.textNode.getShadow()

    def clearShadow(self):
        self.textNode.clearShadow()

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

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

        if self.getChatText():
            if self.chatBalloonType == NametagGlobals.CHAT_BALLOON:
                if not self.chatHidden:
                    model = self.getChatBalloonModel()
                    modelWidth = self.getChatBalloonWidth()
                    modelHeight = self.getChatBalloonHeight()
                    self.drawChatBalloon(model, modelWidth, modelHeight)
                    return
            elif self.chatBalloonType == NametagGlobals.THOUGHT_BALLOON:
                if not self.thoughtHidden:
                    model = NametagGlobals.thoughtBalloonModel
                    modelWidth = NametagGlobals.thoughtBalloonWidth
                    modelHeight = NametagGlobals.thoughtBalloonHeight
                    self.drawChatBalloon(model, modelWidth, modelHeight)
                    return

        if self.getText() and (not self.nametagHidden):
            self.drawNametag()
Пример #4
0
class Sprite(NodePath, DirectObject):
    #- Just constant identifiers
    MouseLeftDown = 'left-down'
    MouseLeftUp = 'left-up'
    MouseLeftClick = 'left-click'
    MouseCenterDown = 'center-down'
    MouseCenterUp = 'center-up'
    MouseCenterClick = 'center-click'
    MouseRightDown = 'right-down'
    MouseRightUp = 'right-up'
    MouseRightClick = 'right-click'
    MouseFourDown = 'four-down'
    MouseFourUp = 'four-up'
    MouseFourClick = 'four-click'
    MouseFiveDown = 'five-down'
    MouseFiveUp = 'five-up'
    MouseFiveClick = 'five-click'
    MouseScrollUp = 'scroll-up'
    MouseScrollDown = 'scroll-down'
    MouseEnter = 'enter'
    MouseExit = 'exit'
    MouseWithin = 'within'
    MouseWithout = 'without'
    StateDefault = 'state-default'
    StateHover = 'state-hover'
    StateClick = 'state-click'
    StateFocus = 'state-focus'
    StateDisabled = 'state-disabled'

    def __init__(self, name):
        NodePath.__init__(self, name)
        DirectObject.__init__(self)
        self.setPythonTag('Sprite', self)

        global SpriteCounter
        SpriteCounter += 1
        self.__id = int(SpriteCounter)

        #- Use PGItem to detect mouse and keyboard input via PGTop (eg, aspect2d, pixel2d, etc)
        self.__pgItem = PGItem(name)
        self.__pgItem.setActive(True)
        self.__pgItemNp = self.attachNewNode(self.__pgItem)

        #- Use TextNode to generate both text and cards for displaying background images
        self.__textNode = TextNode(name)
        self.__textNodeNp = None
        #self.__textNodeNp = self.attachNewNode(self.__textNode)
        #self.__textNode.setCardDecal(True) #- This is what we would do, should Sprite support being non-under PGTop

        self.accept(self.__pgItem.getPressEvent(MouseButton.one()),
                    self.__onMouse, [Sprite.MouseLeftDown])
        self.accept(self.__pgItem.getPressEvent(MouseButton.two()),
                    self.__onMouse, [Sprite.MouseCenterDown])
        self.accept(self.__pgItem.getPressEvent(MouseButton.three()),
                    self.__onMouse, [Sprite.MouseRightDown])
        self.accept(self.__pgItem.getPressEvent(MouseButton.four()),
                    self.__onMouse, [Sprite.MouseFourDown])
        self.accept(self.__pgItem.getPressEvent(MouseButton.five()),
                    self.__onMouse, [Sprite.MouseFiveDown])

        self.accept(self.__pgItem.getReleaseEvent(MouseButton.one()),
                    self.__onMouse, [Sprite.MouseLeftUp])
        self.accept(self.__pgItem.getReleaseEvent(MouseButton.two()),
                    self.__onMouse, [Sprite.MouseCenterUp])
        self.accept(self.__pgItem.getReleaseEvent(MouseButton.three()),
                    self.__onMouse, [Sprite.MouseRightUp])
        self.accept(self.__pgItem.getReleaseEvent(MouseButton.four()),
                    self.__onMouse, [Sprite.MouseFourUp])
        self.accept(self.__pgItem.getReleaseEvent(MouseButton.five()),
                    self.__onMouse, [Sprite.MouseFiveUp])

        self.accept(self.__pgItem.getPressEvent(MouseButton.wheelDown()),
                    self.__onMouse, [Sprite.MouseScrollDown])
        self.accept(self.__pgItem.getPressEvent(MouseButton.wheelUp()),
                    self.__onMouse, [Sprite.MouseScrollUp])

        self.accept(self.__pgItem.getEnterEvent(), self.__onMouse,
                    [Sprite.MouseEnter])
        self.accept(self.__pgItem.getExitEvent(), self.__onMouse,
                    [Sprite.MouseExit])
        self.accept(self.__pgItem.getWithinEvent(), self.__onMouse,
                    [Sprite.MouseWithin])
        self.accept(self.__pgItem.getWithoutEvent(), self.__onMouse,
                    [Sprite.MouseWithout])

        self.__beastDebug = ConfigVariableBool('beast-debug', False).getValue()

        self.__mouseInside = False
        self.__disabled = False

        #- Setup state configuration
        self.__lastStateOptions = None
        self.__state = None
        self.__states = {
            'default': SpriteOptions(),
            'hover': SpriteOptions(),
            'click': SpriteOptions(),
            'focus': SpriteOptions(),
            'disabled': SpriteOptions(),
        }
        self.updateToState('default')

    def setDirty(self, dirty=True):
        if dirty:
            self.setTag('dirty', '')
        else:
            self.clearTag('dirty')

    def setDisabled(self, disabled):
        self.__disabled = disabled
        if self.__disabled:
            self.updateToState('disabled')
        else:
            #- FIXME! Account for focus?
            self.updateToState('default')

    def isDisabled(self):
        return self.__disabled

    def bind(self, event, method, extraArgs=[], onlyOnce=False):
        actualEvent = self.getEvent(event)
        if onlyOnce:
            self.acceptOnce(event, method, extraArgs=extraArgs)
        else:
            self.accept(event, method, extraArgs=extraArgs)

    def unbind(self, event):
        actualEvent = self.getEvent(event)
        self.ignore(actualEvent)

    def getEvent(self, event):
        return 'Sprite-' + str(self.__id) + '-' + self.getName() + '-' + event

    def __onMouse(self, event, mwr):
        if event == Sprite.MouseEnter:
            self.__mouseInside = True
        elif event == Sprite.MouseExit:
            self.__mouseInside = False

        actualEvents = []
        actualEvents.append(event)

        if self.__mouseInside:
            if event == Sprite.MouseLeftUp:
                actualEvents.append(Sprite.MouseLeftClick)
            elif event == Sprite.MouseCenterUp:
                actualEvents.append(Sprite.MouseCenterClick)
            elif event == Sprite.MouseRightUp:
                actualEvents.append(Sprite.MouseRightClick)
            elif event == Sprite.MouseFourUp:
                actualEvents.append(Sprite.MouseFourClick)
            elif event == Sprite.MouseFiveUp:
                actualEvents.append(Sprite.MouseFiveClick)

        #- Handle state changing
        for event in actualEvents:
            if self.__state == 'default':
                if event == Sprite.MouseEnter:
                    self.updateToState('hover')
            elif self.__state == 'hover':
                if event == Sprite.MouseExit:
                    self.updateToState('default')
                if event == Sprite.MouseLeftDown:
                    self.updateToState('click')
            elif self.__state == 'click':
                if event == Sprite.MouseLeftClick:
                    #                    self.updateToState('focus')
                    self.updateToState('hover')
                if event == Sprite.MouseLeftUp and self.__mouseInside == False:
                    #                    self.updateToState('focus')
                    self.updateToState('default')

        #- Finally, we can send out user events
        for event in actualEvents:
            messenger.send(self.getEvent(event))

    def updateToState(self, newState=None):
        if newState == self.__state:
            return
        if newState:
            self.__state = newState
        if self.__beastDebug:
            print self.__state, globalClock.getFrameCount()
        messenger.send(self.getEvent('state-' + self.__state))
        defaultOptions = self.__states['default']
        hoverOptions = self.__states['hover']
        clickOptions = self.__states['click']
        focusOptions = self.__states['focus']
        disabledOptions = self.__states['disabled']

        if self.__state == 'default':
            self._applySpriteOptions(SpriteOptions.combine(defaultOptions))
        elif self.__state == 'hover':
            self._applySpriteOptions(
                SpriteOptions.combine(defaultOptions, hoverOptions))
        elif self.__state == 'click':
            self._applySpriteOptions(
                SpriteOptions.combine(defaultOptions, hoverOptions,
                                      clickOptions))
        elif self.__state == 'focus':
            self._applySpriteOptions(
                SpriteOptions.combine(defaultOptions, focusOptions))
        elif self.__state == 'disabled':
            self._applySpriteOptions(
                SpriteOptions.combine(defaultOptions, disabledOptions))

    def _applySpriteOptions(self, options):
        dirty = False
        if options.options.values() == self.__lastStateOptions:
            return
        else:
            self.__lastStateOptions = options.options.values()
            self.setDirty()
            dirty = True

        #- First we do checks for size
        if options.hasSize():
            self.__pgItem.setActive(True)  #- It has a size, so it's active
            x, y = options.getSize()
            self.__pgItem.setFrame(0, x, -y, 0)
            #- Set both the card and frame bounds to match the size specified
            self.__textNode.setCardActual(0, x, -y, 0)

            if options.hasBorderColor():
                #- They want a border, no matter what
                self.__textNode.setFrameActual(0, x, -y, 0)
                if options.hasBorderWidth():
                    self.__textNode.setFrameLineWidth(options.getBorderWidth())
                else:
                    self.__textNode.setFrameLineWidth(1)
            else:
                #- They want no border
                self.__textNode.clearFrame()
                self.__textNode.setFrameLineWidth(0)  #- Unimportant I think
        else:
            self.__pgItem.setActive(False)  #- It has no size, it's un active
            self.__pgItem.setFrame(0, 0, 0, 0)
            self.__textNode.clearCard()
            self.__textNode.clearFrame()

        if options.hasFont():
            self.__textNode.setFont(options.getFont())
        else:
            self.__textNode.clearFont()

        if options.hasFontSize():
            #- FIXME! change for point2d later
            textNodeScale = 1.0 / options.getFontSize()  #- Applied later
        #    #self.__textNodeNp.setScale(1.0 / options.getFontSize())
        # FIXME? Make sure it's 4 spaces    self.__textNode.setTabWidth(options.getFontSize() * 4.0)
        else:
            textNodeScale = 1.0
        #    #self.__textNodeNp.setScale(1.0)

        if options.hasBackgroundColor():
            color = options.getBackgroundColor().getAsFloat()
            self.__textNode.setCardColor(*color)
        else:
            if options.hasSize():
                self.__textNode.setCardColor(1, 1, 1, 1)  #- White
            else:
                self.__textNode.setCardColor(0, 0, 0, 0)  #- Clear

        if options.hasBorderColor():
            color = options.getBorderColor().getAsFloat()
            self.__textNode.setFrameColor(*color)
        else:
            self.__textNode.setFrameColor(0, 0, 0, 0)  #- Clear

        if options.hasText():
            text = options.getText()
            self.__textNode.setWtext(options.getText())

            if options.hasTextPadding():
                px, py = options.getTextPadding()
            else:
                px, py = (0, 0)

            #- Set sizes, even if they have no color
            self.__textNode.setCardAsMargin(px, px, py, py)
            self.__textNode.setFrameAsMargin(px, px, py, py)

            l, r, b, t = self.__textNode.getCardActual()
            #- Offset the TextNode so it only extends right and down

            #- FIXME! change for point2d later? (Does scale apply there even haha?)
            s = textNodeScale
            l, r, b, t = l * s, r * s, b * s, t * s

            textNodeX = -l  #- Applied later
            textNodeZ = -t  #- Applied later
            #self.__textNodeNp.setX(-l)
            #self.__textNodeNp.setZ(-t)
            self.__pgItem.setActive(True)  #- It has a size now, it's active
            self.__pgItem.setFrame(0, -l + r, -t + b, 0)
        else:
            self.__textNode.setWtext(
                u' '
            )  #- It's important to have a space, otherwise no card is rendered!
            textNodeX = 0  #- Applied later
            textNodeZ = 0  #- Applied later
            #self.__textNodeNp.setX(0)
            #self.__textNodeNp.setZ(0)
            if not options.hasSize():
                self.__pgItem.setFrame(0, 0, 0, 0)

        if options.hasTextAlign():
            align = options.getTextAlign()
            if align == 'left':
                self.__textNode.setAlign(TextNode.ALeft)
            elif align == 'center':
                self.__textNode.setAlign(TextNode.ACenter)
            elif align == 'right':
                self.__textNode.setAlign(TextNode.ARight)
        else:
            self.__textNode.clearAlign()

        if options.hasTextColor():
            color = options.getTextColor().getAsFloat()
            self.__textNode.setTextColor(*color)
            #self.__textNodeNp.setColor(*color)
            #else:
            self.__textNode.setTextColor(0, 0, 0, 1)  #- Black
        #    self.__textNodeNp.clearColor()

        if options.hasTextSmallCaps():
            self.__textNode.setSmallCaps(options.getTextSmallCaps())
        else:
            self.__textNode.clearSmallCaps()

        if options.hasTextSmallCapsScale():
            self.__textNode.setSmallCapsScale(options.getTextSmallCapsScale())
        else:
            self.__textNode.clearSmallCapsScale()

        if options.hasTextSlant():
            self.__textNode.setSlant(options.getTextSlant())
        else:
            self.__textNode.clearSlant()

        if options.hasTextUnderline():
            self.__textNode.setUnderscore(options.getTextUnderline())
        else:
            self.__textNode.clearUnderscore()

        if options.hasTextShadow():
            self.__textNode.setShadow(*options.getTextShadow())
        else:
            self.__textNode.clearShadow()

        if options.hasTextShadowColor():
            color = options.getTextShadowColor().getAsFloat()
            self.__textNode.setShadowColor(*color)
        else:
            self.__textNode.clearShadowColor()

        if self.__textNodeNp:
            self.__textNodeNp.remove()
        self.__textNodeNp = self.attachNewNode(self.__textNode.generate())
        self.__textNodeNp.setScale(textNodeScale)
        self.__textNodeNp.setX(textNodeX)
        self.__textNodeNp.setZ(textNodeZ)
        '''
        def setup(self, textNodeScale, textNodeX, textNodeZ):
            print 'getting lock...'
            base.textThreadLock.acquire()
            print 'Got lock'
            if self.__textNodeNp:
                self.__textNodeNp.remove()
            self.__textNodeNp = self.attachNewNode('foobar')
            #textNode.generate()
            print 'yes, it generated'
            base.textThreadLock.release()
        '''
        #self.__textNodeNp = self.attachNewNode(self.__textNode.generate())
        #self.__textNodeNp.setScale(textNodeScale)
        #self.__textNodeNp.setX(textNodeX)
        #self.__textNodeNp.setZ(textNodeZ)
        '''
        import direct.stdpy.threading
        t = threading.Thread(target=setup, args=(self, textNodeScale, textNodeX, textNodeZ))
        t.start()
        '''

        if dirty:
            messenger.send('beastCollectionUpdate')

    def getOptions(self, state):
        return self.__states[state]

    def getCurrentOptions(self):
        return self.__states[self.__state]

    def getCurrentState(self):
        return self.__state

    def getPgItem(self):
        return self.__pgItem

    def getPgItemNp(self):
        return self.__pgItemNp

    def getTextNode(self):
        return self.__textNode

    def getTextNodeNp(self):
        return self.__textNodeNp

    @staticmethod
    def destroyChildren(np):
        for child in np.getChildren():
            if child.hasPythonTag('Sprite'):
                sprite = child.getPythonTag('Sprite')
                sprite.destroy()

    def destroy(self):
        self.clearPythonTag('Sprite')
        self.ignoreAll()
        Sprite.destroyChildren(self)
        self.remove()