historical/toontown-classic.git/toontown/pets/PetMood.py

216 lines
8.3 KiB
Python
Raw Normal View History

2024-01-16 11:20:27 -06:00
from direct.directnotify import DirectNotifyGlobal
from direct.task import Task
from direct.showbase.PythonUtil import lerp, average
from otp.otpbase.PythonUtil import clampScalar
from toontown.toonbase import TTLocalizer
import random, time, weakref
class PetMood:
notify = DirectNotifyGlobal.directNotify.newCategory('PetMood')
Neutral = 'neutral'
Components = ('boredom', 'restlessness', 'playfulness', 'loneliness', 'sadness', 'affection', 'hunger', 'confusion', 'excitement', 'fatigue', 'anger', 'surprise')
SerialNum = 0
ContentedMoods = ('neutral', 'excitement', 'playfulness', 'affection')
ExcitedMoods = ('excitement', 'playfulness')
UnhappyMoods = ('boredom', 'restlessness', 'loneliness', 'sadness', 'fatigue', 'hunger', 'anger')
DisabledDominants = ('restlessness', 'playfulness')
AssertiveDominants = ('fatigue',)
HOUR = 1.0
MINUTE = HOUR / 60.0
DAY = 24.0 * HOUR
WEEK = 7 * DAY
LONGTIME = 5000 * WEEK
TBoredom = 12 * HOUR
TRestlessness = 18 * HOUR
TPlayfulness = -1 * HOUR
TLoneliness = 24 * HOUR
TSadness = -1 * HOUR
TFatigue = -15 * MINUTE
THunger = 24 * HOUR
TConfusion = -5 * MINUTE
TExcitement = -5 * MINUTE
TSurprise = -5 * MINUTE
TAffection = -10 * MINUTE
TAngerDec = -20 * MINUTE
TAngerInc = 2 * WEEK
def __init__(self, pet = None):
self.setPet(pet)
self.started = 0
self.serialNum = PetMood.SerialNum
PetMood.SerialNum += 1
for comp in PetMood.Components:
self.__dict__[comp] = 0.0
def calcDrift(baseT, trait, fasterDriftIsBetter = False):
value = trait.percentile
if not trait.higherIsBetter:
value = 1.0 - value
if fasterDriftIsBetter:
if value < 0.5:
factor = lerp(2.0, 1.0, value * 2.0)
else:
rebased = (value - 0.5) * 2.0
factor = lerp(1.0, 0.1, rebased * rebased)
elif value < 0.5:
factor = lerp(0.75, 1.0, value * 2.0)
else:
rebased = (value - 0.5) * 2.0
factor = lerp(1.0, 28.0, rebased * rebased)
return baseT * factor
pet = self.getPet()
self.tBoredom = calcDrift(PetMood.TBoredom, pet.traits.traits['boredomThreshold'])
self.tRestlessness = calcDrift(PetMood.TRestlessness, pet.traits.traits['restlessnessThreshold'])
self.tPlayfulness = calcDrift(PetMood.TPlayfulness, pet.traits.traits['playfulnessThreshold'])
self.tLoneliness = calcDrift(PetMood.TLoneliness, pet.traits.traits['lonelinessThreshold'])
self.tSadness = calcDrift(PetMood.TSadness, pet.traits.traits['sadnessThreshold'], True)
self.tFatigue = calcDrift(PetMood.TFatigue, pet.traits.traits['fatigueThreshold'], True)
self.tHunger = calcDrift(PetMood.THunger, pet.traits.traits['hungerThreshold'])
self.tConfusion = calcDrift(PetMood.TConfusion, pet.traits.traits['confusionThreshold'], True)
self.tExcitement = calcDrift(PetMood.TExcitement, pet.traits.traits['excitementThreshold'])
self.tSurprise = calcDrift(PetMood.TSurprise, pet.traits.traits['surpriseThreshold'], True)
self.tAffection = calcDrift(PetMood.TAffection, pet.traits.traits['affectionThreshold'])
self.tAngerDec = calcDrift(PetMood.TAngerDec, pet.traits.traits['angerThreshold'], True)
self.tAngerInc = calcDrift(PetMood.TAngerInc, pet.traits.traits['angerThreshold'])
self.dominantMood = PetMood.Neutral
def destroy(self):
self.stop()
del self.petRef
def setPet(self, pet):
self.petRef = weakref.ref(pet)
def getPet(self):
pet = self.petRef()
if pet is None:
self.notify.error('pet has been deleted')
return pet
def getMoodDriftTaskName(self):
return 'petMoodDrift-%s' % self.serialNum
def getMoodChangeEvent(self):
return 'petMoodChange-%s' % self.serialNum
def getDominantMoodChangeEvent(self):
return 'petDominantMoodChange-%s' % self.serialNum
def announceChange(self, components = []):
oldMood = self.dominantMood
if hasattr(self, 'dominantMood'):
del self.dominantMood
newMood = self.getDominantMood()
messenger.send(self.getMoodChangeEvent(), [components])
if newMood != oldMood:
messenger.send(self.getDominantMoodChangeEvent(), [newMood])
def getComponent(self, compName):
return self.__dict__[compName]
def setComponent(self, compName, value, announce = 1):
different = self.__dict__[compName] != value
self.__dict__[compName] = value
if announce and different:
self.announceChange([compName])
def _getComponentThreshold(self, compName):
threshName = compName + 'Threshold'
pet = self.getPet()
return pet.traits.__dict__[threshName]
def isComponentActive(self, compName):
return self.getComponent(compName) >= self._getComponentThreshold(compName)
def anyActive(self, compNames):
for comp in compNames:
if self.isComponentActive(comp):
return 1
return 0
def getDominantMood(self):
if hasattr(self, 'dominantMood'):
return self.dominantMood
dominantMood = PetMood.Neutral
priority = 1.0
for comp in PetMood.Components:
if comp in PetMood.DisabledDominants:
continue
value = self.getComponent(comp)
pri = value / max(self._getComponentThreshold(comp), 0.01)
if pri >= priority:
dominantMood = comp
priority = pri
elif comp in PetMood.AssertiveDominants and pri >= 1.0:
dominantMood = comp
self.dominantMood = dominantMood
return dominantMood
def makeCopy(self):
other = PetMood(self.getPet())
for comp in PetMood.Components:
other.__dict__[comp] = self.__dict__[comp]
return other
def start(self):
pet = self.getPet()
taskMgr.doMethodLater(simbase.petMoodDriftPeriod / simbase.petMoodTimescale * random.random(), self._driftMoodTask, self.getMoodDriftTaskName())
self.started = 1
def stop(self):
if not self.started:
return
self.started = 0
taskMgr.remove(self.getMoodDriftTaskName())
def driftMood(self, dt = None, curMood = None):
now = globalClock.getFrameTime()
if not hasattr(self, 'lastDriftTime'):
self.lastDriftTime = now
if dt is None:
dt = now - self.lastDriftTime
self.lastDriftTime = now
if dt <= 0.0:
return
if curMood is None:
curMood = self
def doDrift(curValue, timeToMedian, dt = float(dt)):
newValue = curValue + dt / (timeToMedian * 7200)
return clampScalar(newValue, 0.0, 1.0)
self.boredom = doDrift(curMood.boredom, self.tBoredom)
self.loneliness = doDrift(curMood.loneliness, self.tLoneliness)
self.sadness = doDrift(curMood.sadness, self.tSadness)
self.fatigue = doDrift(curMood.fatigue, self.tFatigue)
self.hunger = doDrift(curMood.hunger, self.tHunger)
self.confusion = doDrift(curMood.confusion, self.tConfusion)
self.excitement = doDrift(curMood.excitement, self.tExcitement)
self.surprise = doDrift(curMood.surprise, self.tSurprise)
self.affection = doDrift(curMood.affection, self.tAffection)
abuse = average(curMood.hunger, curMood.hunger, curMood.hunger, curMood.boredom, curMood.loneliness)
tipPoint = 0.6
if abuse < tipPoint:
tAnger = lerp(self.tAngerDec, -PetMood.LONGTIME, abuse / tipPoint)
else:
tAnger = lerp(PetMood.LONGTIME, self.tAngerInc, (abuse - tipPoint) / (1.0 - tipPoint))
self.anger = doDrift(curMood.anger, tAnger)
self.announceChange()
return
def _driftMoodTask(self, task = None):
self.driftMood()
taskMgr.doMethodLater(simbase.petMoodDriftPeriod / simbase.petMoodTimescale, self._driftMoodTask, self.getMoodDriftTaskName())
return Task.done
def __repr__(self):
s = '%s' % self.__class__.__name__
for comp in PetMood.Components:
s += '\n %s: %s' % (comp, self.__dict__[comp])
return s