toontown-just-works/toontown/pets/DistributedPetAI.py
2024-07-07 18:08:39 -05:00

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