oldschool-toontown/otp/avatar/Avatar.py
2023-01-10 22:46:52 -04:00

587 lines
21 KiB
Python

from panda3d.core import *
from panda3d.otp import Nametag, NametagGroup
from panda3d.otp import CFSpeech, CFThought, CFTimeout, CFPageButton, CFNoQuitButton, CFQuitButton
from otp.otpbase import OTPGlobals
from otp.otpbase import OTPLocalizer
from direct.actor.Actor import Actor
from direct.directnotify import DirectNotifyGlobal
from direct.distributed import ClockDelta
from otp.avatar.ShadowCaster import ShadowCaster
import random
from otp.otpbase import OTPRender
from otp.otpbase.PythonUtil import recordCreationStack
teleportNotify = DirectNotifyGlobal.directNotify.newCategory('Teleport')
teleportNotify.showTime = True
if ConfigVariableBool('want-teleport-debug', 1).value:
teleportNotify.setDebug(1)
def reconsiderAllUnderstandable():
for av in Avatar.ActiveAvatars:
av.considerUnderstandable()
class Avatar(Actor, ShadowCaster):
notify = DirectNotifyGlobal.directNotify.newCategory('Avatar')
ActiveAvatars = []
ManagesNametagAmbientLightChanged = False
def __init__(self, other = None):
self._name = ''
try:
self.Avatar_initialized
return
except:
self.Avatar_initialized = 1
Actor.__init__(self, None, None, other, flattenable=0, setFinal=1)
ShadowCaster.__init__(self)
self.__font = OTPGlobals.getInterfaceFont()
self.soundChatBubble = None
self.avatarType = ''
self.nametagNodePath = None
self.__nameVisible = 1
self.nametag = NametagGroup()
self.nametag.setAvatar(self)
self.nametag.setFont(OTPGlobals.getInterfaceFont())
self.nametag2dContents = Nametag.CName | Nametag.CSpeech
self.nametag2dDist = Nametag.CName | Nametag.CSpeech
self.nametag2dNormalContents = Nametag.CName | Nametag.CSpeech
self.nametag3d = self.attachNewNode('nametag3d')
self.nametag3d.setTag('cam', 'nametag')
self.nametag3d.setLightOff()
if self.ManagesNametagAmbientLightChanged:
self.acceptNametagAmbientLightChange()
OTPRender.renderReflection(False, self.nametag3d, 'otp_avatar_nametag', None)
self.getGeomNode().showThrough(OTPRender.ShadowCameraBitmask)
self.nametag3d.hide(OTPRender.ShadowCameraBitmask)
self.collTube = None
self.battleTube = None
self.scale = 1.0
self.nametagScale = 1.0
self.height = 0.0
self.battleTubeHeight = 0.0
self.battleTubeRadius = 0.0
self.style = None
self.commonChatFlags = 0
self.understandable = 1
self.setPlayerType(NametagGroup.CCNormal)
self.ghostMode = 0
self.__chatParagraph = None
self.__chatMessage = None
self.__chatFlags = 0
self.__chatPageNumber = None
self.__chatAddressee = None
self.__chatDialogueList = []
self.__chatSet = 0
self.__chatLocal = 0
self.__currentDialogue = None
self.whitelistChatFlags = 0
return
def delete(self):
try:
self.Avatar_deleted
except:
self.deleteNametag3d()
Actor.cleanup(self)
if self.ManagesNametagAmbientLightChanged:
self.ignoreNametagAmbientLightChange()
self.Avatar_deleted = 1
del self.__font
del self.style
del self.soundChatBubble
del self.nametag
self.nametag3d.removeNode()
ShadowCaster.delete(self)
Actor.delete(self)
def acceptNametagAmbientLightChange(self):
self.accept('nametagAmbientLightChanged', self.nametagAmbientLightChanged)
def ignoreNametagAmbientLightChange(self):
self.ignore('nametagAmbientLightChanged')
def isLocal(self):
return 0
def isPet(self):
return False
def isProxy(self):
return False
def setPlayerType(self, playerType):
self.playerType = playerType
if not hasattr(self, 'nametag'):
self.notify.warning('no nametag attributed, but would have been used.')
return
if self.isUnderstandable():
self.nametag.setColorCode(self.playerType)
else:
self.nametag.setColorCode(NametagGroup.CCNoChat)
def setCommonChatFlags(self, commonChatFlags):
self.commonChatFlags = commonChatFlags
self.considerUnderstandable()
if self == base.localAvatar:
reconsiderAllUnderstandable()
def setWhitelistChatFlags(self, whitelistChatFlags):
self.whitelistChatFlags = whitelistChatFlags
self.considerUnderstandable()
if self == base.localAvatar:
reconsiderAllUnderstandable()
def considerUnderstandable(self):
speed = 0
if self.playerType in (NametagGroup.CCNormal, NametagGroup.CCFreeChat, NametagGroup.CCSpeedChat):
self.setPlayerType(NametagGroup.CCSpeedChat)
speed = 1
if hasattr(base, 'localAvatar') and self == base.localAvatar:
self.understandable = 1
self.setPlayerType(NametagGroup.CCFreeChat)
elif self.playerType == NametagGroup.CCSuit:
self.understandable = 1
self.setPlayerType(NametagGroup.CCSuit)
elif self.playerType not in (NametagGroup.CCNormal, NametagGroup.CCFreeChat, NametagGroup.CCSpeedChat):
self.understandable = 1
self.setPlayerType(NametagGroup.CCNoChat)
elif hasattr(base, 'localAvatar') and self.commonChatFlags & base.localAvatar.commonChatFlags & OTPGlobals.CommonChat:
self.understandable = 1
self.setPlayerType(NametagGroup.CCFreeChat)
elif self.commonChatFlags & OTPGlobals.SuperChat:
self.understandable = 1
self.setPlayerType(NametagGroup.CCFreeChat)
elif hasattr(base, 'localAvatar') and base.localAvatar.commonChatFlags & OTPGlobals.SuperChat:
self.understandable = 1
self.setPlayerType(NametagGroup.CCFreeChat)
elif base.cr.getFriendFlags(self.doId) & OTPGlobals.FriendChat:
self.understandable = 1
self.setPlayerType(NametagGroup.CCFreeChat)
elif base.cr.playerFriendsManager.findPlayerIdFromAvId(self.doId) is not None:
playerInfo = base.cr.playerFriendsManager.findPlayerInfoFromAvId(self.doId)
if playerInfo.openChatFriendshipYesNo:
self.understandable = 1
self.nametag.setColorCode(NametagGroup.CCFreeChat)
elif playerInfo.isUnderstandable():
self.understandable = 1
else:
self.understandable = 0
elif hasattr(base, 'localAvatar') and self.whitelistChatFlags & base.localAvatar.whitelistChatFlags:
self.understandable = 1
else:
self.understandable = 0
if not hasattr(self, 'nametag'):
self.notify.warning('no nametag attributed, but would have been used')
else:
self.nametag.setColorCode(self.playerType)
return
def isUnderstandable(self):
return self.understandable
def setDNAString(self, dnaString):
pass
def setDNA(self, dna):
pass
def getAvatarScale(self):
return self.scale
def setAvatarScale(self, scale):
if self.scale != scale:
self.scale = scale
self.getGeomNode().setScale(scale)
self.setHeight(self.height)
def getNametagScale(self):
return self.nametagScale
def setNametagScale(self, scale):
self.nametagScale = scale
self.nametag3d.setScale(scale)
def adjustNametag3d(self, parentScale = 1.0):
self.nametag3d.setPos(0, 0, self.height + 0.5)
def getHeight(self):
return self.height
def setHeight(self, height):
self.height = height
self.adjustNametag3d()
if self.collTube:
self.collTube.setPointB(0, 0, height - self.getRadius())
if self.collNodePath:
self.collNodePath.forceRecomputeBounds()
if self.battleTube:
self.battleTube.setPointB(0, 0, height - self.getRadius())
def getRadius(self):
return OTPGlobals.AvatarDefaultRadius
def getName(self):
return self._name
def getType(self):
return self.avatarType
def setName(self, name):
if hasattr(self, 'isDisguised'):
if self.isDisguised:
return
self._name = name
if hasattr(self, 'nametag'):
self.nametag.setName(name)
def setDisplayName(self, str):
if hasattr(self, 'isDisguised'):
if self.isDisguised:
return
self.nametag.setDisplayName(str)
def getFont(self):
return self.__font
def setFont(self, font):
self.__font = font
self.nametag.setFont(font)
def getStyle(self):
return self.style
def setStyle(self, style):
self.style = style
def getDialogueArray(self):
return None
def playCurrentDialogue(self, dialogue, chatFlags, interrupt = 1):
if interrupt and self.__currentDialogue is not None:
self.__currentDialogue.stop()
self.__currentDialogue = dialogue
if dialogue:
base.playSfx(dialogue, node=self)
elif chatFlags & CFSpeech != 0 and self.nametag.getNumChatPages() > 0:
self.playDialogueForString(self.nametag.getChat())
if self.soundChatBubble != None:
base.playSfx(self.soundChatBubble, node=self)
return
def playDialogueForString(self, chatString):
searchString = chatString.lower()
if searchString.find(OTPLocalizer.DialogSpecial) >= 0:
type = 'special'
elif searchString.find(OTPLocalizer.DialogExclamation) >= 0:
type = 'exclamation'
elif searchString.find(OTPLocalizer.DialogQuestion) >= 0:
type = 'question'
elif random.randint(0, 1):
type = 'statementA'
else:
type = 'statementB'
stringLength = len(chatString)
if stringLength <= OTPLocalizer.DialogLength1:
length = 1
elif stringLength <= OTPLocalizer.DialogLength2:
length = 2
elif stringLength <= OTPLocalizer.DialogLength3:
length = 3
else:
length = 4
self.playDialogue(type, length)
def playDialogue(self, type, length):
dialogueArray = self.getDialogueArray()
if dialogueArray == None:
return
sfxIndex = None
if type == 'statementA' or type == 'statementB':
if length == 1:
sfxIndex = 0
elif length == 2:
sfxIndex = 1
elif length >= 3:
sfxIndex = 2
elif type == 'question':
sfxIndex = 3
elif type == 'exclamation':
sfxIndex = 4
elif type == 'special':
sfxIndex = 5
else:
notify.error('unrecognized dialogue type: ', type)
if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None:
base.playSfx(dialogueArray[sfxIndex], node=self)
return
def getDialogueSfx(self, type, length):
retval = None
dialogueArray = self.getDialogueArray()
if dialogueArray == None:
return
sfxIndex = None
if type == 'statementA' or type == 'statementB':
if length == 1:
sfxIndex = 0
elif length == 2:
sfxIndex = 1
elif length >= 3:
sfxIndex = 2
elif type == 'question':
sfxIndex = 3
elif type == 'exclamation':
sfxIndex = 4
elif type == 'special':
sfxIndex = 5
else:
notify.error('unrecognized dialogue type: ', type)
if sfxIndex != None and sfxIndex < len(dialogueArray) and dialogueArray[sfxIndex] != None:
retval = dialogueArray[sfxIndex]
return retval
def setChatAbsolute(self, chatString, chatFlags, dialogue = None, interrupt = 1):
self.nametag.setChat(chatString, chatFlags)
self.playCurrentDialogue(dialogue, chatFlags, interrupt)
def setChatMuted(self, chatString, chatFlags, dialogue = None, interrupt = 1, quiet = 0):
pass
def displayTalk(self, chatString):
if not base.cr.avatarFriendsManager.checkIgnored(self.doId):
if base.talkAssistant.isThought(chatString):
self.nametag.setChat(base.talkAssistant.removeThoughtPrefix(chatString), CFThought)
else:
self.nametag.setChat(chatString, CFSpeech | CFTimeout)
def clearChat(self):
self.nametag.clearChat()
def isInView(self):
pos = self.getPos(camera)
eyePos = Point3(pos[0], pos[1], pos[2] + self.getHeight())
return base.camNode.isInView(eyePos)
def getNameVisible(self):
return self.__nameVisible
def setNameVisible(self, bool):
self.__nameVisible = bool
if bool:
self.showName()
if not bool:
self.hideName()
def hideName(self):
self.nametag.getNametag3d().setContents(Nametag.CSpeech | Nametag.CThought)
def showName(self):
if self.__nameVisible and not self.ghostMode:
self.nametag.getNametag3d().setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought)
def hideNametag2d(self):
self.nametag2dContents = 0
self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist)
def showNametag2d(self):
self.nametag2dContents = self.nametag2dNormalContents
if self.ghostMode:
self.nametag2dContents = Nametag.CSpeech
self.nametag.getNametag2d().setContents(self.nametag2dContents & self.nametag2dDist)
def hideNametag3d(self):
self.nametag.getNametag3d().setContents(0)
def showNametag3d(self):
if self.__nameVisible and not self.ghostMode:
self.nametag.getNametag3d().setContents(Nametag.CName | Nametag.CSpeech | Nametag.CThought)
else:
self.nametag.getNametag3d().setContents(0)
def setPickable(self, flag):
self.nametag.setActive(flag)
def clickedNametag(self):
if self.nametag.hasButton():
self.advancePageNumber()
elif self.nametag.isActive():
messenger.send('clickedNametag', [self])
def setPageChat(self, addressee, paragraph, message, quitButton, extraChatFlags = None, dialogueList = [], pageButton = True):
self.__chatAddressee = addressee
self.__chatPageNumber = None
self.__chatParagraph = paragraph
self.__chatMessage = message
if extraChatFlags is None:
self.__chatFlags = CFSpeech
else:
self.__chatFlags = CFSpeech | extraChatFlags
self.__chatDialogueList = dialogueList
self.__chatSet = 0
self.__chatLocal = 0
self.__updatePageChat()
if addressee == base.localAvatar.doId:
if pageButton:
self.__chatFlags |= CFPageButton
if quitButton == None:
self.__chatFlags |= CFNoQuitButton
elif quitButton:
self.__chatFlags |= CFQuitButton
self.b_setPageNumber(self.__chatParagraph, 0)
return
def setLocalPageChat(self, message, quitButton, extraChatFlags = None, dialogueList = []):
self.__chatAddressee = base.localAvatar.doId
self.__chatPageNumber = None
self.__chatParagraph = None
self.__chatMessage = message
if extraChatFlags is None:
self.__chatFlags = CFSpeech
else:
self.__chatFlags = CFSpeech | extraChatFlags
self.__chatDialogueList = dialogueList
self.__chatSet = 1
self.__chatLocal = 1
self.__chatFlags |= CFPageButton
if quitButton == None:
self.__chatFlags |= CFNoQuitButton
elif quitButton:
self.__chatFlags |= CFQuitButton
if len(dialogueList) > 0:
dialogue = dialogueList[0]
else:
dialogue = None
self.clearChat()
self.setChatAbsolute(message, self.__chatFlags, dialogue)
self.setPageNumber(None, 0)
return
def setPageNumber(self, paragraph, pageNumber, timestamp = None):
if timestamp == None:
elapsed = 0.0
else:
elapsed = ClockDelta.globalClockDelta.localElapsedTime(timestamp)
self.__chatPageNumber = [paragraph, pageNumber]
self.__updatePageChat()
if hasattr(self, 'uniqueName'):
if pageNumber >= 0:
messenger.send(self.uniqueName('nextChatPage'), [pageNumber, elapsed])
else:
messenger.send(self.uniqueName('doneChatPage'), [elapsed])
elif pageNumber >= 0:
messenger.send('nextChatPage', [pageNumber, elapsed])
else:
messenger.send('doneChatPage', [elapsed])
return
def advancePageNumber(self):
if self.__chatAddressee == base.localAvatar.doId and self.__chatPageNumber != None and self.__chatPageNumber[0] == self.__chatParagraph:
pageNumber = self.__chatPageNumber[1]
if pageNumber >= 0:
pageNumber += 1
if pageNumber >= self.nametag.getNumChatPages():
pageNumber = -1
if self.__chatLocal:
self.setPageNumber(self.__chatParagraph, pageNumber)
else:
self.b_setPageNumber(self.__chatParagraph, pageNumber)
return
def __updatePageChat(self):
if self.__chatPageNumber != None and self.__chatPageNumber[0] == self.__chatParagraph:
pageNumber = self.__chatPageNumber[1]
if pageNumber >= 0:
if not self.__chatSet:
if len(self.__chatDialogueList) > 0:
dialogue = self.__chatDialogueList[0]
else:
dialogue = None
self.setChatAbsolute(self.__chatMessage, self.__chatFlags, dialogue)
self.__chatSet = 1
if pageNumber < self.nametag.getNumChatPages():
self.nametag.setPageNumber(pageNumber)
if pageNumber > 0:
if len(self.__chatDialogueList) > pageNumber:
dialogue = self.__chatDialogueList[pageNumber]
else:
dialogue = None
self.playCurrentDialogue(dialogue, self.__chatFlags)
else:
self.clearChat()
else:
self.clearChat()
return
def getAirborneHeight(self):
height = self.getPos(self.shadowPlacer.shadowNodePath)
return height.getZ() + 0.025
def initializeNametag3d(self):
self.deleteNametag3d()
nametagNode = self.nametag.getNametag3d()
self.nametagNodePath = self.nametag3d.attachNewNode(nametagNode)
iconNodePath = self.nametag.getNameIcon()
for cJoint in self.getNametagJoints():
cJoint.clearNetTransforms()
cJoint.addNetTransform(nametagNode)
def nametagAmbientLightChanged(self, newlight):
self.nametag3d.setLightOff()
if newlight:
self.nametag3d.setLight(newlight)
def deleteNametag3d(self):
if self.nametagNodePath:
self.nametagNodePath.removeNode()
self.nametagNodePath = None
return
def initializeBodyCollisions(self, collIdStr):
self.collTube = CollisionTube(0, 0, 0.5, 0, 0, self.height - self.getRadius(), self.getRadius())
self.collNode = CollisionNode(collIdStr)
self.collNode.addSolid(self.collTube)
self.collNodePath = self.attachNewNode(self.collNode)
if self.ghostMode:
self.collNode.setCollideMask(OTPGlobals.GhostBitmask)
else:
self.collNode.setCollideMask(OTPGlobals.WallBitmask)
def stashBodyCollisions(self):
if hasattr(self, 'collNodePath'):
self.collNodePath.stash()
def unstashBodyCollisions(self):
if hasattr(self, 'collNodePath'):
self.collNodePath.unstash()
def disableBodyCollisions(self):
if hasattr(self, 'collNodePath'):
self.collNodePath.removeNode()
del self.collNodePath
self.collTube = None
return
def addActive(self):
if base.wantNametags:
try:
Avatar.ActiveAvatars.remove(self)
except ValueError:
pass
Avatar.ActiveAvatars.append(self)
self.nametag.manage(base.marginManager)
self.accept(self.nametag.getUniqueId(), self.clickedNametag)
def removeActive(self):
if base.wantNametags:
try:
Avatar.ActiveAvatars.remove(self)
except ValueError:
pass
self.nametag.unmanage(base.marginManager)
self.ignore(self.nametag.getUniqueId())
def loop(self, animName, restart = 1, partName = None, fromFrame = None, toFrame = None):
return Actor.loop(self, animName, restart, partName, fromFrame, toFrame)