353 lines
15 KiB
Python
353 lines
15 KiB
Python
import time
|
|
from datetime import datetime
|
|
|
|
from direct.directnotify import DirectNotifyGlobal
|
|
from direct.distributed.ClockDelta import globalClockDelta
|
|
|
|
from toontown.catalog import CatalogItem
|
|
from toontown.catalog.CatalogInvalidItem import CatalogInvalidItem
|
|
from toontown.catalog.CatalogItemList import CatalogItemList
|
|
from toontown.catalog.GiftAvatarAI import GiftAvatarAI
|
|
from toontown.estate import PhoneGlobals
|
|
from toontown.estate.DistributedFurnitureItemAI import DistributedFurnitureItemAI
|
|
from toontown.toonbase import ToontownGlobals
|
|
|
|
|
|
class LoadGiftAvatar:
|
|
|
|
def __init__(self, phone, avId, targetId, optional, callback):
|
|
self.air = phone.air
|
|
self.phone = phone
|
|
self.avId = avId
|
|
self.targetId = targetId
|
|
self.optional = optional
|
|
self.callback = callback
|
|
|
|
def start(self):
|
|
self.air.dbInterface.queryObject(self.air.dbId, self.targetId, self.__gotAvatar)
|
|
|
|
def copyDict(self, aDict, *keys):
|
|
return {key: aDict[key] for key in keys}
|
|
|
|
def __gotAvatar(self, dclass, fields):
|
|
if dclass != self.air.dclassesByName['DistributedToonAI']:
|
|
return
|
|
|
|
for key in (
|
|
'setDNAString', 'setMailboxContents', 'setAwardMailboxContents', 'setGiftSchedule',
|
|
'setDeliverySchedule', 'setAwardSchedule'):
|
|
fields[key] = fields[key][0].encode('base64')
|
|
|
|
newDict = self.copyDict(fields, 'setDNAString', 'setMailboxContents', 'setAwardMailboxContents',
|
|
'setGiftSchedule', 'setDeliverySchedule', 'setAwardSchedule', 'setHat', 'setGlasses',
|
|
'setBackpack', 'setShoes', 'setHatList', 'setGlassesList', 'setBackpackList',
|
|
'setShoesList', 'setCustomMessages', 'setEmoteAccess', 'setClothesTopsList',
|
|
'setClothesBottomsList', 'setPetTrickPhrases')
|
|
|
|
self.callback(self.avId, self.targetId, newDict, self.optional)
|
|
del self.phone.giftingOperations[self.avId]
|
|
|
|
|
|
class DistributedPhoneAI(DistributedFurnitureItemAI):
|
|
notify = DirectNotifyGlobal.directNotify.newCategory('DistributedPhoneAI')
|
|
|
|
def __init__(self, air, house, furnitureMgr, catalogItem):
|
|
DistributedFurnitureItemAI.__init__(self, air, house, furnitureMgr, catalogItem)
|
|
self.house = house
|
|
self.furnitureMgr = furnitureMgr
|
|
self.sx = 1
|
|
self.sy = 1
|
|
self.sz = 1
|
|
self.initialScale = (self.sx, self.sy, self.sz)
|
|
self.newScale = None
|
|
self.avId = 0
|
|
self.giftingOperations = {}
|
|
|
|
def getInitialScale(self):
|
|
return self.initialScale
|
|
|
|
def setNewScale(self, sx, sy, sz):
|
|
(self.sx, self.sy, self.sz) = (sx, sy, sz)
|
|
self.newScale = (self.sx, self.sy, self.sz)
|
|
|
|
def getNewScale(self):
|
|
return self.newScale
|
|
|
|
def avatarEnter(self):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if self.avId:
|
|
self.sendUpdateToAvatarId(avId, 'freeAvatar', [])
|
|
return
|
|
|
|
av = self.air.doId2do.get(avId)
|
|
if not av:
|
|
return
|
|
|
|
if not any((av.weeklyCatalog, av.backCatalog, av.monthlyCatalog)):
|
|
self.d_setMovie(PhoneGlobals.PHONE_MOVIE_EMPTY, avId)
|
|
self.d_setMovie(PhoneGlobals.PHONE_MOVIE_CLEAR, 0)
|
|
return
|
|
|
|
houseId = av.getHouseId()
|
|
if not houseId:
|
|
self.sendUpdateToAvatarId(avId, 'freeAvatar', [])
|
|
return
|
|
|
|
self.air.questManager.toonCalledClarabelle(av)
|
|
house = self.air.doId2do.get(houseId)
|
|
self.avId = avId
|
|
self.d_setMovie(PhoneGlobals.PHONE_MOVIE_PICKUP, avId)
|
|
if house:
|
|
numItems = self.furnitureMgr.getNumItems()
|
|
self.sendUpdateToAvatarId(avId, 'setLimits', [numItems])
|
|
else:
|
|
self.air.dbInterface.queryObject(self.air.dbId, houseId, self.__handleHouse)
|
|
|
|
# Remove the "new catalog" icon:
|
|
av.b_setCatalogNotify(ToontownGlobals.NoItems, av.mailboxNotify)
|
|
self.acceptOnce(self.air.getAvatarExitEvent(avId), self.__handleUnexpectedExit, extraArgs=[avId])
|
|
|
|
def __handleHouse(self, dclass, fields):
|
|
if dclass != self.air.dclassesByName['DistributedHouseAI']:
|
|
return
|
|
|
|
interiorItems = CatalogItemList(fields['setInteriorItems'][0], store=CatalogItem.Customization)
|
|
atticItems = CatalogItemList(fields['setAtticItems'][0], store=CatalogItem.Customization)
|
|
atticWallpaper = CatalogItemList(fields['setAtticWallpaper'][0], store=CatalogItem.Customization)
|
|
atticWindows = CatalogItemList(fields['setAtticWindows'][0], store=CatalogItem.Customization)
|
|
interiorWallpaper = CatalogItemList(fields['setInteriorWallpaper'][0], store=CatalogItem.Customization)
|
|
interiorWindows = CatalogItemList(fields['setInteriorWindows'][0], store=CatalogItem.Customization)
|
|
numItems = len(interiorItems) + len(atticItems) + len(atticWallpaper) + len(atticWindows) + len(
|
|
interiorWallpaper) + len(interiorWindows)
|
|
self.sendUpdateToAvatarId(fields['setAvatarId'][0], 'setLimits', [numItems])
|
|
|
|
def __handleUnexpectedExit(self, avId):
|
|
if avId != self.avId:
|
|
self.notify.warning('accepted exit event for av %s not using phone' % avId)
|
|
return
|
|
|
|
self.avId = 0
|
|
self.d_setMovie(PhoneGlobals.PHONE_MOVIE_CLEAR, 0)
|
|
|
|
def avatarExit(self):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
if avId != self.avId:
|
|
self.notify.warning('av %s tried to exit phone they were not using' % avId)
|
|
return
|
|
|
|
self.d_setMovie(PhoneGlobals.PHONE_MOVIE_HANGUP, avId)
|
|
self.d_setMovie(PhoneGlobals.PHONE_MOVIE_CLEAR, 0)
|
|
self.avId = 0
|
|
self.ignore(self.air.getAvatarExitEvent(avId))
|
|
|
|
def d_setMovie(self, mode, avId):
|
|
self.sendUpdate('setMovie', [mode, avId, globalClockDelta.getRealNetworkTime(bits=32)])
|
|
|
|
def requestPurchaseMessage(self, context, blob, optional):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
accId = self.air.getAccountIdFromSender()
|
|
av = self.air.doId2do.get(avId)
|
|
if not av:
|
|
self.air.writeServerEvent('suspicious', avId,
|
|
'av tried to purchase something but they are not on the district!')
|
|
return
|
|
|
|
if avId != self.avId:
|
|
self.air.writeServerEvent('suspicious', avId, 'av tried to purchase item while not at phone')
|
|
self.sendUpdateToAvatarId(avId, 'requestPurchaseResponse', [context, ToontownGlobals.P_NotShopping])
|
|
return
|
|
|
|
item = CatalogItem.getItem(blob, store=CatalogItem.Customization)
|
|
if isinstance(item, CatalogInvalidItem):
|
|
self.air.writeServerEvent('suspicious', avId, 'av tried to purchase something but item is invalid!')
|
|
self.sendUpdateToAvatarId(avId, 'requestPurchaseResponse', [context, ToontownGlobals.P_ItemAvailable])
|
|
return
|
|
|
|
if not any(item in catalog for catalog in (av.weeklyCatalog, av.backCatalog, av.monthlyCatalog)):
|
|
self.air.writeServerEvent('suspicious', avId, 'av tried to purchase item not in catalog')
|
|
self.sendUpdateToAvatarId(avId, 'requestPurchaseResponse', [context, ToontownGlobals.P_NotInCatalog])
|
|
return
|
|
|
|
def handleAccount(dclass, fields):
|
|
if dclass != self.air.dclassesByName['AccountAI']:
|
|
return
|
|
|
|
# Based on game creation date:
|
|
creationDate = fields['CREATED']
|
|
try:
|
|
creationDate = datetime.fromtimestamp(time.mktime(time.strptime(creationDate)))
|
|
except ValueError:
|
|
creationDate = ''
|
|
|
|
accountDays = -1
|
|
if creationDate:
|
|
now = datetime.fromtimestamp(time.mktime(time.strptime(time.ctime())))
|
|
accountDays = abs((now - creationDate).days)
|
|
|
|
if accountDays < 0 or accountDays > 4294967295:
|
|
accountDays = 100000
|
|
|
|
daysToGo = item.loyaltyRequirement() - accountDays
|
|
if daysToGo < 0:
|
|
daysToGo = 0
|
|
|
|
if daysToGo and config.GetBool('want-loyalty-requirement', False):
|
|
self.air.writeServerEvent('suspicious', avId,
|
|
'av tried to purchase a loyalty item before it is available!')
|
|
return
|
|
|
|
if item in av.backCatalog:
|
|
price = item.getPrice(CatalogItem.CatalogTypeBackorder)
|
|
else:
|
|
price = item.getPrice(0)
|
|
|
|
if price == 0:
|
|
self.air.writeServerEvent('suspicious', avId,
|
|
'av tried to purchase something but the price is invalid!')
|
|
return
|
|
|
|
# Take the jellybeans away:
|
|
if price > av.getTotalMoney():
|
|
self.air.writeServerEvent('suspicious', avId, 'Failed to take away the jellybeans from the avatar')
|
|
self.sendUpdateToAvatarId(avId, 'requestPurchaseResponse', [context, ToontownGlobals.P_NotEnoughMoney])
|
|
return
|
|
|
|
if len(av.mailboxContents) >= ToontownGlobals.MaxMailboxContents:
|
|
self.sendUpdateToAvatarId(avId, 'requestPurchaseResponse', [context, ToontownGlobals.P_MailboxFull])
|
|
return
|
|
|
|
if len(av.onOrder) + len(av.mailboxContents) + len(
|
|
av.onGiftOrder) + 1 >= ToontownGlobals.MaxMailboxContents:
|
|
self.sendUpdateToAvatarId(avId, 'requestPurchaseResponse', [context, ToontownGlobals.P_OnOrderListFull])
|
|
return
|
|
|
|
retCode = ToontownGlobals.P_ItemOnOrder
|
|
if not item.getDeliveryTime():
|
|
retCode = item.recordPurchase(av, optional)
|
|
deliveryTime = 0
|
|
elif config.GetBool('want-instant-delivery', False):
|
|
# Do we want instant delivery?
|
|
deliveryTime = int(time.time() / 60) + 1
|
|
else:
|
|
deliveryTime = int(time.time() / 60) + item.getDeliveryTime()
|
|
|
|
# Place the item for order:
|
|
if deliveryTime:
|
|
item.deliveryDate = deliveryTime
|
|
av.onOrder.append(item)
|
|
av.b_setDeliverySchedule(av.onOrder)
|
|
|
|
self.sendUpdateToAvatarId(avId, 'requestPurchaseResponse', [context, retCode])
|
|
if retCode in (ToontownGlobals.P_ItemOnOrder, ToontownGlobals.P_ItemAvailable):
|
|
av.takeMoney(price)
|
|
|
|
self.air.dbInterface.queryObject(self.air.dbId, accId, handleAccount)
|
|
|
|
def requestGiftPurchaseMessage(self, context, targetId, blob, optional):
|
|
avId = self.air.getAvatarIdFromSender()
|
|
accId = self.air.getAccountIdFromSender()
|
|
av = self.air.doId2do.get(avId)
|
|
if not av:
|
|
self.air.writeServerEvent('suspicious', avId,
|
|
'av tried to gift something but they are not on the district!')
|
|
return
|
|
|
|
if avId != self.avId:
|
|
self.air.writeServerEvent('suspicious', avId, 'av tried to gift item while not at phone')
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse', [context, ToontownGlobals.P_NotShopping])
|
|
return
|
|
|
|
item = CatalogItem.getItem(blob, store=CatalogItem.Customization)
|
|
if isinstance(item, CatalogInvalidItem):
|
|
self.air.writeServerEvent('suspicious', avId, 'av tried to gift something but item is invalid!')
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse', [context, ToontownGlobals.P_ItemAvailable])
|
|
return
|
|
|
|
if not any(item in catalog for catalog in (av.weeklyCatalog, av.backCatalog, av.monthlyCatalog)):
|
|
self.air.writeServerEvent('suspicious', avId, 'av tried to gift item not in catalog')
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse', [context, ToontownGlobals.P_NotInCatalog])
|
|
return
|
|
|
|
def handleAccount(dclass, fields):
|
|
if dclass != self.air.dclassesByName['AccountAI']:
|
|
return
|
|
|
|
# Based on game creation date:
|
|
creationDate = fields['CREATED']
|
|
try:
|
|
creationDate = datetime.fromtimestamp(time.mktime(time.strptime(creationDate)))
|
|
except ValueError:
|
|
creationDate = ''
|
|
|
|
accountDays = -1
|
|
if creationDate:
|
|
now = datetime.fromtimestamp(time.mktime(time.strptime(time.ctime())))
|
|
accountDays = abs((now - creationDate).days)
|
|
|
|
if accountDays < 0 or accountDays > 4294967295:
|
|
accountDays = 100000
|
|
|
|
daysToGo = item.loyaltyRequirement() - accountDays
|
|
if daysToGo < 0:
|
|
daysToGo = 0
|
|
|
|
if daysToGo and config.GetBool('want-loyalty-requirement', False):
|
|
self.air.writeServerEvent('suspicious', avId,
|
|
'av tried to gift a loyalty item before it is available!')
|
|
return
|
|
|
|
if not item.isGift():
|
|
self.air.writeServerEvent('suspicious', avId, 'av tried to gift an item that isn\'t a gift!')
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse', [context, ToontownGlobals.P_NotAGift])
|
|
return
|
|
|
|
if item in av.backCatalog:
|
|
price = item.getPrice(CatalogItem.CatalogTypeBackorder)
|
|
else:
|
|
price = item.getPrice(0)
|
|
|
|
# Take the jellybeans away:
|
|
if price > av.getTotalMoney():
|
|
self.air.writeServerEvent('suspicious', avId, 'Failed to take away the jellybeans from the avatar')
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse',
|
|
[context, ToontownGlobals.P_NotEnoughMoney])
|
|
return
|
|
|
|
self.requestGiftAvatarOperation(avId, targetId, [context, item, price], self.attemptGiftPurchase)
|
|
|
|
self.air.dbInterface.queryObject(self.air.dbId, accId, handleAccount)
|
|
|
|
def requestGiftAvatarOperation(self, avId, doId, optional, callback):
|
|
if avId in self.giftingOperations:
|
|
return
|
|
|
|
newGiftingOperation = LoadGiftAvatar(self, avId, doId, optional, callback)
|
|
newGiftingOperation.start()
|
|
self.giftingOperations[avId] = newGiftingOperation
|
|
|
|
def attemptGiftPurchase(self, avId, targetId, avatar, optional):
|
|
av = self.air.doId2do.get(avId)
|
|
if not av:
|
|
return
|
|
|
|
recipient = GiftAvatarAI.createFromFields(avatar)
|
|
context = optional[0]
|
|
item = optional[1]
|
|
if len(recipient.mailboxContents) >= ToontownGlobals.MaxMailboxContents:
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse', [context, ToontownGlobals.P_MailboxFull])
|
|
return
|
|
|
|
if len(recipient.onOrder) + len(recipient.mailboxContents) + len(
|
|
recipient.onGiftOrder) + 1 >= ToontownGlobals.MaxMailboxContents:
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse', [context, ToontownGlobals.P_OnOrderListFull])
|
|
return
|
|
|
|
if item.reachedPurchaseLimit(recipient):
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse',
|
|
[context, ToontownGlobals.P_ReachedPurchaseLimit])
|
|
return
|
|
|
|
retCode = ToontownGlobals.P_ItemOnOrder
|
|
av.takeMoney(optional[2])
|
|
recipient.addToGiftSchedule(avId, targetId, item, item.getDeliveryTime())
|
|
self.sendUpdateToAvatarId(avId, 'requestGiftPurchaseResponse', [context, retCode])
|