2015-03-03 22:10:12 +00:00
|
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
|
|
from direct.fsm import ClassicFSM
|
|
|
|
from direct.fsm import State
|
|
|
|
from direct.gui.DirectGui import *
|
|
|
|
from direct.showbase import DirectObject
|
|
|
|
from pandac.PandaModules import *
|
|
|
|
from otp.otpbase import OTPLocalizer
|
|
|
|
from toontown.chat.ChatGlobals import *
|
|
|
|
|
|
|
|
|
|
|
|
ChatEvent = 'ChatEvent'
|
|
|
|
NormalChatEvent = 'NormalChatEvent'
|
|
|
|
SCChatEvent = 'SCChatEvent'
|
|
|
|
SCCustomChatEvent = 'SCCustomChatEvent'
|
|
|
|
SCEmoteChatEvent = 'SCEmoteChatEvent'
|
|
|
|
OnScreen = 0
|
|
|
|
OffScreen = 1
|
|
|
|
Thought = 2
|
|
|
|
ThoughtPrefix = '.'
|
|
|
|
|
|
|
|
def isThought(message):
|
|
|
|
if len(message) == 0:
|
|
|
|
return 0
|
|
|
|
elif message.find(ThoughtPrefix, 0, len(ThoughtPrefix)) >= 0:
|
|
|
|
return 1
|
|
|
|
else:
|
|
|
|
return 0
|
|
|
|
|
|
|
|
|
|
|
|
def removeThoughtPrefix(message):
|
|
|
|
if isThought(message):
|
|
|
|
return message[len(ThoughtPrefix):]
|
|
|
|
else:
|
|
|
|
return message
|
|
|
|
|
|
|
|
|
|
|
|
class ChatManager(DirectObject.DirectObject):
|
|
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('ChatManager')
|
|
|
|
|
|
|
|
def __init__(self, cr, localAvatar):
|
|
|
|
self.cr = cr
|
|
|
|
self.localAvatar = localAvatar
|
|
|
|
self.wantBackgroundFocus = 1
|
|
|
|
self.__scObscured = 0
|
|
|
|
self.__normalObscured = 0
|
|
|
|
self.openChatWarning = None
|
|
|
|
self.unpaidChatWarning = None
|
|
|
|
self.paidNoParentPassword = None
|
|
|
|
self.noSecretChatAtAll = None
|
|
|
|
self.noSecretChatAtAllAndNoWhitelist = None
|
|
|
|
self.noSecretChatWarning = None
|
|
|
|
self.activateChatGui = None
|
|
|
|
self.secretChatActivated = None
|
|
|
|
self.problemActivatingChat = None
|
|
|
|
self.fsm = ClassicFSM.ClassicFSM('chatManager', [State.State('off', self.enterOff, self.exitOff),
|
|
|
|
State.State('mainMenu', self.enterMainMenu, self.exitMainMenu),
|
|
|
|
State.State('speedChat', self.enterSpeedChat, self.exitSpeedChat),
|
|
|
|
State.State('normalChat', self.enterNormalChat, self.exitNormalChat),
|
|
|
|
State.State('whisper', self.enterWhisper, self.exitWhisper),
|
|
|
|
State.State('whisperChat', self.enterWhisperChat, self.exitWhisperChat),
|
|
|
|
State.State('whisperChatPlayer', self.enterWhisperChatPlayer, self.exitWhisperChatPlayer),
|
|
|
|
State.State('whisperSpeedChat', self.enterWhisperSpeedChat, self.exitWhisperSpeedChat),
|
|
|
|
State.State('whisperSpeedChatPlayer', self.enterWhisperSpeedChatPlayer, self.exitWhisperSpeedChatPlayer),
|
|
|
|
State.State('openChatWarning', self.enterOpenChatWarning, self.exitOpenChatWarning),
|
|
|
|
State.State('unpaidChatWarning', self.enterUnpaidChatWarning, self.exitUnpaidChatWarning),
|
|
|
|
State.State('noSecretChatAtAll', self.enterNoSecretChatAtAll, self.exitNoSecretChatAtAll),
|
|
|
|
State.State('noSecretChatAtAllAndNoWhitelist', self.enterNoSecretChatAtAllAndNoWhitelist, self.exitNoSecretChatAtAllAndNoWhitelist),
|
|
|
|
State.State('noSecretChatWarning', self.enterNoSecretChatWarning, self.exitNoSecretChatWarning),
|
|
|
|
State.State('noFriendsWarning', self.enterNoFriendsWarning, self.exitNoFriendsWarning),
|
|
|
|
State.State('otherDialog', self.enterOtherDialog, self.exitOtherDialog),
|
|
|
|
State.State('activateChat', self.enterActivateChat, self.exitActivateChat),
|
|
|
|
State.State('secretChatActivated', self.enterSecretChatActivated, self.exitSecretChatActivated),
|
|
|
|
State.State('problemActivatingChat', self.enterProblemActivatingChat, self.exitProblemActivatingChat),
|
|
|
|
State.State('whiteListOpenChat', self.enterWhiteListOpenChat, self.exitWhiteListOpenChat),
|
|
|
|
State.State('whiteListAvatarChat', self.enterWhiteListAvatarChat, self.exitWhiteListAvatarChat),
|
2015-04-01 20:29:34 +00:00
|
|
|
State.State('whiteListPlayerChat', self.enterWhiteListPlayerChat, self.exitWhiteListPlayerChat)], 'off', 'off')
|
2015-03-03 22:10:12 +00:00
|
|
|
self.fsm.enterInitialState()
|
|
|
|
return
|
|
|
|
|
|
|
|
def delete(self):
|
|
|
|
self.ignoreAll()
|
|
|
|
del self.fsm
|
|
|
|
if hasattr(self.chatInputNormal, 'destroy'):
|
|
|
|
self.chatInputNormal.destroy()
|
|
|
|
self.chatInputNormal.delete()
|
|
|
|
del self.chatInputNormal
|
|
|
|
self.chatInputSpeedChat.delete()
|
|
|
|
del self.chatInputSpeedChat
|
|
|
|
if self.openChatWarning:
|
|
|
|
self.openChatWarning.destroy()
|
|
|
|
self.openChatWarning = None
|
|
|
|
if self.unpaidChatWarning:
|
|
|
|
self.payButton = None
|
|
|
|
self.unpaidChatWarning.destroy()
|
|
|
|
self.unpaidChatWarning = None
|
|
|
|
if self.noSecretChatAtAll:
|
|
|
|
self.noSecretChatAtAll.destroy()
|
|
|
|
self.noSecretChatAtAll = None
|
|
|
|
if self.noSecretChatAtAllAndNoWhitelist:
|
|
|
|
self.noSecretChatAtAllAndNoWhitelist.destroy()
|
|
|
|
self.noSecretChatAtAllAndNoWhitelist = None
|
|
|
|
if self.noSecretChatWarning:
|
|
|
|
self.noSecretChatWarning.destroy()
|
|
|
|
self.noSecretChatWarning = None
|
|
|
|
if self.activateChatGui:
|
|
|
|
self.activateChatGui.destroy()
|
|
|
|
self.activateChatGui = None
|
|
|
|
if self.secretChatActivated:
|
|
|
|
self.secretChatActivated.destroy()
|
|
|
|
self.secretChatActivated = None
|
|
|
|
if self.problemActivatingChat:
|
|
|
|
self.problemActivatingChat.destroy()
|
|
|
|
self.problemActivatingChat = None
|
|
|
|
del self.localAvatar
|
|
|
|
del self.cr
|
|
|
|
return
|
|
|
|
|
|
|
|
def obscure(self, normal, sc):
|
|
|
|
self.__scObscured = sc
|
|
|
|
if self.__scObscured:
|
|
|
|
self.scButton.hide()
|
|
|
|
self.__normalObscured = normal
|
|
|
|
if self.__normalObscured:
|
|
|
|
self.normalButton.hide()
|
|
|
|
|
|
|
|
def isObscured(self):
|
|
|
|
return (self.__normalObscured, self.__scObscured)
|
|
|
|
|
|
|
|
def stop(self):
|
|
|
|
self.fsm.request('off')
|
|
|
|
self.ignoreAll()
|
|
|
|
|
|
|
|
def start(self):
|
|
|
|
self.fsm.request('mainMenu')
|
|
|
|
|
|
|
|
def announceChat(self):
|
|
|
|
messenger.send(ChatEvent)
|
|
|
|
|
|
|
|
def announceSCChat(self):
|
|
|
|
messenger.send(SCChatEvent)
|
|
|
|
self.announceChat()
|
|
|
|
|
|
|
|
def sendChatString(self, message):
|
|
|
|
chatFlags = CFSpeech | CFTimeout
|
|
|
|
if base.cr.wantSwitchboardHacks:
|
|
|
|
from otp.switchboard import badwordpy
|
|
|
|
badwordpy.init('', '')
|
|
|
|
message = badwordpy.scrub(message)
|
|
|
|
if isThought(message):
|
|
|
|
message = removeThoughtPrefix(message)
|
|
|
|
chatFlags = CFThought
|
|
|
|
messenger.send(NormalChatEvent)
|
|
|
|
self.announceChat()
|
|
|
|
|
|
|
|
def sendWhisperString(self, message, whisperAvatarId):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def sendSCChatMessage(self, msgIndex):
|
|
|
|
base.talkAssistant.sendOpenSpeedChat(1, msgIndex)
|
|
|
|
|
|
|
|
def sendSCWhisperMessage(self, msgIndex, whisperAvatarId, toPlayer):
|
|
|
|
if toPlayer:
|
|
|
|
base.talkAssistant.sendPlayerWhisperSpeedChat(1, msgIndex, whisperAvatarId)
|
|
|
|
else:
|
|
|
|
base.talkAssistant.sendAvatarWhisperSpeedChat(1, msgIndex, whisperAvatarId)
|
|
|
|
|
|
|
|
def sendSCCustomChatMessage(self, msgIndex):
|
|
|
|
base.talkAssistant.sendOpenSpeedChat(3, msgIndex)
|
|
|
|
|
|
|
|
def sendSCCustomWhisperMessage(self, msgIndex, whisperAvatarId, toPlayer):
|
|
|
|
if toPlayer:
|
|
|
|
base.talkAssistant.sendPlayerWhisperSpeedChat(3, msgIndex, whisperAvatarId)
|
|
|
|
else:
|
|
|
|
base.talkAssistant.sendAvatarWhisperSpeedChat(3, msgIndex, whisperAvatarId)
|
|
|
|
|
|
|
|
def sendSCEmoteChatMessage(self, emoteId):
|
|
|
|
base.talkAssistant.sendOpenSpeedChat(2, emoteId)
|
|
|
|
|
|
|
|
def sendSCEmoteWhisperMessage(self, emoteId, whisperAvatarId, toPlayer):
|
|
|
|
if toPlayer:
|
|
|
|
base.talkAssistant.sendPlayerWhisperSpeedChat(2, emoteId, whisperAvatarId)
|
|
|
|
else:
|
|
|
|
base.talkAssistant.sendAvatarWhisperSpeedChat(2, emoteId, whisperAvatarId)
|
|
|
|
|
|
|
|
def enterOff(self):
|
|
|
|
self.scButton.hide()
|
|
|
|
self.normalButton.hide()
|
|
|
|
self.ignoreAll()
|
|
|
|
|
|
|
|
def exitOff(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def enterMainMenu(self):
|
|
|
|
self.checkObscurred()
|
|
|
|
if self.localAvatar.canChat() or self.cr.wantMagicWords:
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 1
|
|
|
|
self.acceptOnce('enterNormalChat', self.fsm.request, ['normalChat'])
|
|
|
|
|
|
|
|
def checkObscurred(self):
|
|
|
|
if not self.__scObscured:
|
|
|
|
self.scButton.show()
|
|
|
|
if not self.__normalObscured:
|
|
|
|
self.normalButton.show()
|
|
|
|
|
|
|
|
def exitMainMenu(self):
|
|
|
|
self.scButton.hide()
|
|
|
|
self.normalButton.hide()
|
|
|
|
self.ignore('enterNormalChat')
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 0
|
|
|
|
|
|
|
|
def whisperTo(self, avatarName, avatarId, playerId = None):
|
|
|
|
self.fsm.request('whisper', [avatarName, avatarId, playerId])
|
|
|
|
|
|
|
|
def noWhisper(self):
|
|
|
|
self.fsm.request('mainMenu')
|
|
|
|
|
|
|
|
def handleWhiteListSelect(self):
|
|
|
|
self.fsm.request('whiteListOpenChat')
|
|
|
|
|
|
|
|
def enterWhiteListOpenChat(self):
|
|
|
|
self.checkObscurred()
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 0
|
|
|
|
base.localAvatar.chatMgr.chatInputWhiteList.activateByData()
|
|
|
|
|
|
|
|
def exitWhiteListOpenChat(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def enterWhiteListAvatarChat(self, receiverId):
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 0
|
|
|
|
base.localAvatar.chatMgr.chatInputWhiteList.activateByData(receiverId, 0)
|
|
|
|
|
|
|
|
def exitWhiteListAvatarChat(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def enterWhiteListPlayerChat(self, receiverId):
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 0
|
|
|
|
base.localAvatar.chatMgr.chatInputWhiteList.activateByData(receiverId, 1)
|
|
|
|
|
|
|
|
def exitWhiteListPlayerChat(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def enterWhisper(self, avatarName, avatarId, playerId = None):
|
|
|
|
self.whisperScButton['extraArgs'] = [avatarName, avatarId, playerId]
|
|
|
|
self.whisperButton['extraArgs'] = [avatarName, avatarId, playerId]
|
|
|
|
playerName = None
|
|
|
|
chatToToon = 1
|
|
|
|
online = 0
|
|
|
|
if avatarId in self.cr.doId2do:
|
|
|
|
online = 1
|
|
|
|
elif self.cr.isFriend(avatarId):
|
|
|
|
online = self.cr.isFriendOnline(avatarId)
|
|
|
|
hasManager = hasattr(base.cr, 'playerFriendsManager')
|
|
|
|
if hasManager:
|
|
|
|
if base.cr.playerFriendsManager.askAvatarOnline(avatarId):
|
|
|
|
online = 1
|
|
|
|
avatarUnderstandable = 0
|
|
|
|
playerUnderstandable = 0
|
|
|
|
av = None
|
|
|
|
if avatarId:
|
|
|
|
av = self.cr.identifyAvatar(avatarId)
|
|
|
|
if av != None:
|
|
|
|
avatarUnderstandable = av.isUnderstandable()
|
|
|
|
if playerId:
|
|
|
|
if playerId in base.cr.playerFriendsManager.playerId2Info:
|
|
|
|
playerInfo = base.cr.playerFriendsManager.playerId2Info.get(playerId)
|
|
|
|
playerName = playerInfo.playerName
|
|
|
|
online = 1
|
|
|
|
playerUnderstandable = playerInfo.understandableYesNo
|
|
|
|
if playerUnderstandable or not avatarId:
|
|
|
|
chatToToon = 0
|
|
|
|
if chatToToon:
|
|
|
|
chatName = avatarName
|
|
|
|
else:
|
|
|
|
chatName = playerName
|
|
|
|
normalButtonObscured, scButtonObscured = self.isObscured()
|
|
|
|
if (avatarUnderstandable or playerUnderstandable) and online and not normalButtonObscured:
|
|
|
|
self.whisperButton['state'] = 'normal'
|
|
|
|
self.enablewhisperButton()
|
|
|
|
else:
|
|
|
|
self.whisperButton['state'] = 'inactive'
|
|
|
|
self.disablewhisperButton()
|
|
|
|
if online:
|
|
|
|
self.whisperScButton['state'] = 'normal'
|
|
|
|
self.whisperButton['state'] = 'normal'
|
|
|
|
self.changeFrameText(OTPLocalizer.ChatManagerWhisperToName % chatName)
|
|
|
|
else:
|
|
|
|
self.whisperScButton['state'] = 'inactive'
|
|
|
|
self.whisperButton['state'] = 'inactive'
|
|
|
|
self.changeFrameText(OTPLocalizer.ChatManagerWhisperOffline % chatName)
|
|
|
|
self.whisperFrame.show()
|
|
|
|
self.refreshWhisperFrame()
|
|
|
|
if avatarUnderstandable or playerUnderstandable:
|
|
|
|
if playerId and not chatToToon:
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 1
|
|
|
|
self.acceptOnce('enterNormalChat', self.fsm.request, ['whisperChatPlayer', [avatarName, playerId]])
|
|
|
|
elif online and chatToToon:
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 1
|
|
|
|
self.acceptOnce('enterNormalChat', self.fsm.request, ['whisperChat', [avatarName, avatarId]])
|
|
|
|
return
|
|
|
|
|
|
|
|
def disablewhisperButton(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def enablewhisperButton(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def refreshWhisperFrame(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def changeFrameText(self, newText):
|
|
|
|
self.whisperFrame['text'] = newText
|
|
|
|
|
|
|
|
def exitWhisper(self):
|
|
|
|
self.whisperFrame.hide()
|
|
|
|
self.ignore('enterNormalChat')
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 0
|
|
|
|
|
|
|
|
def enterWhisperSpeedChat(self, avatarId):
|
|
|
|
self.whisperFrame.show()
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 0
|
|
|
|
self.chatInputSpeedChat.show(avatarId)
|
|
|
|
|
|
|
|
def exitWhisperSpeedChat(self):
|
|
|
|
self.whisperFrame.hide()
|
|
|
|
self.chatInputSpeedChat.hide()
|
|
|
|
|
|
|
|
def enterWhisperSpeedChatPlayer(self, playerId):
|
|
|
|
self.whisperFrame.show()
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 0
|
|
|
|
self.chatInputSpeedChat.show(playerId, 1)
|
|
|
|
|
|
|
|
def exitWhisperSpeedChatPlayer(self):
|
|
|
|
self.whisperFrame.hide()
|
|
|
|
self.chatInputSpeedChat.hide()
|
|
|
|
|
|
|
|
def enterWhisperChat(self, avatarName, avatarId):
|
|
|
|
result = self.chatInputNormal.activateByData(avatarId)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def exitWhisperChat(self):
|
|
|
|
self.chatInputNormal.deactivate()
|
|
|
|
|
|
|
|
def enterWhisperChatPlayer(self, avatarName, playerId):
|
|
|
|
playerInfo = base.cr.playerFriendsManager.getFriendInfo(playerId)
|
|
|
|
if playerInfo:
|
|
|
|
avatarName = playerInfo.playerName
|
|
|
|
result = self.chatInputNormal.activateByData(playerId, 1)
|
|
|
|
return result
|
|
|
|
|
|
|
|
def exitWhisperChatPlayer(self):
|
|
|
|
self.chatInputNormal.deactivate()
|
|
|
|
|
|
|
|
def enterSpeedChat(self):
|
|
|
|
messenger.send('enterSpeedChat')
|
|
|
|
if not self.__scObscured:
|
|
|
|
self.scButton.show()
|
|
|
|
if not self.__normalObscured:
|
|
|
|
self.normalButton.show()
|
|
|
|
if self.wantBackgroundFocus:
|
|
|
|
self.chatInputNormal.chatEntry['backgroundFocus'] = 0
|
|
|
|
self.chatInputSpeedChat.show()
|
|
|
|
|
|
|
|
def exitSpeedChat(self):
|
|
|
|
self.scButton.hide()
|
|
|
|
self.normalButton.hide()
|
|
|
|
self.chatInputSpeedChat.hide()
|
|
|
|
|
|
|
|
def enterNormalChat(self):
|
|
|
|
result = self.chatInputNormal.activateByData()
|
|
|
|
return result
|
|
|
|
|
|
|
|
def exitNormalChat(self):
|
|
|
|
self.chatInputNormal.deactivate()
|
|
|
|
|
|
|
|
def enterOpenChatWarning(self):
|
|
|
|
self.notify.error('called enterOpenChatWarning() on parent class')
|
|
|
|
|
|
|
|
def exitOpenChatWarning(self):
|
|
|
|
self.notify.error('called exitOpenChatWarning() on parent class')
|
|
|
|
|
|
|
|
def enterUnpaidChatWarning(self):
|
|
|
|
self.notify.error('called enterUnpaidChatWarning() on parent class')
|
|
|
|
|
|
|
|
def exitUnpaidChatWarning(self):
|
|
|
|
self.notify.error('called exitUnpaidChatWarning() on parent class')
|
|
|
|
|
|
|
|
def enterNoSecretChatAtAll(self):
|
|
|
|
self.notify.error('called enterNoSecretChatAtAll() on parent class')
|
|
|
|
|
|
|
|
def exitNoSecretChatAtAll(self):
|
|
|
|
self.notify.error('called exitNoSecretChatAtAll() on parent class')
|
|
|
|
|
|
|
|
def enterNoSecretChatAtAllAndNoWhitelist(self):
|
|
|
|
self.notify.error('called enterNoSecretChatAtAllAndNoWhitelist() on parent class')
|
|
|
|
|
|
|
|
def exitNoSecretChatAtAllAndNoWhitelist(self):
|
|
|
|
self.notify.error('called exitNoSecretChatAtAllAndNoWhitelist() on parent class')
|
|
|
|
|
|
|
|
def enterNoSecretChatWarning(self):
|
|
|
|
self.notify.error('called enterNoSecretChatWarning() on parent class')
|
|
|
|
|
|
|
|
def exitNoSecretChatWarning(self):
|
|
|
|
self.notify.error('called exitNoSecretChatWarning() on parent class')
|
|
|
|
|
|
|
|
def enterNoFriendsWarning(self):
|
|
|
|
self.notify.error('called enterNoFriendsWarning() on parent class')
|
|
|
|
|
|
|
|
def exitNoFriendsWarning(self):
|
|
|
|
self.notify.error('called exitNoFriendsWarning() on parent class')
|
|
|
|
|
|
|
|
def enterActivateChat(self):
|
|
|
|
self.notify.error('called enterActivateChat() on parent class')
|
|
|
|
|
|
|
|
def exitActivateChat(self):
|
|
|
|
self.notify.error('called exitActivateChat() on parent class')
|
|
|
|
|
|
|
|
def enterOtherDialog(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def exitOtherDialog(self):
|
|
|
|
pass
|
|
|
|
|
|
|
|
def enterSecretChatActivated(self):
|
|
|
|
self.notify.error('called enterSecretChatActivated() on parent class')
|
|
|
|
|
|
|
|
def exitSecretChatActivated(self):
|
|
|
|
self.notify.error('called exitSecretChatActivated() on parent class')
|
|
|
|
|
|
|
|
def enterProblemActivatingChat(self):
|
|
|
|
self.notify.error('called enterProblemActivatingChat() on parent class')
|
|
|
|
|
|
|
|
def exitProblemActivatingChat(self):
|
2015-04-01 20:29:34 +00:00
|
|
|
self.notify.error('called exitProblemActivatingChat() on parent class')
|