Poodletooth-iLand/toontown/pets/PetBrain.py

537 lines
19 KiB
Python
Raw Normal View History

from panda3d.core import *
2015-03-03 16:10:12 -06:00
from direct.showbase.PythonUtil import weightedChoice, randFloat, Functor
from direct.showbase.PythonUtil import list2dict
from direct.showbase import DirectObject
2015-03-29 10:41:02 -05:00
from direct.distributed import DistributedObject, DistributedObjectAI
2015-03-03 16:10:12 -06:00
from direct.directnotify import DirectNotifyGlobal
from direct.task import Task
from direct.fsm import FSM
from toontown.pets import PetConstants, PetObserve, PetGoal, PetGoalMgr
from toontown.pets import PetTricks, PetLookerAI
import random, types
class PetBrain(DirectObject.DirectObject):
notify = DirectNotifyGlobal.directNotify.newCategory('PetBrain')
def __init__(self, pet):
self.pet = pet
self.focus = None
self.started = 0
self.inMovie = 0
self.chaseNode = self.pet.getRender().attachNewNode('PetChaseNode')
self.goalMgr = PetGoalMgr.PetGoalMgr(self.pet)
self.doId2goals = {}
self.nearbyAvs = {}
self.avAwareness = {}
self.lastInteractTime = {}
self.nextAwarenessIndex = 0
def destroy(self):
taskMgr.remove(self.getTeleportTaskName())
self.stop()
self.goalMgr.destroy()
self.chaseNode.removeNode()
del self.chaseNode
del self.focus
del self.pet
if self.doId2goals:
self.notify.warning('destroy(): self.doId2goals is not empty: %s' % self.doId2goals.keys())
for goalList in self.doId2goals.values():
for goal in goalList:
goal.destroy()
del self.doId2goals
del self.avAwareness
def getThinkTaskName(self):
return 'petThink-%s' % self.pet.doId
def getTeleportTaskName(self):
return 'petTeleport-%s' % self.pet.doId
def getObserveEventAttendedByAvStart(self, otherDoId):
return 'petObserveAttendedByAvStart-%s-%s' % (self.pet.doId, otherDoId)
def getObserveEventAttendedByAvStop(self, otherDoId):
return 'petObserveAttendedByAvStop-%s-%s' % (self.pet.doId, otherDoId)
def getObserveEventAttendingAvStart(self, otherDoId):
return 'petObserveAttendingAvStart-%s-%s' % (self.pet.doId, otherDoId)
def getObserveEventAttendingAvStop(self, otherDoId):
return 'petObserveAttendingAvStop-%s-%s' % (self.pet.doId, otherDoId)
def start(self):
PetBrain.notify.debug('start: %s' % self.pet.doId)
self.lookers = {}
self.lookees = {}
self.accept(PetLookerAI.getStartLookedAtByOtherEvent(self.pet.doId), self._handleLookedAtByOtherStart)
self.accept(PetLookerAI.getStopLookedAtByOtherEvent(self.pet.doId), self._handleLookedAtByOtherStop)
self.accept(PetLookerAI.getStartLookingAtOtherEvent(self.pet.doId), self._handleLookingAtOtherStart)
self.accept(PetLookerAI.getStopLookingAtOtherEvent(self.pet.doId), self._handleLookingAtOtherStop)
self.globalGoals = [PetGoal.Wander()]
for goal in self.globalGoals:
self.goalMgr.addGoal(goal)
for doId in self.pet._getNearbyAvatarDict():
self._handleAvatarArrive(doId)
self.tLastLonelinessUpdate = globalClock.getFrameTime()
taskMgr.doMethodLater(simbase.petThinkPeriod * random.random(), self._think, self.getThinkTaskName())
self.started = 1
def stop(self):
PetBrain.notify.debug('stop: %s' % self.pet.doId)
if not self.started:
return
self.started = 0
del self.lookers
del self.lookees
for doId in self.pet._getNearbyAvatarDict():
self._handleAvatarLeave(doId)
for goal in self.globalGoals:
self.goalMgr.removeGoal(goal)
goal.destroy()
del self.globalGoals
self.clearFocus()
taskMgr.remove(self.getThinkTaskName())
self.ignore(PetLookerAI.getStartLookedAtByOtherEvent(self.pet.doId))
self.ignore(PetLookerAI.getStopLookedAtByOtherEvent(self.pet.doId))
self.ignore(PetLookerAI.getStartLookingAtOtherEvent(self.pet.doId))
self.ignore(PetLookerAI.getStopLookingAtOtherEvent(self.pet.doId))
def observe(self, petObserve):
if petObserve.isForgettable():
if random.random() < 0.05 * self.pet.traits.forgetfulness:
return
petObserve._influence(self)
def updateLastInteractTime(self, avId):
if avId in self.lastInteractTime:
self.lastInteractTime[avId] = globalClock.getFrameTime()
def _think(self, task = None):
if not self.inMovie:
self._updatePriorities()
if len(self.nearbyAvs) > PetConstants.MaxAvatarAwareness:
self.nextAwarenessIndex %= len(self.nearbyAvs)
self._considerBecomeAwareOf(self.nearbyAvs.keys()[self.nextAwarenessIndex])
self.nextAwarenessIndex += 1
curT = globalClock.getFrameTime()
tSinceLastLonelinessUpdate = curT - self.tLastLonelinessUpdate
if tSinceLastLonelinessUpdate >= PetConstants.LonelinessUpdatePeriod:
self.tLastLonelinessUpdate = curT
numLookers = len(self.lookers)
if numLookers:
dt = tSinceLastLonelinessUpdate
self.pet.lerpMood('loneliness', max(-1.0, dt * -.003 * numLookers))
if numLookers > 5:
self.pet.lerpMood('excitement', min(1.0, dt * 0.001 * numLookers))
taskMgr.doMethodLater(simbase.petThinkPeriod, self._think, self.getThinkTaskName())
return Task.done
def _updatePriorities(self):
self.goalMgr.updatePriorities()
def _handleLookingAtOtherStart(self, avId):
if avId in self.lookees:
PetBrain.notify.warning('%s: already looking at av %s' % (self.pet.doId, avId))
return
self.lookees[avId] = avId
self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDING_START, avId))
def _handleLookingAtOtherStop(self, avId):
if avId not in self.lookees:
PetBrain.notify.warning('%s: not looking at av %s' % (self.pet.doId, avId))
return
del self.lookees[avId]
self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDING_STOP, avId))
def _handleLookedAtByOtherStart(self, avId):
if avId in self.lookers:
PetBrain.notify.warning('%s: av %s already looking at me' % (self.pet.doId, avId))
return
self.lookers[avId] = avId
self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDED_START, avId))
def _handleLookedAtByOtherStop(self, avId):
if avId not in self.lookers:
PetBrain.notify.warning('%s: av %s not looking at me' % (self.pet.doId, avId))
return
del self.lookers[avId]
self.observe(PetObserve.PetActionObserve(PetObserve.Actions.ATTENDED_STOP, avId))
def lookedAtBy(self, avId):
return avId in self.lookers
def lookingAt(self, avId):
return avId in self.lookees
def getAvIdsLookingAtUs(self):
return self.lookers
def getAvIdsWeAreLookingAt(self):
return self.lookees
def setFocus(self, object):
if isinstance(self.focus, DistributedObjectAI.DistributedObjectAI):
self.ignore(self.focus.getDeleteEvent())
self.lastInteractTime.setdefault(self.focus.doId, 0)
PetBrain.notify.debug('setFocus: %s' % object)
self.focus = object
if isinstance(self.focus, DistributedObjectAI.DistributedObjectAI):
self.accept(self.focus.getDeleteEvent(), self._handleFocusHasLeft)
def getFocus(self):
return self.focus
def clearFocus(self):
self.setFocus(None)
return
def _handleFocusHasLeft(self):
if self.focus.isEmpty():
self.chaseNode.setPos(self.pet, 0, 0, 0)
else:
self.chaseNode.setPos(self.focus, 0, 0, 0)
self._inspectSpot(self.chaseNode)
def _chase(self, target):
if callable(target):
target = target()
if target is None:
return 0
self.setFocus(target)
self.pet.actionFSM.request('Chase', target)
return 1
def _wander(self):
self.clearFocus()
self.pet.actionFSM.request('Wander')
return 1
def _unstick(self):
self.clearFocus()
self.pet.actionFSM.request('Unstick')
return 1
def _flee(self, chaser):
if callable(chaser):
chaser = chaser()
if chaser is None:
return 0
self.setFocus(chaser)
self.pet.actionFSM.request('Flee', chaser)
return 1
def _inspectSpot(self, spot = None):
if spot is None:
spot = NodePath('randomSpot')
spot.setPos(randFloat(-20, 20), randFloat(-20, 20), 0)
self.setFocus(spot)
self.pet.actionFSM.request('InspectSpot', spot)
return 1
def _stay(self, avatar):
self.setFocus(avatar)
self.pet.actionFSM.request('Stay', avatar)
return 1
def _doTrick(self, trickId, avatar):
self.setFocus(avatar)
self.pet.actionFSM.request('Trick', avatar, trickId)
return 1
def _heal(self, avatar):
if callable(avatar):
avatar = avatar()
if avatar is None:
return 0
self.setFocus(avatar)
self.pet.actionFSM.request('Heal', avatar)
return 1
def _startMovie(self):
self.setFocus(None)
self.pet.actionFSM.request('Movie')
self.inMovie = 1
return
def _endMovie(self):
self.inMovie = 0
def _handleGenericObserve(self, observe):
pass
def _handleActionObserve(self, observe):
action = observe.getAction()
avId = observe.getAvId()
OA = PetObserve.Actions
dbg = PetBrain.notify.debug
if action == OA.ATTENDED_START:
dbg('avatar %s is looking at me' % avId)
self.pet.lerpMoods({'boredom': -.1,
'excitement': 0.05,
'loneliness': -.05})
messenger.send(self.getObserveEventAttendedByAvStart(avId))
elif action == OA.ATTENDED_STOP:
dbg('avatar %s is no longer looking at me' % avId)
messenger.send(self.getObserveEventAttendedByAvStop(avId))
elif action == OA.ATTENDING_START:
dbg('I am looking at avatar %s' % avId)
messenger.send(self.getObserveEventAttendingAvStart(avId))
elif action == OA.ATTENDING_STOP:
dbg('I am no longer looking at avatar %s' % avId)
messenger.send(self.getObserveEventAttendingAvStop(avId))
elif action == OA.CHANGE_ZONE:
if avId != self.pet.doId:
oldZoneId, newZoneId = observe.getData()
PetBrain.notify.debug('%s.CHANGE_ZONE: %s, %s->%s' % (self.pet.doId,
avId,
oldZoneId,
newZoneId))
myZoneId = self.pet.zoneId
if newZoneId != oldZoneId:
if newZoneId == myZoneId:
self._handleAvatarArrive(avId)
elif oldZoneId == myZoneId:
self._handleAvatarLeave(avId)
if self.pet.inEstate:
if avId in (self.pet.ownerId, self.pet.estateOwnerId):
if oldZoneId in self.pet.estateZones and newZoneId not in self.pet.estateZones:
if avId == self.pet.ownerId:
self._handleOwnerLeave()
else:
self._handleEstateOwnerLeave()
elif action == OA.LOGOUT:
if avId == self.pet.ownerId:
self._handleOwnerLeave()
elif avId == self.pet.estateOwnerId:
self._handleEstateOwnerLeave()
elif action == OA.FEED:
dbg('avatar %s is feeding me' % avId)
self.pet.lerpMoods({'affection': 0.35,
'anger': -.07,
'boredom': -.5,
'excitement': 0.5,
'fatigue': -.2,
'hunger': -.5,
'loneliness': -.08,
'playfulness': 0.1,
'restlessness': -.05,
'sadness': -.2})
self.updateLastInteractTime(avId)
avatar = simbase.air.doId2do.get(avId)
if avatar is not None:
avatar.setHatePets(0)
elif action == OA.SCRATCH:
dbg('avatar %s is scratching me' % avId)
self.pet.lerpMoods({'affection': 0.45,
'anger': -.1,
'boredom': -.8,
'excitement': 0.5,
'fatigue': -.25,
'loneliness': -.2,
'playfulness': 0.1,
'restlessness': -.2,
'sadness': -.2})
self.updateLastInteractTime(avId)
avatar = simbase.air.doId2do.get(avId)
if avatar is not None:
avatar.setHatePets(0)
elif action == OA.GARDEN:
dbg('avatar %s is gardening' % avId)
avatar = simbase.air.doId2do.get(avId)
if avatar is not None:
if self.getFocus() == avatar:
self._wander()
return
def _handlePhraseObserve(self, observe):
def _handleGettingFriendlyAttention(avId, self = self):
self.pet.lerpMoods({'boredom': -.85,
'restlessness': -.1,
'playfulness': 0.2,
'loneliness': -.4,
'sadness': -.1,
'fatigue': -.05,
'excitement': 0.05,
'anger': -.05})
self.updateLastInteractTime(avId)
def _handleComeHere(avId, self = self):
avatar = simbase.air.doId2do.get(avId)
if avatar:
2015-03-29 10:41:02 -05:00
self.pet.mover.walkToAvatar(avatar)
2015-03-03 16:10:12 -06:00
avatar.setHatePets(0)
def _handleFollowMe(avId, self = self):
avatar = simbase.air.doId2do.get(avId)
if avatar:
2015-03-29 10:41:02 -05:00
self.pet.mover.walkToAvatar(avatar)
2015-03-03 16:10:12 -06:00
avatar.setHatePets(0)
def _handleStay(avId, self = self):
avatar = simbase.air.doId2do.get(avId)
if avatar:
self._stay(avatar)
def _handleCriticism(avId, self = self):
ownerFactor = 0.5
if avId == self.pet.ownerId:
ownerFactor = 1.0
self.pet.lerpMoods({'affection': -.4,
'anger': 0.4,
'boredom': -.3,
'confusion': 0.05,
'fatigue': 0.2,
'playfulness': -.1,
'sadness': 0.5 * ownerFactor})
def _handleGoAway(avId, self = self):
avatar = simbase.air.doId2do.get(avId)
if avatar is not None:
if self.getFocus() == avatar:
self._wander()
return
def _handleDoTrick(trickId, avId, self = self):
2015-03-29 10:41:02 -05:00
looked = self.lookedAtBy(avId) or config.GetBool('pet-brain-ignore-looked-tricks', True)
2015-03-03 16:10:12 -06:00
avatar = simbase.air.doId2do.get(avId)
if avatar:
2015-03-29 10:41:02 -05:00
if looked:
if not self.pet._willDoTrick(trickId):
trickId = PetTricks.Tricks.BALK
self._doTrick(trickId, avatar)
2015-03-03 16:10:12 -06:00
phrase = observe.getPetPhrase()
avId = observe.getAvId()
OP = PetObserve.Phrases
if phrase in list2dict([OP.COME,
OP.FOLLOW_ME,
OP.STAY,
OP.NEED_LAFF,
OP.NEED_GAGS,
OP.NEED_JB,
OP.HI,
OP.SOOTHE,
OP.PRAISE,
OP.HAPPY,
OP.QUESTION,
OP.FRIENDLY,
OP.LETS_PLAY,
OP.DO_TRICK]):
_handleGettingFriendlyAttention(avId)
if phrase == OP.COME:
_handleComeHere(avId)
if phrase == OP.FOLLOW_ME:
_handleFollowMe(avId)
if phrase == OP.STAY:
_handleStay(avId)
if phrase == OP.CRITICISM:
_handleCriticism(avId)
if phrase == OP.GO_AWAY:
_handleGoAway(avId)
if phrase == OP.DO_TRICK:
_handleDoTrick(observe.getTrickId(), avId)
def _addGoalsReAvatar(self, avId):
av = self.pet.air.doId2do.get(avId)
if av is None:
PetBrain.notify.warning('%s._addGoalsReAvatar: %s not in doId2do' % (self.pet.doId, avId))
return
if avId not in self.doId2goals:
goals = [PetGoal.ChaseAvatar(av), PetGoal.FleeFromAvatar(av)]
self.doId2goals[avId] = goals
self.lastInteractTime.setdefault(avId, 0)
for goal in self.doId2goals[avId]:
self.goalMgr.addGoal(goal)
return
def _removeGoalsReAvatar(self, avId):
if avId not in self.doId2goals:
PetBrain.notify.warning('no goals re av %s to remove' % avId)
return
for goal in self.doId2goals[avId]:
self.goalMgr.removeGoal(goal)
goal.destroy()
del self.doId2goals[avId]
def _considerBecomeAwareOf(self, avId):
av = simbase.air.doId2do.get(avId)
if av is None:
PetBrain.notify.warning('_considerBecomeAwareOf: av %s does not exist' % avId)
return
if avId in self.avAwareness:
return
def becomeAwareOf(avId, self = self):
self.avAwareness[avId] = None
self._addGoalsReAvatar(avId)
return
if len(self.avAwareness) < PetConstants.MaxAvatarAwareness:
becomeAwareOf(avId)
return
def calcInterest(avId, self = self):
if avId == self.pet.ownerId:
return 100.0
return random.random()
avInterest = calcInterest(avId)
minInterest = avInterest
minInterestAvId = avId
for awAvId in self.avAwareness:
i = calcInterest(awAvId)
if i < minInterest:
minInterest = i
minInterestAvId = awAvId
break
if minInterestAvId != avId:
self._removeAwarenessOf(minInterestAvId)
becomeAwareOf(avId)
return
def _removeAwarenessOf(self, avId):
if avId in self.avAwareness:
self._removeGoalsReAvatar(avId)
del self.avAwareness[avId]
def _handleAvatarArrive(self, avId):
PetBrain.notify.debug('%s._handleAvatarArrive: %s' % (self.pet.doId, avId))
if avId in self.nearbyAvs:
PetBrain.notify.warning('%s already in self.nearbyAvs' % avId)
return
self.nearbyAvs[avId] = None
excitement = 0.3
if avId == self.pet.ownerId:
excitement = 0.7
self.pet.lerpMoods({'excitement': 0.7,
'loneliness': -.4})
self._considerBecomeAwareOf(avId)
return
def _handleAvatarLeave(self, avId):
PetBrain.notify.debug('%s._handleAvatarLeave: %s' % (self.pet.doId, avId))
if avId not in self.nearbyAvs:
PetBrain.notify.warning('av %s not in self.nearbyAvs' % avId)
return
del self.nearbyAvs[avId]
self.pet.lerpMoods({'loneliness': 0.1})
self._removeAwarenessOf(avId)
def _handleOwnerLeave(self):
self.pet.teleportOut()
taskMgr.doMethodLater(PetConstants.TELEPORT_OUT_DURATION, self.pet.requestDelete, self.getTeleportTaskName())
def _handleEstateOwnerLeave(self):
self.pet.teleportOut()
taskMgr.doMethodLater(PetConstants.TELEPORT_OUT_DURATION, self.pet.requestDelete, self.getTeleportTaskName())