oldschool-toontown/toontown/pets/PetTraits.py

250 lines
9.3 KiB
Python
Raw Normal View History

from direct.showbase.PythonUtil import randFloat, normalDistrib
2019-11-02 17:27:54 -05:00
from direct.showbase.PythonUtil import clampScalar
from toontown.toonbase import TTLocalizer, ToontownGlobals
import random, copy
from enum import IntEnum
2019-11-02 17:27:54 -05:00
TraitDivisor = 10000
def getTraitNames():
if not hasattr(PetTraits, 'TraitNames'):
traitNames = []
for desc in PetTraits.TraitDescs:
traitNames.append(desc[0])
PetTraits.TraitNames = traitNames
return PetTraits.TraitNames
def uniform(min, max, rng):
return randFloat(min, max, rng.random)
def gaussian(min, max, rng):
return normalDistrib(min, max, rng.gauss)
class TraitDistribution:
2022-11-08 08:14:06 -06:00
TraitQuality = IntEnum('TraitQuality', ('VERY_BAD', 'BAD', 'AVERAGE', 'GOOD', 'VERY_GOOD'), start=0)
TraitTypes = IntEnum('TraitTypes', ('INCREASING', 'DECREASING'), start=0)
2019-11-02 17:27:54 -05:00
Sz2MinMax = None
TraitType = None
TraitCutoffs = {TraitTypes.INCREASING: {TraitQuality.VERY_BAD: 0.1,
TraitQuality.BAD: 0.25,
TraitQuality.GOOD: 0.75,
TraitQuality.VERY_GOOD: 0.9},
TraitTypes.DECREASING: {TraitQuality.VERY_BAD: 0.9,
TraitQuality.BAD: 0.75,
TraitQuality.GOOD: 0.25,
TraitQuality.VERY_GOOD: 0.1}}
def __init__(self, rndFunc = gaussian):
self.rndFunc = rndFunc
if not hasattr(self.__class__, 'GlobalMinMax'):
_min = 1.0
_max = 0.0
minMax = self.Sz2MinMax
for sz in minMax:
thisMin, thisMax = minMax[sz]
_min = min(_min, thisMin)
_max = max(_max, thisMax)
self.__class__.GlobalMinMax = [_min, _max]
def getRandValue(self, szId, rng = random):
min, max = self.getMinMax(szId)
return self.rndFunc(min, max, rng)
def getHigherIsBetter(self):
return self.TraitType == TraitDistribution.TraitTypes.INCREASING
def getMinMax(self, szId):
return (self.Sz2MinMax[szId][0], self.Sz2MinMax[szId][1])
def getGlobalMinMax(self):
return (self.GlobalMinMax[0], self.GlobalMinMax[1])
def _getTraitPercent(self, traitValue):
gMin, gMax = self.getGlobalMinMax()
if traitValue < gMin:
gMin = traitValue
elif traitValue > gMax:
gMax = traitValue
return (traitValue - gMin) / (gMax - gMin)
def getPercentile(self, traitValue):
if self.TraitType is TraitDistribution.TraitTypes.INCREASING:
return self._getTraitPercent(traitValue)
else:
return 1.0 - self._getTraitPercent(traitValue)
def getQuality(self, traitValue):
TraitQuality = TraitDistribution.TraitQuality
TraitCutoffs = self.TraitCutoffs[self.TraitType]
percent = self._getTraitPercent(traitValue)
if self.TraitType is TraitDistribution.TraitTypes.INCREASING:
if percent <= TraitCutoffs[TraitQuality.VERY_BAD]:
return TraitQuality.VERY_BAD
elif percent <= TraitCutoffs[TraitQuality.BAD]:
return TraitQuality.BAD
elif percent >= TraitCutoffs[TraitQuality.VERY_GOOD]:
return TraitQuality.VERY_GOOD
elif percent >= TraitCutoffs[TraitQuality.GOOD]:
return TraitQuality.GOOD
else:
return TraitQuality.AVERAGE
elif percent <= TraitCutoffs[TraitQuality.VERY_GOOD]:
return TraitQuality.VERY_GOOD
elif percent <= TraitCutoffs[TraitQuality.GOOD]:
return TraitQuality.GOOD
elif percent >= TraitCutoffs[TraitQuality.VERY_BAD]:
return TraitQuality.VERY_BAD
elif percent >= TraitCutoffs[TraitQuality.BAD]:
return TraitQuality.BAD
else:
return TraitQuality.AVERAGE
def getExtremeness(self, traitValue):
percent = self._getTraitPercent(traitValue)
if percent < 0.5:
howExtreme = (0.5 - percent) * 2.0
else:
howExtreme = (percent - 0.5) * 2.0
return clampScalar(howExtreme, 0.0, 1.0)
class PetTraits:
class StdIncDistrib(TraitDistribution):
TraitType = TraitDistribution.TraitTypes.INCREASING
Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.2, 0.65),
ToontownGlobals.DonaldsDock: (0.3, 0.7),
ToontownGlobals.DaisyGardens: (0.4, 0.75),
ToontownGlobals.MinniesMelodyland: (0.5, 0.8),
ToontownGlobals.TheBrrrgh: (0.6, 0.85),
ToontownGlobals.DonaldsDreamland: (0.7, 0.9)}
class StdDecDistrib(TraitDistribution):
TraitType = TraitDistribution.TraitTypes.DECREASING
Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.35, 0.8),
ToontownGlobals.DonaldsDock: (0.3, 0.7),
ToontownGlobals.DaisyGardens: (0.25, 0.6),
ToontownGlobals.MinniesMelodyland: (0.2, 0.5),
ToontownGlobals.TheBrrrgh: (0.15, 0.4),
ToontownGlobals.DonaldsDreamland: (0.1, 0.3)}
class ForgetfulnessDistrib(TraitDistribution):
TraitType = TraitDistribution.TraitTypes.DECREASING
Sz2MinMax = {ToontownGlobals.ToontownCentral: (0.0, 1.0),
ToontownGlobals.DonaldsDock: (0.0, 0.9),
ToontownGlobals.DaisyGardens: (0.0, 0.8),
ToontownGlobals.MinniesMelodyland: (0.0, 0.7),
ToontownGlobals.TheBrrrgh: (0.0, 0.6),
ToontownGlobals.DonaldsDreamland: (0.0, 0.5)}
TraitDescs = (('forgetfulness', ForgetfulnessDistrib(), True),
('boredomThreshold', StdIncDistrib(), True),
('restlessnessThreshold', StdIncDistrib(), True),
('playfulnessThreshold', StdDecDistrib(), True),
('lonelinessThreshold', StdIncDistrib(), True),
('sadnessThreshold', StdIncDistrib(), True),
('fatigueThreshold', StdIncDistrib(), True),
('hungerThreshold', StdIncDistrib(), True),
('confusionThreshold', StdIncDistrib(), True),
('excitementThreshold', StdDecDistrib(), True),
('angerThreshold', StdIncDistrib(), True),
('surpriseThreshold', StdIncDistrib(), False),
('affectionThreshold', StdDecDistrib(), True))
NumTraits = len(TraitDescs)
class Trait:
def __init__(self, index, traitsObj, value = None):
self.name, distrib, self.hasWorth = PetTraits.TraitDescs[index]
if value is not None:
self.value = value
else:
szId = traitsObj.safeZoneId
self.value = distrib.getRandValue(szId, traitsObj.rng)
self.value = int(self.value * TraitDivisor) / float(TraitDivisor)
self.higherIsBetter = distrib.getHigherIsBetter()
self.percentile = distrib.getPercentile(self.value)
self.quality = distrib.getQuality(self.value)
self.howExtreme = distrib.getExtremeness(self.value)
return
def __repr__(self):
return 'Trait: %s, %s, %s, %s' % (self.name,
self.value,
TraitDistribution.TraitQuality.getString(self.quality),
self.howExtreme)
def __init__(self, traitSeed, safeZoneId, traitValueList = []):
self.traitSeed = traitSeed
self.safeZoneId = safeZoneId
self.rng = random.Random(self.traitSeed)
self.traits = {}
for i in range(len(PetTraits.TraitDescs)):
2019-11-02 17:27:54 -05:00
if i < len(traitValueList) and traitValueList[i] > 0.0:
trait = PetTraits.Trait(i, self, traitValueList[i])
else:
trait = PetTraits.Trait(i, self)
self.traits[trait.name] = trait
self.__dict__[trait.name] = trait.value
extremeTraits = []
for trait in list(self.traits.values()):
2019-11-02 17:27:54 -05:00
if not trait.hasWorth:
continue
if trait.quality == TraitDistribution.TraitQuality.AVERAGE:
continue
i = 0
while i < len(extremeTraits) and extremeTraits[i].howExtreme > trait.howExtreme:
i += 1
extremeTraits.insert(i, trait)
self.extremeTraits = []
for trait in extremeTraits:
self.extremeTraits.append((trait.name, trait.quality))
def getValueList(self):
traitValues = []
for desc in PetTraits.TraitDescs:
traitName = desc[0]
traitValues.append(self.traits[traitName].value)
return traitValues
def getTraitValue(self, traitName):
return self.traits[traitName].value
def getExtremeTraits(self):
return copy.copy(self.extremeTraits)
def getOverallValue(self):
total = 0
numUsed = 0
for trait in list(self.traits.values()):
2019-11-02 17:27:54 -05:00
if trait.hasWorth:
if trait.higherIsBetter:
value = trait.value
else:
value = 1.0 - trait.value
total += value
numUsed += 1
value = total / len(list(self.traits.values()))
2019-11-02 17:27:54 -05:00
return value
def getExtremeTraitDescriptions(self):
descs = []
TraitQuality = TraitDistribution.TraitQuality
Quality2index = {TraitQuality.VERY_BAD: 0,
TraitQuality.BAD: 1,
TraitQuality.GOOD: 2,
TraitQuality.VERY_GOOD: 3}
for name, quality in self.extremeTraits:
descs.append(TTLocalizer.PetTrait2descriptions[name][Quality2index[quality]])
return descs