mirror of
https://github.com/Sneed-Group/Poodletooth-iLand
synced 2024-12-27 13:42:28 -06:00
323 lines
11 KiB
Python
323 lines
11 KiB
Python
|
from direct.task.Task import Task
|
||
|
import math
|
||
|
from panda3d.core import PGButton, VBase4, DepthWriteAttrib, Point3
|
||
|
|
||
|
from toontown.chat.ChatBalloon import ChatBalloon
|
||
|
from toontown.margins import MarginGlobals
|
||
|
from toontown.margins.MarginVisible import MarginVisible
|
||
|
from toontown.nametag import NametagGlobals
|
||
|
from toontown.nametag.Nametag import Nametag
|
||
|
from toontown.toontowngui.Clickable2d import Clickable2d
|
||
|
|
||
|
|
||
|
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[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)
|