historical/toontown-classic.git/toontown/estate/GardenManagerAI.py

368 lines
12 KiB
Python
Raw Normal View History

2024-01-16 11:20:27 -06:00
import json
import os
from direct.directnotify import DirectNotifyGlobal
from toontown.estate import GardenGlobals
from toontown.estate.DistributedAnimatedStatuaryAI import DistributedAnimatedStatuaryAI
from toontown.estate.DistributedChangingStatuaryAI import DistributedChangingStatuaryAI
from toontown.estate.DistributedFlowerAI import DistributedFlowerAI
from toontown.estate.DistributedGagTreeAI import DistributedGagTreeAI
from toontown.estate.DistributedGardenBoxAI import DistributedGardenBoxAI
from toontown.estate.DistributedGardenPlotAI import DistributedGardenPlotAI
from toontown.estate.DistributedStatuaryAI import DistributedStatuaryAI
from toontown.estate.DistributedToonStatuaryAI import DistributedToonStatuaryAI
# Structure for NULL_PLANT:
# [planted, waterLevel, lastCheck, growthLevel, optional]
NULL_PLANT = [-1, -1, 0, 0, 0]
NULL_TREES = [NULL_PLANT] * 8
NULL_FLOWERS = [NULL_PLANT] * 10
NULL_STATUARY = 0
# NULL_DATA is just a dictionary containing all the null values that are
# defined above; this makes up a default garden with nothing in it.
NULL_DATA = {'flowers': NULL_FLOWERS, 'trees': NULL_TREES, 'statuary': NULL_STATUARY}
# X position offsets for flowers & flower plots:
FLOWER_X_OFFSETS = (None, (0,), (-1.5, 1.5), (-3.4, 0, 3.5))
class GardenAI:
notify = DirectNotifyGlobal.directNotify.newCategory('GardenAI')
WANT_FLOWERS = True
WANT_TREES = True
WANT_STATUARY = True
def __init__(self, air, gardenMgr, avId):
self.air = air
self.gardenMgr = gardenMgr
self.avId = avId
self.estate = None
self._estateBoxes = None
self.trees = set()
self.flowers = set()
self.objects = set()
self.fileName = 'garden_%s.json' % avId
self.filePath = 'backups/gardens/'
try:
with open(self.filePath + self.fileName, 'r') as f:
self.data = json.load(f)
self.dbExists = True
except:
self.data = NULL_DATA.copy()
self.dbExists = False
if not self.dbExists:
# Use self.update() to setup initial db:
self.update()
self.data.pop('_id', None)
def destroy(self):
messenger.send('garden-%d-%d-going-down' % (id(self.gardenMgr), self.avId))
for tree in self.trees:
tree.requestDelete()
for flower in self.flowers:
flower.requestDelete()
for obj in self.objects:
obj.requestDelete()
self.air = None
self.estate = None
def load(self, estate):
self.estate = estate
if self.avId not in estate.activeToons:
self.notify.warning('Garden associated with unknown avatar %d, deleting...' % self.avId)
return False
houseIndex = estate.activeToons.index(self.avId)
if self.WANT_FLOWERS:
estateBoxIndex = 0
estateBoxes = []
estateBoxData = GardenGlobals.estateBoxes[houseIndex]
for x, y, h, estateBoxType in estateBoxData:
gardenBox = DistributedGardenBoxAI(self)
gardenBox.setTypeIndex(estateBoxType)
gardenBox.setPos(x, y, 0)
gardenBox.setH(h)
gardenBox.setOwnerIndex(houseIndex)
gardenBox.generateWithRequired(estate.zoneId)
self.objects.add(gardenBox)
estateBoxes.append(gardenBox)
estateBoxIndex += 1
self._estateBoxes = estateBoxes
estatePlots = GardenGlobals.estatePlots[houseIndex]
treeIndex = 0
flowerIndex = 0
for estatePlot, (x, y, h, estatePlotType) in enumerate(estatePlots):
if estatePlotType == GardenGlobals.GAG_TREE_TYPE and self.WANT_TREES:
data = self.data['trees'][treeIndex]
planted, waterLevel, lastCheck, growthLevel, lastHarvested = data
if planted != -1:
obj = self.plantTree(treeIndex, planted, waterLevel=waterLevel, lastCheck=lastCheck,
growthLevel=growthLevel, lastHarvested=lastHarvested, generate=False)
self.trees.add(obj)
else:
obj = self.placePlot(treeIndex)
obj.setPos(x, y, 0)
obj.setH(h)
obj.setPlot(estatePlot)
obj.setOwnerIndex(houseIndex)
obj.generateWithRequired(estate.zoneId)
treeIndex += 1
elif estatePlotType == GardenGlobals.FLOWER_TYPE and self.WANT_FLOWERS:
data = self.data['flowers'][flowerIndex]
planted, waterLevel, lastCheck, growthLevel, variety = data
if planted != -1:
obj = self.plantFlower(flowerIndex, planted, variety, waterLevel=waterLevel, lastCheck=lastCheck,
growthLevel=growthLevel, generate=False)
zOffset = 1.5
else:
obj = self.placePlot(flowerIndex)
obj.setFlowerIndex(flowerIndex)
zOffset = 1.2
obj.setPlot(estatePlot)
obj.setOwnerIndex(houseIndex)
# <hack>
index = (0, 1, 2, 2, 2, 3, 3, 3, 4, 4)[flowerIndex]
idx = (0, 0, 0, 1, 2, 0, 1, 2, 0, 1)[flowerIndex]
gardenBox = self._estateBoxes[index]
xOffset = FLOWER_X_OFFSETS[gardenBox.getTypeIndex()][idx]
obj.setPos(gardenBox, 0, 0, 0)
obj.setZ(gardenBox, zOffset)
obj.setX(gardenBox, xOffset)
obj.setH(gardenBox, 0)
# </hack>
obj.generateWithRequired(estate.zoneId)
flowerIndex += 1
elif estatePlotType == GardenGlobals.STATUARY_TYPE and self.WANT_STATUARY:
data = self.data['statuary']
if data == 0:
obj = self.placePlot(-1)
else:
obj = self.placeStatuary(data, generate=False)
obj.setPos(x, y, 0)
obj.setH(h)
obj.setPlot(estatePlot)
obj.setOwnerIndex(houseIndex)
obj.generateWithRequired(estate.zoneId)
for tree in self.trees:
tree.calcDependencies()
self.reconsiderAvatarOrganicBonus()
return True
def placePlot(self, treeIndex):
obj = DistributedGardenPlotAI(self)
obj.setTreeIndex(treeIndex)
self.objects.add(obj)
return obj
def getNullPlant(self):
return NULL_PLANT
def reconsiderAvatarOrganicBonus(self):
av = self.air.doId2do.get(self.avId)
if not av:
return
bonus = [-1] * 7
for track in xrange(7):
for level in xrange(8):
if not self.hasTree(track, level):
break
tree = self.getTree(track, level)
if tree.getGrowthLevel() < tree.growthThresholds[1] or tree.getWilted():
break
bonus[track] = level - 1
av.b_setTrackBonusLevel(bonus)
def hasTree(self, track, index):
treeTypeIndex = GardenGlobals.getTreeTypeIndex(track, index)
for tree in self.data['trees']:
if tree[0] == treeTypeIndex:
return True
return False
def getTree(self, track, index):
for tree in self.trees:
if tree.getTypeIndex() == GardenGlobals.getTreeTypeIndex(track, index):
return tree
def plantTree(self, treeIndex, value, plot=None, waterLevel=-1, lastCheck=0, growthLevel=0, lastHarvested=0,
ownerIndex=-1, plotId=-1, pos=None, generate=True):
if not self.air:
return
if plot:
if plot not in self.objects:
return
plot.requestDelete()
self.objects.remove(plot)
tree = DistributedGagTreeAI(self)
tree.setTypeIndex(value)
tree.setWaterLevel(waterLevel)
tree.setGrowthLevel(growthLevel)
if ownerIndex != -1:
tree.setOwnerIndex(ownerIndex)
if plotId != -1:
tree.setPlot(plotId)
if pos is not None:
pos, h = pos
tree.setPos(pos)
tree.setH(h)
tree.setTreeIndex(treeIndex)
tree.calculate(lastHarvested, lastCheck)
self.trees.add(tree)
if generate:
tree.generateWithRequired(self.estate.zoneId)
return tree
def placeStatuary(self, data, plot=None, plotId=-1, ownerIndex=-1, pos=None, generate=True):
if not self.air:
return
if plot:
if plot not in self.objects:
return
plot.requestDelete()
self.objects.remove(plot)
data, lastCheck, index, growthLevel = self.S_unpack(data)
dclass = DistributedStatuaryAI
if index in GardenGlobals.ToonStatuaryTypeIndices:
dclass = DistributedToonStatuaryAI
elif index in GardenGlobals.ChangingStatuaryTypeIndices:
dclass = DistributedChangingStatuaryAI
elif index in GardenGlobals.AnimatedStatuaryTypeIndices:
dclass = DistributedAnimatedStatuaryAI
obj = dclass(self)
obj.setGrowthLevel(growthLevel)
obj.setTypeIndex(index)
obj.setOptional(data)
if ownerIndex != -1:
obj.setOwnerIndex(ownerIndex)
if plotId != -1:
obj.setPlot(plotId)
if pos is not None:
pos, h = pos
obj.setPos(pos)
obj.setH(h)
obj.calculate(lastCheck)
self.objects.add(obj)
if generate:
obj.announceGenerate()
return obj
def plantFlower(self, flowerIndex, species, variety, plot=None, waterLevel=-1, lastCheck=0, growthLevel=0,
ownerIndex=-1, plotId=-1, generate=True):
if not self.air:
return
if plot:
if plot not in self.objects:
return
plot.requestDelete()
self.objects.remove(plot)
flower = DistributedFlowerAI(self)
flower.setTypeIndex(species)
flower.setVariety(variety)
flower.setWaterLevel(waterLevel)
flower.setGrowthLevel(growthLevel)
if ownerIndex != -1:
flower.setOwnerIndex(ownerIndex)
if plotId != -1:
flower.setPlot(plotId)
flower.setFlowerIndex(flowerIndex)
flower.calculate(lastCheck)
self.flowers.add(flower)
if generate:
flower.generateWithRequired(self.estate.zoneId)
return flower
# Data structure
# VERY HIGH (vh) (64-bit)
# high high (H) = data (32-bit)
# high low (L) = lastCheck (32-bit)
# VERY LOW (vl) (16-bit)
# low high (h) = index (8-bit)
# low low (l) = growthLevel (8-bit)
@staticmethod
def S_pack(data, lastCheck, index, growthLevel):
vh = data << 32 | lastCheck
vl = index << 8 | growthLevel
return vh << 16 | vl
@staticmethod
def S_unpack(x):
vh = x >> 16
vl = x & 0xFFFF
data = vh >> 32
lastCheck = vh & 0xFFFFFFFF
index = vl >> 8
growthLevel = vl & 0xFF
return data, lastCheck, index, growthLevel
def update(self):
if not os.path.exists(self.filePath):
os.makedirs(self.filePath)
with open(self.filePath + self.fileName, 'w+') as f:
json.dump(self.data, f)
class GardenManagerAI:
notify = DirectNotifyGlobal.directNotify.newCategory('GardenManagerAI')
def __init__(self, air, estate):
self.air = air
self.estate = estate
self.gardens = {}
def loadGarden(self, avId):
garden = GardenAI(self.air, self, avId)
result = garden.load(self.estate)
if result:
self.gardens[avId] = garden
def destroy(self):
for garden in self.gardens.values():
garden.destroy()
del self.gardens