from direct.showbase.PythonUtil import randFloat, normalDistrib from direct.showbase.PythonUtil import clampScalar from toontown.toonbase import TTLocalizer, ToontownGlobals import random, copy from enum import IntEnum 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: TraitQuality = IntEnum('TraitQuality', ('VERY_BAD', 'BAD', 'AVERAGE', 'GOOD', 'VERY_GOOD')) TraitTypes = IntEnum('TraitTypes', ('INCREASING', 'DECREASING')) 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)): 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()): 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()): 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())) 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