class Whisper: LENGTH_FACTOR = 0.6 def createSystemMessage(self, message, important = 1): try: taskMgr.remove('clearSystemMessage-' + self.taskName) self.bubble.remove_node() self.bubble = None except: pass self.taskName = str(random.uniform(0, 10101010100L)) msg_color = (0.8, 0.3, 0.6, 0.6) sysmsg_data = [[Point3(0.075, 0, -0.2), base.a2dLeftCenter], [Point3(-0.6, 0, -0.7), base.a2dRightCenter], [Point3(-0.6, 0, -0.2), base.a2dRightCenter], [Point3(0.075, 0, -0.7), base.a2dLeftCenter], [Point3(0.35, 0, 0.2), base.a2dBottomCenter], [Point3(-0.2, 0, 0.2), base.a2dBottomCenter], [Point3(-0.8, 0, 0.2), base.a2dBottomCenter]] data = random.choice(sysmsg_data) sfx = loader.loadSfx('phase_3.5/audio/sfx/GUI_whisper_3.mp3') SoundInterval(sfx).start() length = math.sqrt(len(message)) / self.LENGTH_FACTOR self.bubble = ChatBalloon(loader.loadModel('phase_3/models/props/chatbox_noarrow.bam')).generate(message, CIGlobals.getToonFont(), balloonColor=msg_color) self.bubble.reparent_to(data[1]) self.bubble.set_pos(data[0]) self.bubble.set_scale(0.05) taskMgr.doMethodLater(length, self.clearSystemMessage, 'clearSystemMessage-' + self.taskName) return def clearSystemMessage(self, task): self.bubble.remove_node() self.bubble = None return task.done
def setChatAbsolute(self, chatString = None): if not chatString or chatString.isspace() or len(chatString) == 0: return self.clearChat() self.taskId = random.randint(0, 1000000000000000000000000000000L) if self.nameTag: self.getNameTag().hide() if self.isThought(chatString): chatString = self.removeThoughtPrefix(chatString) bubble = loader.loadModel(CIGlobals.ThoughtBubble) else: length = math.sqrt(len(chatString)) / self.LENGTH_FACTOR if length < self.MIN_LENGTH: length = self.MIN_LENGTH if length > self.MAX_LENGTH: length = self.MAX_LENGTH bubble = loader.loadModel(CIGlobals.ChatBubble) if self.autoClearChat: taskMgr.doMethodLater(length, self.clearChatTask, 'clearAvatarChat-%s' % str(self.taskId)) if self.avatarType == CIGlobals.Suit: font = CIGlobals.getSuitFont() else: font = CIGlobals.getToonFont() self.chatBubble = ChatBalloon(bubble).generate(chatString, font) self.chatBubble.setEffect(BillboardEffect.make(Vec3(0, 0, 1), True, False, 3.0, camera, Point3(0, 0, 0))) if self.nameTag: self.chatBubble.setZ(self.getNameTag().getZ()) elif self.avatarType == CIGlobals.Suit: self.chatBubble.setZ(CIGlobals.SuitNameTagPos[self.head]) if hasattr(self.avatar, 'getGhost'): if not self.avatar.getGhost() or self.avatar.doId == base.localAvatar.doId: self.chatBubble.reparentTo(self) else: self.chatBubble.reparentTo(self) LabelScaler().resize(self.chatBubble)
def setChatAbsolute(self, chatString=None): if not chatString or chatString.isspace() or len(chatString) == 0: return self.clearChat() self.taskId = random.randint(0, 1000000000000000000000000000000) if self.nameTag: self.getNameTag().hide() if self.isThought(chatString): chatString = self.removeThoughtPrefix(chatString) bubble = loader.loadModel(CIGlobals.ThoughtBubble) else: length = math.sqrt(len(chatString)) / self.LENGTH_FACTOR if length < self.MIN_LENGTH: length = self.MIN_LENGTH if length > self.MAX_LENGTH: length = self.MAX_LENGTH bubble = loader.loadModel(CIGlobals.ChatBubble) if self.autoClearChat: taskMgr.doMethodLater( length, self.clearChatTask, "clearAvatarChat-%s" % (str(self.taskId))) if self.avatarType == CIGlobals.Suit: font = CIGlobals.getSuitFont() else: font = CIGlobals.getToonFont() self.chatBubble = ChatBalloon(bubble).generate(chatString, font) self.chatBubble.setEffect( BillboardEffect.make(Vec3(0, 0, 1), True, False, 3.0, camera, Point3(0, 0, 0))) if self.nameTag: self.chatBubble.setZ(self.getNameTag().getZ()) else: if self.avatarType == CIGlobals.Suit: self.chatBubble.setZ(CIGlobals.SuitNameTagPos[self.head]) if hasattr(self.avatar, 'getGhost'): if not self.avatar.getGhost( ) or self.avatar.doId == base.localAvatar.doId: self.chatBubble.reparentTo(self) else: self.chatBubble.reparentTo(self) LabelScaler().resize(self.chatBubble)
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)
def drawChatBalloon(self, model, modelWidth, modelHeight): if self.chatFont is None: return if self.avatar.avatarType == CIGlobals.Suit and len( self.getText().split('\n')) == 3: name, dept, level = self.getText().split('\n') else: name = self.getText() self.chatTextNode.setText(name + ': ' + 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) return
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) return
def setChat(self, chat): if chat == "": return self.nameTag.hide() try: self.bubble.remove() taskMgr.remove("RemoveMickeyChat-" + str(self.random_taskid)) except: pass self.chat = chat self.it = loader.loadFont(CIGlobals.ToonFont, lineHeight=CIGlobals.ToonFontLineHeight) b = loader.loadTexture("phase_3/maps/chatbubble.jpg", "phase_3/maps/chatbubble_a.rgb") self.balloon_sfx = loader.loadSfx( "phase_3/audio/sfx/GUI_balloon_popup.mp3") self.balloon_sfx.play() self.dial = loader.loadSfx("phase_3/audio/dial/mickey.wav") self.dial.play() self.box = loader.loadModel(CIGlobals.ChatBubble) self.ChatBalloon = ChatBalloon(self.box) LS = LabelScaler() self.bubble = self.ChatBalloon.generate(chat, self.it) LS.resize(self.bubble) self.bubble.reparentTo(self) self.bubble.setZ(self.nameTag.getZ() - 0.3) self.bubble.setBillboardPointEye() self.random_taskid = random.randint( 0, 10000000000000000000000000000000000000000000000000000000000000000000000 ) taskMgr.doMethodLater(7, self.delChat, "RemoveMickeyChat-" + str(self.random_taskid))
class ToonTalker: THOUGHT_PREFIX = '.' LENGTH_FACTOR = 0.6 MIN_LENGTH = 5 MAX_LENGTH = 20 def __init__(self): self.avatar = None self.nametag = None self.autoClearChat = True return def setAutoClearChat(self, flag): self.autoClearChat = flag def setAvatar(self, avatar, nametag): self.avatar = avatar self.nametag = nametag def setChatAbsolute(self, chatString=None): if not chatString or chatString.isspace() or len(chatString) == 0: return self.clearChat() self.taskId = random.randint(0, 1000000000000000000000000000000L) if self.nameTag: self.getNameTag().hide() if self.isThought(chatString): chatString = self.removeThoughtPrefix(chatString) bubble = loader.loadModel(CIGlobals.ThoughtBubble) else: length = math.sqrt(len(chatString)) / self.LENGTH_FACTOR if length < self.MIN_LENGTH: length = self.MIN_LENGTH if length > self.MAX_LENGTH: length = self.MAX_LENGTH bubble = loader.loadModel(CIGlobals.ChatBubble) if self.autoClearChat: taskMgr.doMethodLater(length, self.clearChatTask, 'clearAvatarChat-%s' % str(self.taskId)) if self.avatarType == CIGlobals.Suit: font = CIGlobals.getSuitFont() else: font = CIGlobals.getToonFont() self.chatBubble = ChatBalloon(bubble).generate(chatString, font) self.chatBubble.setEffect( BillboardEffect.make(Vec3(0, 0, 1), True, False, 3.0, camera, Point3(0, 0, 0))) if self.nameTag: self.chatBubble.setZ(self.getNameTag().getZ()) else: if self.avatarType == CIGlobals.Suit: self.chatBubble.setZ(CIGlobals.SuitNameTagPos[self.head]) if hasattr(self.avatar, 'getGhost'): if not self.avatar.getGhost( ) or self.avatar.doId == base.localAvatar.doId: self.chatBubble.reparentTo(self) else: self.chatBubble.reparentTo(self) LabelScaler().resize(self.chatBubble) def isThought(self, message): if message.isspace(): return False if message[0] == self.THOUGHT_PREFIX: return True return False def removeThoughtPrefix(self, message): if self.isThought(message): return message[len(self.THOUGHT_PREFIX):] notify.warning( 'attempted to remove a thought prefix on a non-thought message') return message def clearChatTask(self, task): self.clearChat() return task.done def clearChat(self): try: self.chatBubble.removeNode() del self.chatBubble except: return if hasattr(self.avatar, 'getGhost'): if self.nameTag and not self.avatar.getGhost( ) or self.nameTag and self.avatar.doId == base.localAvatar.doId: self.getNameTag().show() else: if self.nameTag: self.getNameTag().show() taskMgr.remove('clearAvatarChat-' + str(self.taskId))
class Nametag3d(Nametag, Clickable3d): SCALING_MIN_DISTANCE = 1 SCALING_MAX_DISTANCE = 50 SCALING_FACTOR = 0.065 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 bottom = NametagGlobals.chatBalloon3dHeight - 2.4 top = bottom + self.chatBalloon.height self.setClickRegionFrame(left, right, bottom, top) else: if 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) return 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 else: if 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) return 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(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) return
class DistributedMickey(DistributedSmoothNode): def __init__(self, cr): self.cr = cr DistributedSmoothNode.__init__(self, cr) NodePath.__init__(self, 'Mickey') self.name = "Mickey" self.anim = "" self.chat = "" self.mickey = Actor( "phase_3/models/char/mickey-1200.bam", { "neutral": "phase_3/models/char/mickey-wait.bam", "walk": "phase_3/models/char/mickey-walk.bam", "run": "phase_3/models/char/mickey-run.bam", "left-start": "phase_3.5/models/char/mickey-left-start.bam", "left": "phase_3.5/models/char/mickey-left.bam", "right-start": "phase_3.5/models/char/mickey-right-start.bam", "right": "phase_3.5/models/char/mickey-right.bam" }) self.mickeyEye = self.mickey.controlJoint(None, "modelRoot", "joint_pupilR") self.mickeyEye.setY(0.025) self.mickey.reparentTo(self) self.mickey.setScale(1.25) for bundle in self.mickey.getPartBundleDict().values(): bundle = bundle['modelRoot'].getBundle() earNull = bundle.findChild('sphere3') if not earNull: earNull = bundle.findChild('*sphere3') earNull.clearNetTransforms() for bundle in self.mickey.getPartBundleDict().values(): charNodepath = bundle['modelRoot'].partBundleNP bundle = bundle['modelRoot'].getBundle() earNull = bundle.findChild('sphere3') if not earNull: earNull = bundle.findChild('*sphere3') ears = charNodepath.find('**/sphere3') if ears.isEmpty(): ears = charNodepath.find('**/*sphere3') ears.clearEffect(CharacterJointEffect.getClassType()) earRoot = charNodepath.attachNewNode('earRoot') earPitch = earRoot.attachNewNode('earPitch') earPitch.setP(40.0) ears.reparentTo(earPitch) earNull.addNetTransform(earRoot.node()) ears.clearMat() ears.node().setPreserveTransform(ModelNode.PTNone) ears.setP(-40.0) ears.flattenMedium() ears.setBillboardAxis() self.shadow = loader.loadModel("phase_3/models/props/drop_shadow.bam") self.shadow.setScale(0.55) self.shadow.flattenMedium() self.shadow.setBillboardAxis(4) try: self.shadowPlacer = ShadowPlacer(base.cTrav, self.shadow, base.wall_mask, base.floor_mask) self.shadowPlacer.on() except: pass self.shadow.reparentTo(self) cs = CollisionSphere(0, 0, 0, 2) cnode = CollisionNode('mickeyCNode') cnode.addSolid(cs) rs = CollisionRay(0, 0, 2, 0, 0, -1) rnode = CollisionNode('mickeyRNode') rnode.addSolid(rs) self.cnp = self.attachNewNode(cnode) self.cnp.setZ(0.75) self.rnp = self.attachNewNode(rnode) def mickeyCollisions(self): self.cnp.setCollideMask(BitMask32(0)) self.cnp.node().setFromCollideMask(CIGlobals.WallBitmask) self.rnp.setCollideMask(BitMask32(0)) self.rnp.node().setFromCollideMask(CIGlobals.FloorBitmask) ss = CollisionSphere(0, 0, 0, 10) snode = CollisionNode('mickeySNode') snode.addSolid(ss) self.snp = self.attachNewNode(snode) self.snp.setZ(0.75) self.snp.setCollideMask(BitMask32(0)) self.snp.node().setFromCollideMask(CIGlobals.EventBitmask) pusher = CollisionHandlerPusher() pusher.setInPattern("%in") pusher.addCollider(self.cnp, self) floor = CollisionHandlerFloor() floor.setInPattern("%in") floor.addCollider(self.rnp, self) event = CollisionHandlerEvent() event.setInPattern("%fn-into") event.setOutPattern("%fn-out") base.cTrav.addCollider(self.cnp, pusher) base.cTrav.addCollider(self.rnp, floor) base.cTrav.addCollider(self.snp, event) def setName(self, name): self.name = name if name == "": return elif self.name == "minnie": self.name = "Minnie" try: self.nameTag.remove() del self.nameTag except: pass self.it = loader.loadFont("phase_3/models/fonts/ImpressBT.ttf") self.nameTag = DirectLabel(text=self.name, text_fg=(0.992188, 0.480469, 0.167969, 1.0), text_bg=(0.75, 0.75, 0.75, 0.5), text_wordwrap=8, text_decal=True, relief=None, parent=self) self.nameTag.setPos(0, 0, 5) self.nameTag.setBillboardPointEye() LS = LabelScaler() LS.resize(self.nameTag) def b_setName(self, name): self.d_setName(name) self.setName(name) def d_setName(self, name): self.sendUpdate("setName", [name]) def setChat(self, chat): if chat == "": return self.nameTag.hide() try: self.bubble.remove() taskMgr.remove("RemoveMickeyChat-" + str(self.random_taskid)) except: pass self.chat = chat self.it = loader.loadFont(CIGlobals.ToonFont, lineHeight=CIGlobals.ToonFontLineHeight) b = loader.loadTexture("phase_3/maps/chatbubble.jpg", "phase_3/maps/chatbubble_a.rgb") self.balloon_sfx = loader.loadSfx( "phase_3/audio/sfx/GUI_balloon_popup.mp3") self.balloon_sfx.play() self.dial = loader.loadSfx("phase_3/audio/dial/mickey.wav") self.dial.play() self.box = loader.loadModel(CIGlobals.ChatBubble) self.ChatBalloon = ChatBalloon(self.box) LS = LabelScaler() self.bubble = self.ChatBalloon.generate(chat, self.it) LS.resize(self.bubble) self.bubble.reparentTo(self) self.bubble.setZ(self.nameTag.getZ() - 0.3) self.bubble.setBillboardPointEye() self.random_taskid = random.randint( 0, 10000000000000000000000000000000000000000000000000000000000000000000000 ) taskMgr.doMethodLater(7, self.delChat, "RemoveMickeyChat-" + str(self.random_taskid)) def delChat(self, task): self.chat = "" self.nameTag.show() self.bubble.remove() return task.done def b_setChat(self, chat): self.d_setChat(chat) self.setChat(chat) def d_setChat(self, chat): self.sendUpdate("setChat", [chat]) def getChat(self): return self.chat def setAnimState(self, anim): self.anim = anim if "start" in anim: self.mickey.play(anim) self.mickey.loop(anim) def b_setAnimState(self, anim): self.d_setAnimState(anim) self.setAnimState(anim) def d_setAnimState(self, anim): self.sendUpdate("setAnimState", [anim]) def getAnimState(self): return self.anim def announceGenerate(self): DistributedSmoothNode.announceGenerate(self) self.reparentTo(render) def generate(self): DistributedSmoothNode.generate(self) self.activateSmoothing(True, False) self.startSmooth() def disable(self): self.stopSmooth() self.detachNode() DistributedSmoothNode.disable(self) def delete(self): self.mickey = None DistributedSmoothNode.delete(self)
class Nametag2d(Nametag, Clickable2d, MarginVisible): CONTENTS_SCALE = 0.25 CHAT_TEXT_MAX_ROWS = 6 CHAT_TEXT_WORD_WRAP = 8 CHAT_BALLOON_ALPHA = 0.5 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) return 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) return 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) else: if 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) return 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() else: if self.region is not None: self.region.setActive(False) return 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 if self.avatar.avatarType == CIGlobals.Suit and len( self.getText().split('\n')) == 3: name, dept, level = self.getText().split('\n') else: name = self.getText() self.chatTextNode.setText(name + ': ' + 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) return 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( NametagGlobals.NametagColors[NametagGlobals.CCOtherPlayer][0][0]) return def marginVisibilityChanged(self): if self.cell is not None: self.reposition() self.updateClickRegion() else: if self.region is not None: self.region.setActive(False) return 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() else: if 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)) else: if self.cell in base.leftCells: origin = self.contents.getRelativePoint( nodePath, (left, 0, (bottom + top) / 2.0)) else: if self.cell in base.rightCells: origin = self.contents.getRelativePoint( nodePath, (right, 0, (bottom + top) / 2.0)) self.contents.setPos(self.contents, -origin) return
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) return 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) return 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() else: if self.region is not None: self.region.setActive(False) return 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) def manage(self, marginManager): MarginVisible.manage(self, marginManager) base.playSfx(base.cr.whisperNoise) 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=[senderName, 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) return 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 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() return def marginVisibilityChanged(self): if self.cell is not None: self.reposition() self.updateClickRegion() else: if self.region is not None: self.region.setActive(False) return 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)) else: if self.cell in base.leftCells: origin = self.contents.getRelativePoint( self.chatBalloon.textNodePath, (left, 0, (bottom + top) / 2.0)) else: if self.cell in base.rightCells: origin = self.contents.getRelativePoint( self.chatBalloon.textNodePath, (right, 0, (bottom + top) / 2.0)) self.contents.setPos(self.contents, -origin) return
class ToonTalker: THOUGHT_PREFIX = '.' LENGTH_FACTOR = 0.6 MIN_LENGTH = 5 MAX_LENGTH = 20 def __init__(self): self.avatar = None self.nametag = None self.autoClearChat = True return def setAvatar(self, avatar, nametag): self.avatar = avatar self.nametag = nametag def setChatAbsolute(self, chatString = None): if not chatString or chatString.isspace() or len(chatString) == 0: return self.clearChat() self.taskId = random.randint(0, 1000000000000000000000000000000L) if self.nameTag: self.getNameTag().hide() if self.isThought(chatString): chatString = self.removeThoughtPrefix(chatString) bubble = loader.loadModel(CIGlobals.ThoughtBubble) else: length = math.sqrt(len(chatString)) / self.LENGTH_FACTOR if length < self.MIN_LENGTH: length = self.MIN_LENGTH if length > self.MAX_LENGTH: length = self.MAX_LENGTH bubble = loader.loadModel(CIGlobals.ChatBubble) if self.autoClearChat: taskMgr.doMethodLater(length, self.clearChatTask, 'clearAvatarChat-%s' % str(self.taskId)) if self.avatarType == CIGlobals.Suit: font = CIGlobals.getSuitFont() else: font = CIGlobals.getToonFont() self.chatBubble = ChatBalloon(bubble).generate(chatString, font) self.chatBubble.setEffect(BillboardEffect.make(Vec3(0, 0, 1), True, False, 3.0, camera, Point3(0, 0, 0))) if self.nameTag: self.chatBubble.setZ(self.getNameTag().getZ()) elif self.avatarType == CIGlobals.Suit: self.chatBubble.setZ(CIGlobals.SuitNameTagPos[self.head]) if hasattr(self.avatar, 'getGhost'): if not self.avatar.getGhost() or self.avatar.doId == base.localAvatar.doId: self.chatBubble.reparentTo(self) else: self.chatBubble.reparentTo(self) LabelScaler().resize(self.chatBubble) def isThought(self, message): if message.isspace(): return False elif message[0] == self.THOUGHT_PREFIX: return True else: return False def removeThoughtPrefix(self, message): if self.isThought(message): return message[len(self.THOUGHT_PREFIX):] else: notify.warning('attempted to remove a thought prefix on a non-thought message') return message def clearChatTask(self, task): self.clearChat() return task.done def clearChat(self): try: self.chatBubble.removeNode() del self.chatBubble except: return if hasattr(self.avatar, 'getGhost'): if self.nameTag and not self.avatar.getGhost() or self.nameTag and self.avatar.doId == base.localAvatar.doId: self.getNameTag().show() elif self.nameTag: self.getNameTag().show() taskMgr.remove('clearAvatarChat-' + str(self.taskId))
class Nametag3d(Nametag, Clickable3d): SCALING_MIN_DISTANCE = 1 SCALING_MAX_DISTANCE = 100 SCALING_FACTOR = 0.065 def __init__(self): Nametag.__init__(self) Clickable3d.__init__(self, 'Nametag3d') self.cTag = CNametag3d() self.distance = 0 self.card = None self.cardNP = None self.avatarNode = None self.billboardOffset = 3 self.doBillboardEffect() def destroy(self): self.ignoreAll() self.cTag = None 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: reg = [] self.cTag.get_chatballoon_region( self.chatBalloon.center, NametagGlobals.chatBalloon3dHeight, reg) self.setClickRegionFrame(*reg) elif self.panel is not None: reg = [] self.cTag.get_panel_region(self.textNode, reg) self.setClickRegionFrame(*reg) 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( self.cTag.get_scale(distance, self.SCALING_FACTOR)) self.distance = distance if self.isClickable(): 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)