2022-09-19 15:47:30 -05:00
|
|
|
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
|
2022-09-19 15:47:30 -05:00
|
|
|
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 = {}
|
2019-12-30 00:07:56 -06:00
|
|
|
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 = []
|
2019-12-30 00:07:56 -06:00
|
|
|
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
|
2019-12-30 00:07:56 -06:00
|
|
|
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
|
|
|
|
|
2019-12-30 00:07:56 -06:00
|
|
|
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
|