985 lines
34 KiB
Python
985 lines
34 KiB
Python
from panda3d.core import *
|
|
from direct.showbase.PythonUtil import weightedChoice, randFloat, lerp
|
|
from direct.showbase.PythonUtil import contains, list2dict
|
|
from toontown.toonbase.PythonUtil import clampScalar
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from direct.distributed import DistributedSmoothNodeAI
|
|
from direct.distributed import DistributedSmoothNodeBase
|
|
from direct.distributed import ClockDelta
|
|
from direct.fsm import ClassicFSM, State
|
|
from direct.interval.IntervalGlobal import *
|
|
from toontown.toonbase import ToontownGlobals
|
|
from direct.task import Task
|
|
from toontown.pets import PetLookerAI
|
|
from toontown.pets import PetConstants, PetDNA, PetTraits
|
|
from toontown.pets import PetObserve, PetBrain, PetMood
|
|
from toontown.pets import PetActionFSM, PetBase, PetGoal, PetTricks
|
|
from direct.fsm import FSM
|
|
from toontown.toon import DistributedToonAI
|
|
import random
|
|
import time
|
|
import string
|
|
import copy
|
|
from direct.showbase.PythonUtil import StackTrace
|
|
|
|
from PetMoverAI import PetMoverAI
|
|
|
|
class DistributedPetAI(DistributedSmoothNodeAI.DistributedSmoothNodeAI, PetLookerAI.PetLookerAI, PetBase.PetBase):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPetAI')
|
|
movieTimeSwitch = {PetConstants.PET_MOVIE_FEED: PetConstants.FEED_TIME,
|
|
PetConstants.PET_MOVIE_SCRATCH: PetConstants.SCRATCH_TIME,
|
|
PetConstants.PET_MOVIE_CALL: PetConstants.CALL_TIME}
|
|
movieDistSwitch = {PetConstants.PET_MOVIE_FEED: PetConstants.FEED_DIST.get,
|
|
PetConstants.PET_MOVIE_SCRATCH: PetConstants.SCRATCH_DIST.get}
|
|
|
|
def __init__(self, air, dna = None):
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.__init__(self, air)
|
|
PetLookerAI.PetLookerAI.__init__(self)
|
|
self.ownerId = 0
|
|
self.petName = 'unnamed'
|
|
self.traitSeed = 0
|
|
self.safeZone = ToontownGlobals.ToontownCentral
|
|
self.initialDNA = dna
|
|
self.active = 1
|
|
self.activated = 0
|
|
self._outOfBounds = False
|
|
self.traitList = [0] * PetTraits.PetTraits.NumTraits
|
|
self.head = -1
|
|
self.ears = -1
|
|
self.nose = -1
|
|
self.tail = -1
|
|
self.bodyTexture = 0
|
|
self.color = 0
|
|
self.colorScale = 0
|
|
self.eyeColor = 0
|
|
self.gender = 0
|
|
self.movieMode = None
|
|
self.lockMoverEnabled = 0
|
|
self.trickAptitudes = []
|
|
self.inEstate = 0
|
|
self.estateOwnerId = None
|
|
self.estateZones = []
|
|
self.lastSeenTimestamp = self.getCurEpochTimestamp()
|
|
self.requiredMoodComponents = {}
|
|
self.__funcsToDelete = []
|
|
self.__generateDistTraitFuncs()
|
|
self.__generateDistMoodFuncs()
|
|
self.busy = 0
|
|
self.gaitFSM = ClassicFSM.ClassicFSM('petGaitFSM', [State.State('off', self.gaitEnterOff, self.gaitExitOff),
|
|
State.State('neutral', self.gaitEnterNeutral, self.gaitExitNeutral),
|
|
State.State('happy', self.gaitEnterHappy, self.gaitExitHappy),
|
|
State.State('sad', self.gaitEnterSad, self.gaitExitSad)], 'off', 'off')
|
|
self.gaitFSM.enterInitialState()
|
|
self.unstickFSM = ClassicFSM.ClassicFSM('unstickFSM', [State.State('off', self.unstickEnterOff, self.unstickExitOff), State.State('on', self.unstickEnterOn, self.unstickExitOn)], 'off', 'off')
|
|
self.unstickFSM.enterInitialState()
|
|
return
|
|
|
|
def setInactive(self):
|
|
self.active = 0
|
|
|
|
def _initDBVals(self, ownerId, name = None, traitSeed = 0, dna = None, safeZone = ToontownGlobals.ToontownCentral):
|
|
self.b_setOwnerId(ownerId)
|
|
if name is None:
|
|
name = 'pet%s' % self.doId
|
|
self.b_setPetName(name)
|
|
self.b_setTraitSeed(traitSeed)
|
|
self.b_setSafeZone(safeZone)
|
|
traits = PetTraits.PetTraits(traitSeed, safeZone)
|
|
for traitName in PetTraits.getTraitNames():
|
|
setter = self.getSetterName(traitName, 'b_set')
|
|
self.__dict__[setter](traits.getTraitValue(traitName))
|
|
|
|
self.traits = traits
|
|
for component in PetMood.PetMood.Components:
|
|
setterName = self.getSetterName(component, 'b_set')
|
|
self.__dict__[setterName](0.0)
|
|
|
|
if not dna:
|
|
dna = PetDNA.getRandomPetDNA()
|
|
self.setDNA(dna)
|
|
self.b_setLastSeenTimestamp(self.getCurEpochTimestamp())
|
|
for component in PetMood.PetMood.Components:
|
|
self.setMoodComponent(component, 0.0)
|
|
|
|
self.b_setTrickAptitudes([])
|
|
return
|
|
|
|
def setDNA(self, dna):
|
|
head, ears, nose, tail, body, color, colorScale, eyes, gender = dna
|
|
self.b_setHead(head)
|
|
self.b_setEars(ears)
|
|
self.b_setNose(nose)
|
|
self.b_setTail(tail)
|
|
self.b_setBodyTexture(body)
|
|
self.b_setColor(color)
|
|
self.b_setColorScale(colorScale)
|
|
self.b_setEyeColor(eyes)
|
|
self.b_setGender(gender)
|
|
|
|
def handleZoneChange(self, newZoneId, oldZoneId):
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.handleZoneChange(self, newZoneId, oldZoneId)
|
|
self.ignore(PetObserve.getEventName(oldZoneId))
|
|
self.accept(PetObserve.getEventName(newZoneId), self.brain.observe)
|
|
|
|
def handleLogicalZoneChange(self, newZoneId, oldZoneId):
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.handleLogicalZoneChange(self, newZoneId, oldZoneId)
|
|
self.announceZoneChange(newZoneId, oldZoneId)
|
|
|
|
def announceZoneChange(self, newZoneId, oldZoneId):
|
|
DistributedPetAI.notify.debug('%s.announceZoneChange: %s->%s' % (self.doId, oldZoneId, newZoneId))
|
|
broadcastZones = list2dict([newZoneId, oldZoneId])
|
|
self.estateOwnerId = simbase.air.estateManager.getOwnerFromZone(newZoneId)
|
|
if self.estateOwnerId:
|
|
self.inEstate = 1
|
|
self.estateZones = simbase.air.estateManager.getEstateZones(self.estateOwnerId)
|
|
else:
|
|
self.inEstate = 0
|
|
self.estateZones = []
|
|
PetObserve.send(broadcastZones.keys(), PetObserve.PetActionObserve(PetObserve.Actions.CHANGE_ZONE, self.doId, (oldZoneId, newZoneId)))
|
|
|
|
def getOwnerId(self):
|
|
return self.ownerId
|
|
|
|
def b_setOwnerId(self, ownerId):
|
|
self.d_setOwnerId(ownerId)
|
|
self.setOwnerId(ownerId)
|
|
|
|
def d_setOwnerId(self, ownerId):
|
|
self.sendUpdate('setOwnerId', [ownerId])
|
|
|
|
def setOwnerId(self, ownerId):
|
|
self.ownerId = ownerId
|
|
|
|
def getPetName(self):
|
|
return self.petName
|
|
|
|
def b_setPetName(self, petName):
|
|
self.d_setPetName(petName)
|
|
self.setPetName(petName)
|
|
|
|
def d_setPetName(self, petName):
|
|
self.sendUpdate('setPetName', [petName])
|
|
|
|
def setPetName(self, petName):
|
|
self.petName = petName
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.setName(self, self.petName)
|
|
|
|
def getTraitSeed(self):
|
|
return self.traitSeed
|
|
|
|
def b_setTraitSeed(self, traitSeed):
|
|
self.d_setTraitSeed(traitSeed)
|
|
self.setTraitSeed(traitSeed)
|
|
|
|
def d_setTraitSeed(self, traitSeed):
|
|
self.sendUpdate('setTraitSeed', [traitSeed])
|
|
|
|
def setTraitSeed(self, traitSeed):
|
|
self.traitSeed = traitSeed
|
|
|
|
def getSafeZone(self):
|
|
return self.safeZone
|
|
|
|
def b_setSafeZone(self, safeZone):
|
|
self.d_setSafeZone(safeZone)
|
|
self.setSafeZone(safeZone)
|
|
|
|
def d_setSafeZone(self, safeZone):
|
|
self.sendUpdate('setSafeZone', [safeZone])
|
|
|
|
def setSafeZone(self, safeZone):
|
|
self.safeZone = safeZone
|
|
|
|
def getPetName(self):
|
|
return self.petName
|
|
|
|
def b_setPetName(self, petName):
|
|
self.d_setPetName(petName)
|
|
self.setPetName(petName)
|
|
|
|
def d_setPetName(self, petName):
|
|
self.sendUpdate('setPetName', [petName])
|
|
|
|
def setPetName(self, petName):
|
|
self.petName = petName
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.setName(self, self.petName)
|
|
|
|
def setTraits(self, traitList):
|
|
self.traitList = traitList
|
|
|
|
def __generateDistTraitFuncs(self):
|
|
for i in xrange(PetTraits.PetTraits.NumTraits):
|
|
traitName = PetTraits.getTraitNames()[i]
|
|
getterName = self.getSetterName(traitName, 'get')
|
|
b_setterName = self.getSetterName(traitName, 'b_set')
|
|
d_setterName = self.getSetterName(traitName, 'd_set')
|
|
setterName = self.getSetterName(traitName)
|
|
|
|
def traitGetter(i = i):
|
|
return self.traitList[i]
|
|
|
|
def b_traitSetter(value, setterName = setterName, d_setterName = d_setterName):
|
|
self.__dict__[d_setterName](value)
|
|
self.__dict__[setterName](value)
|
|
|
|
def d_traitSetter(value, setterName = setterName):
|
|
self.sendUpdate(setterName, [value])
|
|
|
|
def traitSetter(value, i = i):
|
|
self.traitList[i] = value
|
|
|
|
self.__dict__[getterName] = traitGetter
|
|
self.__dict__[b_setterName] = b_traitSetter
|
|
self.__dict__[d_setterName] = d_traitSetter
|
|
self.__dict__[setterName] = traitSetter
|
|
self.__funcsToDelete.append(getterName)
|
|
self.__funcsToDelete.append(b_setterName)
|
|
self.__funcsToDelete.append(d_setterName)
|
|
self.__funcsToDelete.append(setterName)
|
|
|
|
def getHead(self):
|
|
return self.head
|
|
|
|
def b_setHead(self, head):
|
|
self.d_setHead(head)
|
|
self.setHead(head)
|
|
|
|
def d_setHead(self, head):
|
|
self.sendUpdate('setHead', [head])
|
|
|
|
def setHead(self, head):
|
|
self.head = head
|
|
|
|
def getEars(self):
|
|
return self.ears
|
|
|
|
def b_setEars(self, ears):
|
|
self.d_setEars(ears)
|
|
self.setEars(ears)
|
|
|
|
def d_setEars(self, ears):
|
|
self.sendUpdate('setEars', [ears])
|
|
|
|
def setEars(self, ears):
|
|
self.ears = ears
|
|
|
|
def getNose(self):
|
|
return self.nose
|
|
|
|
def b_setNose(self, nose):
|
|
self.d_setNose(nose)
|
|
self.setNose(nose)
|
|
|
|
def d_setNose(self, nose):
|
|
self.sendUpdate('setNose', [nose])
|
|
|
|
def setNose(self, nose):
|
|
self.nose = nose
|
|
|
|
def getTail(self):
|
|
return self.tail
|
|
|
|
def b_setTail(self, tail):
|
|
self.d_setTail(tail)
|
|
self.setTail(tail)
|
|
|
|
def d_setTail(self, tail):
|
|
self.sendUpdate('setTail', [tail])
|
|
|
|
def setTail(self, tail):
|
|
self.tail = tail
|
|
|
|
def getBodyTexture(self):
|
|
return self.bodyTexture
|
|
|
|
def b_setBodyTexture(self, bodyTexture):
|
|
self.d_setBodyTexture(bodyTexture)
|
|
self.setBodyTexture(bodyTexture)
|
|
|
|
def d_setBodyTexture(self, bodyTexture):
|
|
self.sendUpdate('setBodyTexture', [bodyTexture])
|
|
|
|
def setBodyTexture(self, bodyTexture):
|
|
self.bodyTexture = bodyTexture
|
|
|
|
def getColor(self):
|
|
return self.color
|
|
|
|
def b_setColor(self, color):
|
|
self.d_setColor(color)
|
|
self.setColor(color)
|
|
|
|
def d_setColor(self, color):
|
|
self.sendUpdate('setColor', [color])
|
|
|
|
def setColor(self, color):
|
|
self.color = color
|
|
|
|
def getColorScale(self):
|
|
return self.colorScale
|
|
|
|
def b_setColorScale(self, colorScale):
|
|
self.d_setColorScale(colorScale)
|
|
self.setColorScale(colorScale)
|
|
|
|
def d_setColorScale(self, colorScale):
|
|
self.sendUpdate('setColorScale', [colorScale])
|
|
|
|
def setColorScale(self, colorScale):
|
|
self.colorScale = colorScale
|
|
|
|
def getEyeColor(self):
|
|
return self.eyeColor
|
|
|
|
def b_setEyeColor(self, eyeColor):
|
|
self.d_setEyeColor(eyeColor)
|
|
self.setEyeColor(eyeColor)
|
|
|
|
def d_setEyeColor(self, eyeColor):
|
|
self.sendUpdate('setEyeColor', [eyeColor])
|
|
|
|
def setEyeColor(self, eyeColor):
|
|
self.eyeColor = eyeColor
|
|
|
|
def getGender(self):
|
|
return self.gender
|
|
|
|
def b_setGender(self, gender):
|
|
self.d_setGender(gender)
|
|
self.setGender(gender)
|
|
|
|
def d_setGender(self, gender):
|
|
self.sendUpdate('setGender', [gender])
|
|
|
|
def setGender(self, gender):
|
|
self.gender = gender
|
|
|
|
def teleportIn(self, timestamp = None):
|
|
self.notify.debug('DPAI: teleportIn')
|
|
timestamp = ClockDelta.globalClockDelta.getRealNetworkTime()
|
|
self.notify.debug('DPAI: sending update @ ts = %s' % timestamp)
|
|
self.sendUpdate('teleportIn', [timestamp])
|
|
return None
|
|
|
|
def teleportOut(self, timestamp = None):
|
|
self.notify.debug('DPAI: teleportOut')
|
|
timestamp = ClockDelta.globalClockDelta.getRealNetworkTime()
|
|
self.notify.debug('DPAI: sending update @ ts = %s' % timestamp)
|
|
self.sendUpdate('teleportOut', [timestamp])
|
|
return None
|
|
|
|
def getLastSeenTimestamp(self):
|
|
return self.lastSeenTimestamp
|
|
|
|
def b_setLastSeenTimestamp(self, timestamp):
|
|
self.d_setLastSeenTimestamp(timestamp)
|
|
self.setLastSeenTimestamp(timestamp)
|
|
|
|
def d_setLastSeenTimestamp(self, timestamp):
|
|
self.sendUpdate('setLastSeenTimestamp', [timestamp])
|
|
|
|
def setLastSeenTimestamp(self, timestamp):
|
|
self.lastSeenTimestamp = timestamp
|
|
|
|
def getCurEpochTimestamp(self):
|
|
return int(time.time())
|
|
|
|
def getTimeSinceLastSeen(self):
|
|
t = time.time() - self.lastSeenTimestamp
|
|
return max(0.0, t)
|
|
|
|
def __handleMoodSet(self, component, value):
|
|
if self.isGenerated():
|
|
self.mood.setComponent(component, value)
|
|
else:
|
|
self.requiredMoodComponents[component] = value
|
|
|
|
def __handleMoodGet(self, component):
|
|
if self.isGenerated():
|
|
return self.mood.getComponent(component)
|
|
else:
|
|
return 0.0
|
|
|
|
def __generateDistMoodFuncs(self):
|
|
for compName in PetMood.PetMood.Components:
|
|
getterName = self.getSetterName(compName, 'get')
|
|
setterName = self.getSetterName(compName)
|
|
|
|
def moodGetter(compName = compName):
|
|
return self.__handleMoodGet(compName)
|
|
|
|
def b_moodSetter(value, setterName = setterName):
|
|
self.__dict__[setterName](value)
|
|
|
|
def d_moodSetter(value, setterName = setterName):
|
|
self.sendUpdate(setterName, [value])
|
|
|
|
def moodSetter(value, compName = compName):
|
|
self.__handleMoodSet(compName, value)
|
|
|
|
self.__dict__[getterName] = moodGetter
|
|
self.__dict__['b_%s' % setterName] = b_moodSetter
|
|
self.__dict__['d_%s' % setterName] = d_moodSetter
|
|
self.__dict__[setterName] = moodSetter
|
|
self.__funcsToDelete.append(getterName)
|
|
self.__funcsToDelete.append('b_%s' % setterName)
|
|
self.__funcsToDelete.append('d_%s' % setterName)
|
|
self.__funcsToDelete.append(setterName)
|
|
|
|
def getTrickAptitudes(self):
|
|
return self.trickAptitudes
|
|
|
|
def b_setTrickAptitudes(self, aptitudes):
|
|
self.setTrickAptitudes(aptitudes, local=1)
|
|
self.d_setTrickAptitudes(aptitudes)
|
|
|
|
def d_setTrickAptitudes(self, aptitudes):
|
|
while len(aptitudes) < len(PetTricks.Tricks) - 1:
|
|
aptitudes.append(0.0)
|
|
|
|
self.sendUpdate('setTrickAptitudes', [aptitudes])
|
|
|
|
def setTrickAptitudes(self, aptitudes, local = 0):
|
|
if not local:
|
|
DistributedPetAI.notify.debug('setTrickAptitudes: %s' % aptitudes)
|
|
while len(self.trickAptitudes) < len(PetTricks.Tricks) - 1:
|
|
self.trickAptitudes.append(0.0)
|
|
|
|
self.trickAptitudes = aptitudes
|
|
|
|
def getTrickAptitude(self, trickId):
|
|
if trickId > len(self.trickAptitudes) - 1:
|
|
return 0.0
|
|
return self.trickAptitudes[trickId]
|
|
|
|
def setTrickAptitude(self, trickId, aptitude, send = 1):
|
|
aptitude = clampScalar(aptitude, 0.0, 1.0)
|
|
aptitudes = self.trickAptitudes
|
|
while len(aptitudes) - 1 < trickId:
|
|
aptitudes.append(0.0)
|
|
|
|
if aptitudes[trickId] != aptitude:
|
|
aptitudes[trickId] = aptitude
|
|
if send:
|
|
self.b_setTrickAptitudes(aptitudes)
|
|
else:
|
|
self.setTrickAptitudes(aptitudes, local=1)
|
|
|
|
def announceGenerate(self):
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.announceGenerate(self)
|
|
self._hasCleanedUp = False
|
|
self.setHasRequestedDelete(False)
|
|
self.b_setParent(ToontownGlobals.SPHidden)
|
|
self.lockedDown = 0
|
|
self.leashMode = 0
|
|
self.leashAvId = None
|
|
self.leashGoal = None
|
|
self.traits = PetTraits.PetTraits(self.traitSeed, self.safeZone)
|
|
if not hasattr(self, '_beingCreatedInDB'):
|
|
for i in xrange(len(self.traitList)):
|
|
value = self.traitList[i]
|
|
if value == 0.0:
|
|
traitName = PetTraits.getTraitNames()[i]
|
|
traitValue = self.traits.getTraitValue(traitName)
|
|
DistributedPetAI.notify.info("%s: initializing new trait '%s' to %s, seed=%s" % (self.doId,
|
|
traitName,
|
|
traitValue,
|
|
self.traitSeed))
|
|
setterName = self.getSetterName(traitName, 'b_set')
|
|
self.__dict__[setterName](traitValue)
|
|
|
|
self.mood = PetMood.PetMood(self)
|
|
if not self.active:
|
|
return
|
|
self.activated = 1
|
|
self.announceZoneChange(self.zoneId, ToontownGlobals.QuietZone)
|
|
self.b_setParent(ToontownGlobals.SPRender)
|
|
self.setPos(randFloat(-20, 20), randFloat(-20, 20), 0)
|
|
self.setH(randFloat(360))
|
|
if self.initialDNA:
|
|
self.setDNA(self.initialDNA)
|
|
for mood, value in self.requiredMoodComponents.items():
|
|
self.mood.setComponent(mood, value, announce=0)
|
|
|
|
self.requiredMoodComponents = {}
|
|
self.brain = PetBrain.PetBrain(self)
|
|
self.mover = PetMoverAI(self)
|
|
self.enterPetLook()
|
|
self.actionFSM = PetActionFSM.PetActionFSM(self)
|
|
self.teleportIn()
|
|
self.handleMoodChange(distribute=0)
|
|
taskMgr.doMethodLater(simbase.petMovePeriod * random.random(), self.move, self.getMoveTaskName())
|
|
self.startPosHprBroadcast()
|
|
self.accept(PetObserve.getEventName(self.zoneId), self.brain.observe)
|
|
self.accept(self.mood.getMoodChangeEvent(), self.handleMoodChange)
|
|
self.mood.start()
|
|
self.brain.start()
|
|
return
|
|
|
|
def _isPet(self):
|
|
return 1
|
|
|
|
def setHasRequestedDelete(self, flag):
|
|
self._requestedDeleteFlag = flag
|
|
|
|
def hasRequestedDelete(self):
|
|
return self._requestedDeleteFlag
|
|
|
|
def requestDelete(self, task = None):
|
|
DistributedPetAI.notify.info('PetAI.requestDelete: %s, owner=%s' % (self.doId, self.ownerId))
|
|
if self.hasRequestedDelete():
|
|
DistributedPetAI.notify.info('PetAI.requestDelete: %s, owner=%s returning immediately' % (self.doId, self.ownerId))
|
|
return
|
|
self.setHasRequestedDelete(True)
|
|
self.b_setLastSeenTimestamp(self.getCurEpochTimestamp())
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.requestDelete(self)
|
|
|
|
def _doDeleteCleanup(self):
|
|
taskMgr.remove(self.uniqueName('clearMovie'))
|
|
taskMgr.remove(self.uniqueName('PetMovieWait'))
|
|
taskMgr.remove(self.uniqueName('PetMovieClear'))
|
|
taskMgr.remove(self.uniqueName('PetMovieComplete'))
|
|
taskMgr.remove(self.getLockMoveTaskName())
|
|
taskMgr.remove(self.getMoveTaskName())
|
|
if hasattr(self, 'zoneId'):
|
|
self.announceZoneChange(ToontownGlobals.QuietZone, self.zoneId)
|
|
else:
|
|
myDoId = 'No doId'
|
|
myTaskName = 'No task name'
|
|
myStackTrace = StackTrace().trace
|
|
myOldStackTrace = 'No Trace'
|
|
if hasattr(self, 'doId'):
|
|
myDoId = self.doId
|
|
if task:
|
|
myTaskName = task.name
|
|
if hasattr(self, 'destroyDoStackTrace'):
|
|
myOldStackTrace = self.destroyDoStackTrace.trace
|
|
simbase.air.writeServerEvent('Pet RequestDelete duplicate', myDoId, 'from task %s' % myTaskName)
|
|
simbase.air.writeServerEvent('Pet RequestDelete duplicate StackTrace', myDoId, '%s' % myStackTrace)
|
|
simbase.air.writeServerEvent('Pet RequestDelete duplicate OldStackTrace', myDoId, '%s' % myOldStackTrace)
|
|
DistributedPetAI.notify.warning('double requestDelete from task %s' % myTaskName)
|
|
self.setParent(ToontownGlobals.SPHidden)
|
|
if hasattr(self, 'activated'):
|
|
if self.activated:
|
|
self.activated = 0
|
|
self.brain.destroy()
|
|
del self.brain
|
|
self.actionFSM.destroy()
|
|
del self.actionFSM
|
|
self.exitPetLook()
|
|
self.mover.destroy()
|
|
del self.mover
|
|
self.stopPosHprBroadcast()
|
|
if hasattr(self, 'mood'):
|
|
self.mood.destroy()
|
|
del self.mood
|
|
if hasattr(self, 'traits'):
|
|
del self.traits
|
|
try:
|
|
for funcName in self.__funcsToDelete:
|
|
del self.__dict__[funcName]
|
|
|
|
except:
|
|
pass
|
|
|
|
if hasattr(self, 'gaitFSM'):
|
|
if self.gaitFSM:
|
|
self.gaitFSM.requestFinalState()
|
|
del self.gaitFSM
|
|
if hasattr(self, 'unstickFSM'):
|
|
if self.unstickFSM:
|
|
self.unstickFSM.requestFinalState()
|
|
del self.unstickFSM
|
|
PetLookerAI.PetLookerAI.destroy(self)
|
|
self.ignoreAll()
|
|
self._hasCleanedUp = True
|
|
|
|
def delete(self):
|
|
DistributedPetAI.notify.info('PetAI.delete: %s, owner=%s' % (self.doId, self.ownerId))
|
|
if not self._hasCleanedUp:
|
|
self._doDeleteCleanup()
|
|
self.setHasRequestedDelete(False)
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self)
|
|
|
|
def patchDelete(self):
|
|
for funcName in self.__funcsToDelete:
|
|
del self.__dict__[funcName]
|
|
|
|
del self.gaitFSM
|
|
del self.unstickFSM
|
|
PetLookerAI.PetLookerAI.destroy(self)
|
|
self.doNotDeallocateChannel = True
|
|
self.zoneId = None
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.delete(self)
|
|
self.ignoreAll()
|
|
return
|
|
|
|
def getMoveTaskName(self):
|
|
return 'petMove-%s' % self.doId
|
|
|
|
def getLockMoveTaskName(self):
|
|
return 'petLockMove-%s' % self.doId
|
|
|
|
def move(self, task = None):
|
|
if self.isEmpty():
|
|
self.air.writeServerEvent('Late Pet Move Call', self.doId, ' ')
|
|
taskMgr.remove(task.name)
|
|
return Task.done
|
|
|
|
numNearby = len(self.brain.nearbyAvs) - 1
|
|
minNearby = 5
|
|
if numNearby > minNearby:
|
|
delay = 0.08 * (numNearby - minNearby)
|
|
self.setPosHprBroadcastPeriod(PetConstants.PosBroadcastPeriod + lerp(delay * 0.75, delay, random.random()))
|
|
maxDist = 1000
|
|
if abs(self.getX()) > maxDist or abs(self.getY()) > maxDist:
|
|
DistributedPetAI.notify.warning('deleting pet %s before he wanders off too far' % self.doId)
|
|
self._outOfBounds = True
|
|
self.stopPosHprBroadcast()
|
|
self.requestDelete()
|
|
return Task.done
|
|
taskMgr.doMethodLater(simbase.petMovePeriod, self.move, self.getMoveTaskName())
|
|
return Task.done
|
|
|
|
def startPosHprBroadcast(self):
|
|
if self._outOfBounds:
|
|
return
|
|
DistributedSmoothNodeAI.DistributedSmoothNodeAI.startPosHprBroadcast(self, period=simbase.petPosBroadcastPeriod, type=DistributedSmoothNodeBase.DistributedSmoothNodeBase.BroadcastTypes.XYH)
|
|
|
|
def setMoodComponent(self, component, value):
|
|
setter = self.getSetterName(component, 'b_set')
|
|
self.__dict__[setter](value)
|
|
|
|
def addToMood(self, component, delta):
|
|
value = self.mood.getComponent(component)
|
|
value += delta
|
|
self.setMoodComponent(component, clampScalar(value, 0.0, 1.0))
|
|
|
|
def lerpMood(self, component, factor):
|
|
curVal = self.mood.getComponent(component)
|
|
if factor < 0:
|
|
self.setMoodComponent(component, lerp(curVal, 0.0, -factor))
|
|
else:
|
|
self.setMoodComponent(component, lerp(curVal, 1.0, factor))
|
|
|
|
def addToMoods(self, mood2delta):
|
|
for mood, delta in mood2delta.items():
|
|
self.addToMood(mood, delta)
|
|
|
|
def lerpMoods(self, mood2factor):
|
|
for mood, factor in mood2factor.items():
|
|
self.lerpMood(mood, factor)
|
|
|
|
def handleMoodChange(self, components = [], distribute = 1):
|
|
if len(components) == 0:
|
|
components = PetMood.PetMood.Components
|
|
if distribute:
|
|
if len(components) == len(PetMood.PetMood.Components):
|
|
values = []
|
|
for comp in PetMood.PetMood.Components:
|
|
values.append(self.mood.getComponent(comp))
|
|
|
|
self.sendUpdate('setMood', values)
|
|
else:
|
|
for comp in components:
|
|
setter = self.getSetterName(comp, 'd_set')
|
|
self.__dict__[setter](self.mood.getComponent(comp))
|
|
|
|
if self.isExcited():
|
|
self.gaitFSM.request('happy')
|
|
elif self.isSad():
|
|
self.gaitFSM.request('sad')
|
|
else:
|
|
self.gaitFSM.request('neutral')
|
|
|
|
def isContented(self):
|
|
return self.mood.getDominantMood() in PetMood.PetMood.ContentedMoods
|
|
|
|
def call(self, avatar):
|
|
self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.COME, avatar.doId))
|
|
self.__petMovieStart(avatar.doId)
|
|
|
|
def feed(self, avatar):
|
|
if avatar.takeMoney(PetConstants.FEED_AMOUNT):
|
|
self.startLockPetMove(avatar.doId)
|
|
self.brain.observe(PetObserve.PetActionObserve(PetObserve.Actions.FEED, avatar.doId))
|
|
|
|
def scratch(self, avatar):
|
|
self.startLockPetMove(avatar.doId)
|
|
self.brain.observe(PetObserve.PetActionObserve(PetObserve.Actions.SCRATCH, avatar.doId))
|
|
|
|
def lockPet(self):
|
|
DistributedPetAI.notify.debug('%s: lockPet' % self.doId)
|
|
if not self.lockedDown:
|
|
self.mover.lock()
|
|
self.stopPosHprBroadcast()
|
|
self.lockedDown += 1
|
|
|
|
def isLockedDown(self):
|
|
return self.lockedDown != 0
|
|
|
|
def unlockPet(self):
|
|
DistributedPetAI.notify.debug('%s: unlockPet' % self.doId)
|
|
if self.lockedDown <= 0:
|
|
DistributedPetAI.notify.warning('%s: unlockPet called on unlocked pet' % self.doId)
|
|
else:
|
|
self.lockedDown -= 1
|
|
if not self.lockedDown and not self.isDeleted():
|
|
self.startPosHprBroadcast()
|
|
|
|
def handleStay(self, avatar):
|
|
self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.STAY, avatar.doId))
|
|
|
|
def handleShoo(self, avatar):
|
|
self.brain.observe(PetObserve.PetPhraseObserve(PetObserve.Phrases.GO_AWAY, avatar.doId))
|
|
|
|
def gaitEnterOff(self):
|
|
pass
|
|
|
|
def gaitExitOff(self):
|
|
pass
|
|
|
|
def gaitEnterNeutral(self):
|
|
self.mover.setFwdSpeed(PetConstants.FwdSpeed)
|
|
self.mover.setRotSpeed(PetConstants.RotSpeed)
|
|
|
|
def gaitExitNeutral(self):
|
|
pass
|
|
|
|
def gaitEnterHappy(self):
|
|
self.mover.setFwdSpeed(PetConstants.HappyFwdSpeed)
|
|
self.mover.setRotSpeed(PetConstants.HappyRotSpeed)
|
|
|
|
def gaitExitHappy(self):
|
|
pass
|
|
|
|
def gaitEnterSad(self):
|
|
self.mover.setFwdSpeed(PetConstants.SadFwdSpeed)
|
|
self.mover.setRotSpeed(PetConstants.SadRotSpeed)
|
|
|
|
def gaitExitSad(self):
|
|
pass
|
|
|
|
def unstickEnterOff(self):
|
|
pass
|
|
|
|
def unstickExitOff(self):
|
|
pass
|
|
|
|
def unstickEnterOn(self):
|
|
self._collisionTimestamps = []
|
|
|
|
def unstickExitOn(self):
|
|
pass
|
|
|
|
def ownerIsOnline(self):
|
|
return self.ownerId in simbase.air.doId2do
|
|
|
|
def ownerIsInSameZone(self):
|
|
if not self.ownerIsOnline():
|
|
return 0
|
|
return self.zoneId == simbase.air.doId2do[self.ownerId].zoneId
|
|
|
|
def _getOwnerDict(self):
|
|
if self.owner is not None:
|
|
if self.ownerIsInSameZone():
|
|
return {self.ownerId: self.owner}
|
|
return {}
|
|
|
|
def _getFullNearbyToonDict(self):
|
|
toons = self.air.getObjectsOfClassInZone(self.air.districtId, self.zoneId, DistributedToonAI.DistributedToonAI)
|
|
return toons
|
|
|
|
def _getNearbyToonDict(self):
|
|
toons = self._getFullNearbyToonDict()
|
|
if self.ownerId in toons:
|
|
del toons[self.ownerId]
|
|
return toons
|
|
|
|
def _getNearbyPetDict(self):
|
|
pets = self.air.getObjectsOfClassInZone(self.air.districtId, self.zoneId, DistributedPetAI)
|
|
if self.doId in pets:
|
|
del pets[self.doId]
|
|
return pets
|
|
|
|
def _getNearbyAvatarDict(self):
|
|
avs = self._getFullNearbyToonDict()
|
|
avs.update(self._getNearbyPetDict())
|
|
return avs
|
|
|
|
def _getOwner(self):
|
|
return self.air.doId2do.get(self.ownerId)
|
|
|
|
def _getNearbyToon(self):
|
|
nearbyToonDict = self._getFullNearbyToonDict()
|
|
if not len(nearbyToonDict):
|
|
return None
|
|
return nearbyToonDict[random.choice(nearbyToonDict.keys())]
|
|
|
|
def _getNearbyToonNonOwner(self):
|
|
nearbyToonDict = self._getNearbyToonDict()
|
|
if not len(nearbyToonDict):
|
|
return None
|
|
return nearbyToonDict[random.choice(nearbyToonDict.keys())]
|
|
|
|
def _getNearbyPet(self):
|
|
nearbyPetDict = self._getNearbyPetDict()
|
|
if not len(nearbyPetDict):
|
|
return None
|
|
return nearbyPetDict[random.choice(nearbyPetDict.keys())]
|
|
|
|
def _getNearbyAvatar(self):
|
|
nearbyAvDict = self._getNearbyAvatarDict()
|
|
if not len(nearbyAvDict):
|
|
return None
|
|
return nearbyAvDict[random.choice(nearbyAvDict.keys())]
|
|
|
|
def isBusy(self):
|
|
return self.busy > 0
|
|
|
|
def freeAvatar(self, avId):
|
|
self.sendUpdateToAvatarId(avId, 'freeAvatar', [])
|
|
|
|
def avatarInteract(self, avId):
|
|
av = self.air.doId2do.get(avId)
|
|
if av is None:
|
|
self.notify.warning('Avatar: %s not found' % avId)
|
|
return 0
|
|
if self.isBusy():
|
|
self.notify.debug('freeing avatar!')
|
|
self.freeAvatar(avId)
|
|
return 0
|
|
self.busy = avId
|
|
self.notify.debug('sending update')
|
|
self.sendUpdateToAvatarId(avId, 'avatarInteract', [avId])
|
|
self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId])
|
|
return 1
|
|
|
|
def rejectAvatar(self, avId):
|
|
self.notify.error('rejectAvatar: should not be called by a pet!')
|
|
|
|
def d_setMovie(self, avId, flag):
|
|
self.sendUpdate('setMovie', [flag, avId, ClockDelta.globalClockDelta.getRealNetworkTime()])
|
|
|
|
def sendClearMovie(self, task = None):
|
|
if self.air != None:
|
|
self.ignore(self.air.getAvatarExitEvent(self.busy))
|
|
taskMgr.remove(self.uniqueName('clearMovie'))
|
|
self.busy = 0
|
|
self.d_setMovie(0, PetConstants.PET_MOVIE_CLEAR)
|
|
return Task.done
|
|
|
|
def __handleUnexpectedExit(self, avId):
|
|
self.notify.warning('avatar:' + str(avId) + ' has exited unexpectedly')
|
|
self.notify.warning('not busy with avId: %s, busy: %s ' % (avId, self.busy))
|
|
taskMgr.remove(self.uniqueName('clearMovie'))
|
|
self.sendClearMovie()
|
|
|
|
def handleAvPetInteraction(self, mode, avId):
|
|
if mode not in (PetConstants.PET_MOVIE_SCRATCH, PetConstants.PET_MOVIE_FEED, PetConstants.PET_MOVIE_CALL):
|
|
self.air.writeServerEvent('suspicious', avId, 'DistributedPetAI: unknown mode: %s' % mode)
|
|
return
|
|
if self.avatarInteract(avId):
|
|
self.notify.debug('handleAvPetInteraction() avatarInteract calling callback')
|
|
self.movieMode = mode
|
|
callback = {PetConstants.PET_MOVIE_SCRATCH: self.scratch,
|
|
PetConstants.PET_MOVIE_FEED: self.feed,
|
|
PetConstants.PET_MOVIE_CALL: self.call}.get(mode)
|
|
callback(self.air.doId2do.get(avId))
|
|
else:
|
|
self.notify.debug('handleAvPetInteraction() avatarInteract was busy or unhappy')
|
|
|
|
def __petMovieStart(self, avId):
|
|
self.d_setMovie(avId, self.movieMode)
|
|
time = self.movieTimeSwitch.get(self.movieMode)
|
|
taskMgr.doMethodLater(time, self.__petMovieComplete, self.uniqueName('PetMovieComplete'))
|
|
|
|
def __petMovieComplete(self, task = None):
|
|
self.disableLockMover()
|
|
self.unlockPet()
|
|
self.sendClearMovie()
|
|
self.movieMode = None
|
|
return Task.done
|
|
|
|
def startLockPetMove(self, avId):
|
|
self.enableLockMover()
|
|
dist_Callable = self.movieDistSwitch.get(self.movieMode)
|
|
dist = dist_Callable(self.air.doId2do.get(avId).getStyle().getLegSize())
|
|
self.distList = [0, 0, 0]
|
|
self.mover.walkToAvatar(self.air.doId2do[avId], callback=lambda: self.endLockPetMove(avId))
|
|
self.__lockPetMoveTask(avId)
|
|
|
|
def getAverageDist(self):
|
|
sum = 0
|
|
for i in self.distList:
|
|
sum += i
|
|
|
|
return sum / 3.0
|
|
|
|
def __lockPetMoveTask(self, avId):
|
|
return Task.done
|
|
|
|
def endLockPetMove(self, avId):
|
|
del self.distList
|
|
taskMgr.remove(self.getLockMoveTaskName())
|
|
self.lockPet()
|
|
self.__petMovieStart(avId)
|
|
|
|
def enableLockMover(self):
|
|
if not hasattr(self, 'brain'):
|
|
return
|
|
|
|
if self.lockMoverEnabled == 0:
|
|
self.brain._startMovie()
|
|
self.lockMoverEnabled += 1
|
|
|
|
def isLockMoverEnabled(self):
|
|
return self.lockMoverEnabled > 0
|
|
|
|
def disableLockMover(self):
|
|
if not hasattr(self, 'brain'):
|
|
return
|
|
|
|
if self.lockMoverEnabled > 0:
|
|
self.lockMoverEnabled -= 1
|
|
if self.lockMoverEnabled == 0:
|
|
self.brain._endMovie()
|
|
|
|
def _willDoTrick(self, trickId):
|
|
if self.isContented():
|
|
minApt = PetTricks.MinActualTrickAptitude
|
|
maxApt = PetTricks.MaxActualTrickAptitude
|
|
else:
|
|
minApt = PetTricks.NonHappyMinActualTrickAptitude
|
|
maxApt = PetTricks.NonHappyMaxActualTrickAptitude
|
|
randVal = random.random()
|
|
cutoff = lerp(minApt, maxApt, self.getTrickAptitude(trickId))
|
|
if self.mood.isComponentActive('fatigue'):
|
|
cutoff *= 0.5
|
|
cutoff *= PetTricks.TrickAccuracies[trickId]
|
|
DistributedPetAI.notify.info('_willDoTrick: %s / %s' % (randVal, cutoff)) # .debug
|
|
return randVal < cutoff
|
|
|
|
def _handleDidTrick(self, trickId):
|
|
DistributedPetAI.notify.debug('_handleDidTrick: %s' % trickId)
|
|
if trickId == PetTricks.Tricks.BALK:
|
|
return
|
|
aptitude = self.getTrickAptitude(trickId)
|
|
self.setTrickAptitude(trickId, aptitude + PetTricks.AptitudeIncrementDidTrick)
|
|
self.addToMood('fatigue', lerp(PetTricks.MaxTrickFatigue, PetTricks.MinTrickFatigue, aptitude))
|
|
|
|
def _handleGotPositiveTrickFeedback(self, trickId, magnitude):
|
|
if trickId == PetTricks.Tricks.BALK:
|
|
return
|
|
self.setTrickAptitude(trickId, self.getTrickAptitude(trickId) + PetTricks.MaxAptitudeIncrementGotPraise * magnitude)
|
|
|
|
def toggleLeash(self, avId):
|
|
if self.leashMode:
|
|
self.leashMode = 0
|
|
self.leashAvId = None
|
|
response = 'leash OFF'
|
|
else:
|
|
self.leashMode = 1
|
|
self.leashAvId = avId
|
|
response = 'leash ON'
|
|
return response
|