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