class DistributedFurnitureManagerAI(DistributedObjectAI):
    notify = DirectNotifyGlobal.directNotify.newCategory('DistributedFurnitureManagerAI')

    def __init__(self, air, house, interior):
        DistributedObjectAI.__init__(self, air)
        self.house = house
        self.interior = interior
        self.director = None
        self.ownerId = house.avatarId
        self.ownerName = house.name
        self.avId = None
        self.atticItems = None
        self.atticWallpaper = None
        self.wallpaper = None
        self.atticWindows = None
        self.windows = None
        self.deletedItems = None
        self.items = []
        self.loadFromHouse()

    def announceGenerate(self):
        DistributedObjectAI.announceGenerate(self)
        for item in self.items:
            item.generateWithRequired(self.zoneId)

    def delete(self):
        for item in self.items:
            item.destroy()

        DistributedObjectAI.delete(self)

    def loadFromHouse(self):
        self.b_setAtticItems(self.house.getAtticItems())
        self.b_setAtticWallpaper(self.house.getAtticWallpaper())
        self.b_setAtticWindows(self.house.getAtticWindows())
        self.b_setDeletedItems(self.house.getDeletedItems())
        self.wallpaper = CatalogItemList(self.house.getInteriorWallpaper(), store=CatalogItem.Customization)
        self.applyWallpaper()
        self.windows = CatalogItemList(self.house.getInteriorWindows(), store=CatalogItem.Customization | CatalogItem.WindowPlacement)
        self.applyWindows()
        self.setItems(self.house.getInteriorItems())

    def saveToHouse(self):
        self.house.b_setAtticItems(self.getAtticItems())
        self.house.b_setAtticWallpaper(self.getAtticWallpaper())
        self.house.b_setAtticWindows(self.getAtticWindows())
        self.house.b_setDeletedItems(self.getDeletedItems())
        self.house.b_setInteriorWallpaper(self.wallpaper.getBlob())
        self.house.b_setInteriorWindows(self.windows.getBlob())
        self.house.b_setInteriorItems(self.getItems())

    def applyWallpaper(self):
        self.interior.b_setWallpaper(self.wallpaper.getBlob())

    def applyWindows(self):
        self.interior.b_setWindows(self.windows.getBlob())

    def setItems(self, items):
        items = CatalogItemList(items, store=CatalogItem.Customization | CatalogItem.Location)
        for item in self.items:
            item.destroy()

        self.items = []
        for item in items:
            if item.getHashContents() in (1300, 1310, 1320, 1330, 1340, 1350):
                do = DistributedBankAI(self.air, self, item)
            elif 500 <= item.getHashContents() <= 518:
                do = DistributedClosetAI(self.air, self, item)
                do.setOwnerId(self.avId)
            elif item.getHashContents() == 1399:
                do = DistributedPhoneAI(self.air, self, item)
            elif item.getHashContents() in (4000, 4010):
                do = DistributedTrunkAI(self.air, self, item)
                do.setOwnerId(self.avId)
            else:
                do = DistributedFurnitureItemAI(self.air, self, item)
            if self.isGenerated():
                do.generateWithRequired(self.zoneId)
            self.items.append(do)

    def getItems(self):
        items = CatalogItemList(store=CatalogItem.Customization | CatalogItem.Location)
        for item in self.items:
            items.append(item.catalogItem)

        return items.getBlob()

    def setOwnerId(self, ownerId):
        self.ownerId = ownerId

    def d_setOwnerId(self, ownerId):
        self.sendUpdate('setOwnerId', [ownerId])

    def b_setOwnerId(self, ownerId):
        self.setOwnerId(ownerId)
        self.d_setOwnerId(ownerId)

    def getOwnerId(self):
        return self.ownerId

    def setOwnerName(self, ownerName):
        self.ownerName = ownerName

    def d_setOwnerName(self, ownerName):
        self.sendUpdate('setOwnerName', [ownerName])

    def b_setOwnerName(self, ownerName):
        self.setOwnerName(ownerName)
        self.d_setOwnerName(ownerName)

    def getOwnerName(self):
        return self.ownerName

    def getInteriorId(self):
        return self.interior.doId

    def setAtticItems(self, items):
        self.atticItems = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticItems(self, items):
        self.sendUpdate('setAtticItems', [items])

    def b_setAtticItems(self, items):
        self.setAtticItems(items)
        if self.isGenerated():
            self.d_setAtticItems(items)

    def getAtticItems(self):
        return self.atticItems.getBlob()

    def setAtticWallpaper(self, items):
        self.atticWallpaper = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticWallpaper(self, items):
        self.sendUpdate('setAtticWallpaper', [items])

    def b_setAtticWallpaper(self, items):
        self.setAtticWallpaper(items)
        if self.isGenerated():
            self.d_setAtticWallpaper(items)

    def getAtticWallpaper(self):
        return self.atticWallpaper.getBlob()

    def setAtticWindows(self, items):
        self.atticWindows = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticWindows(self, items):
        self.sendUpdate('setAtticWindows', [items])

    def b_setAtticWindows(self, items):
        self.setAtticWindows(items)
        if self.isGenerated():
            self.d_setAtticWindows(items)

    def getAtticWindows(self):
        return self.atticWindows.getBlob()

    def setDeletedItems(self, items):
        self.deletedItems = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setDeletedItems(self, items):
        self.sendUpdate('setDeletedItems', [items])

    def b_setDeletedItems(self, items):
        self.setDeletedItems(items)
        if self.isGenerated():
            self.d_setDeletedItems(items)

    def getDeletedItems(self):
        return self.deletedItems.getBlob()

    def suggestDirector(self, directorId):
        senderId = self.air.getAvatarIdFromSender()
        if self.ownerId != senderId:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to move furniture, but not the house owner!')
            return
        if senderId != directorId and directorId != 0:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to make someone else (%d) move their furniture!' % directorId)
            return
        director = self.air.doId2do.get(directorId)
        if directorId and not director:
            self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to move furniture without being on the shard!')
            return
        if self.director:
            self.director.b_setGhostMode(0)
        if director:
            if director.zoneId != self.zoneId:
                self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to become director from another zone!')
                return
            director.b_setGhostMode(1)
        self.director = director
        self.sendUpdate('setDirector', [directorId])
        self.saveToHouse()

    def avatarEnter(self):
        pass

    def avatarExit(self):
        pass

    def moveItemToAttic(self, doId):
        item = self.getItemObject(doId)
        self.atticItems.append(item.catalogItem)
        self.d_setAtticItems(self.getAtticItems())
        item.destroy()
        self.items.remove(item)
        return ToontownGlobals.FM_MovedItem

    def moveItemFromAttic(self, index, x, y, z, h, p, r):
        item = self.getAtticFurniture(self.atticItems, index)
        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())
        item.posHpr = (x,
         y,
         z,
         h,
         p,
         r)
        if item.getFlags() & FLTrunk:
            if self.house.gender is 0:
                if item.furnitureType - 4000 < 10:
                    item.furnitureType += 10
            elif item.furnitureType - 4000 > 10:
                item.furnitureType -= 10
            do = DistributedTrunkAI(self.air, self, item)
        elif item.getFlags() & FLCloset:
            if self.house.gender is 0:
                if item.furnitureType - 500 < 10:
                    item.furnitureType += 10
            elif item.furnitureType - 500 > 10:
                item.furnitureType -= 10
            do = DistributedClosetAI(self.air, self, item)
        elif item.getFlags() & FLBank:
            do = DistributedBankAI(self.air, self, item)
        elif item.getFlags() & FLPhone:
            do = DistributedPhoneAI(self.air, self, item)
        else:
            do = DistributedFurnitureItemAI(self.air, self, item)

        do.generateWithRequired(self.zoneId)
        self.items.append(do)
        return ToontownGlobals.FM_MovedItem, do.doId

    def deleteItemFromAttic(self, blob, index):
        item = self.getAtticFurniture(self.atticItems, index)
        if item is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid item at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex
        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())
        return ToontownGlobals.FM_DeletedItem

    def deleteItemFromRoom(self, blob, doId):
        pass

    def moveWallpaperFromAttic(self, index, room):
        retcode = ToontownGlobals.FM_SwappedItem
        wallpaper = self.getAtticFurniture(self.atticWallpaper, index)
        if wallpaper is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Invalid wallpaper at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex
        if room > 1:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to apply a wallpaper in an invalid room %d!' % room)
            return ToontownGlobals.FM_InvalidItem
        interiorIndex = room * 4
        if isinstance(wallpaper, CatalogMouldingItem):
            interiorIndex += 1
        elif isinstance(wallpaper, CatalogFlooringItem):
            interiorIndex += 2
        elif isinstance(wallpaper, CatalogWainscotingItem):
            interiorIndex += 3
        atticIndex = self.atticWallpaper.index(wallpaper)
        self.atticWallpaper[atticIndex] = self.wallpaper[interiorIndex]
        self.d_setAtticWallpaper(self.getAtticWallpaper())
        self.wallpaper[interiorIndex] = wallpaper
        self.applyWallpaper()
        return retcode

    def deleteWallpaperFromAttic(self, blob, index):
        wallpaper = self.getAtticFurniture(self.atticWallpaper, index)
        self.atticWallpaper.remove(wallpaper)
        self.b_setAtticWallpaper(self.getAtticWallpaper())

    def moveWindowToAttic(self, slot):
        window = self.getWindow(slot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex
        self.windows.remove(window)
        self.applyWindows()
        self.atticWindows.append(window)
        self.d_setAtticWindows(self.getAtticWindows())
        return ToontownGlobals.FM_MovedItem

    def moveWindowFromAttic(self, index, slot):
        retcode = ToontownGlobals.FM_MovedItem
        window = self.getAtticFurniture(self.atticWindows, index)
        if slot > 5:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to move window to invalid slot %d!' % slot)
            return ToontownGlobals.FM_HouseFull
        if self.getWindow(slot):
            self.moveWindowToAttic(slot)
            retcode = ToontownGlobals.FM_SwappedItem
        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())
        window.placement = slot
        self.windows.append(window)
        self.applyWindows()
        return retcode

    def moveWindow(self, fromSlot, toSlot):
        retcode = ToontownGlobals.FM_MovedItem
        window = self.getWindow(fromSlot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex
        if toSlot > 5:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='DistributedfTried to move window to invalid slot %d!' % toSlot)
            return ToontownGlobals.FM_HouseFull
        if self.getWindow(toSlot):
            self.moveWindowToAttic(toSlot)
            retcode = ToontownGlobals.FM_SwappedItem
        window.placement = toSlot
        self.applyWindows()
        return retcode

    def deleteWindowFromAttic(self, blob, index):
        window = self.getAtticFurniture(self.atticWindows, index)
        if window is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid window at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex
        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())
        return ToontownGlobals.FM_DeletedItem

    def recoverDeletedItem(self, blob, index):
        pass

    def handleMessage(self, func, response, *args):
        context = args[-1]
        args = args[:-1]
        senderId = self.air.getAvatarIdFromSender()
        if not self.director or senderId != self.director.doId:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Sent furniture management request without being the director.')
            retval = ToontownGlobals.FM_NotDirector
        else:
            try:
                retval = func(*args) or 0
            except FurnitureError as e:
                retval = e.code

        if response == 'moveItemFromAtticResponse':
            if type(retval) == tuple:
                retval, doId = retval
            else:
                doId = 0
            taskMgr.doMethodLater(1, self.sendUpdateToAvatarId, self.uniqueName('send-attic-response'), extraArgs=[senderId, response, [retval, doId, context]])
        else:
            self.sendUpdateToAvatarId(senderId, response, [retval, context])

    def moveItemToAtticMessage(self, doId, context):
        self.handleMessage(self.moveItemToAttic, 'moveItemToAtticResponse', doId, context)

    def moveItemFromAtticMessage(self, index, x, y, z, h, p, r, context):
        self.handleMessage(self.moveItemFromAttic, 'moveItemFromAtticResponse', index, x, y, z, h, p, r, context)

    def deleteItemFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteItemFromAttic, 'deleteItemFromAtticResponse', blob, index, context)

    def deleteItemFromRoomMessage(self, blob, doId, context):
        self.handleMessage(self.deleteItemFromRoom, 'deleteItemFromRoomResponse', blob, doId, context)

    def moveWallpaperFromAtticMessage(self, index, room, context):
        self.handleMessage(self.moveWallpaperFromAttic, 'moveWallpaperFromAtticResponse', index, room, context)

    def deleteWallpaperFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWallpaperFromAttic, 'deleteWallpaperFromAtticResponse', blob, index, context)

    def moveWindowToAtticMessage(self, slot, context):
        self.handleMessage(self.moveWindowToAttic, 'moveWindowToAtticResponse', slot, context)

    def moveWindowFromAtticMessage(self, index, slot, context):
        self.handleMessage(self.moveWindowFromAttic, 'moveWindowFromAtticResponse', index, slot, context)

    def moveWindowMessage(self, fromSlot, toSlot, context):
        self.handleMessage(self.moveWindow, 'moveWindowResponse', fromSlot, toSlot, context)

    def deleteWindowFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWindowFromAttic, 'deleteWindowFromAtticResponse', blob, index, context)

    def recoverDeletedItemMessage(self, blob, index, context):
        self.handleMessage(self.recoverDeletedItem, 'recoverDeletedItemResponse', blob, index, context)

    def getItemObject(self, doId):
        item = self.air.doId2do.get(doId)
        if item is None:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)
        if item not in self.items:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)
        return item

    def getAtticFurniture(self, attic, index):
        if index >= len(attic):
            raise FurnitureError(ToontownGlobals.FM_InvalidIndex)
        return attic[index]

    def getWindow(self, slot):
        for window in self.windows:
            if window.placement == slot:
                return window
class DistributedFurnitureManagerAI(DistributedObjectAI):
    notify = DirectNotifyGlobal.directNotify.newCategory("DistributedFurnitureManagerAI")

    def __init__(self, air, house, interior):
        DistributedObjectAI.__init__(self, air)

        self.house = house
        self.interior = interior

        self.director = None

        self.ownerId = house.avatarId
        self.ownerName = house.name

        self.atticItems = None
        self.atticWallpaper = None
        self.wallpaper = None
        self.atticWindows = None
        self.windows = None
        self.deletedItems = None

        self.items = []

        # Initialize the above variables:
        self.loadFromHouse()

    def announceGenerate(self):
        DistributedObjectAI.announceGenerate(self)
        for item in self.items:
            item.generateWithRequired(self.zoneId)

    def delete(self):
        for item in self.items:
            item.destroy()
        DistributedObjectAI.delete(self)

    def loadFromHouse(self):
        self.b_setAtticItems(self.house.getAtticItems())
        self.b_setAtticWallpaper(self.house.getAtticWallpaper())
        self.b_setAtticWindows(self.house.getAtticWindows())
        self.b_setDeletedItems(self.house.getDeletedItems())

        self.wallpaper = CatalogItemList(self.house.getInteriorWallpaper(),
                                         store=CatalogItem.Customization)
        self.applyWallpaper()
        self.windows = CatalogItemList(self.house.getInteriorWindows(),
                                       store=CatalogItem.Customization |
                                             CatalogItem.WindowPlacement)
        self.applyWindows()

        self.setItems(self.house.getInteriorItems())

    def saveToHouse(self):
        self.house.b_setAtticItems(self.getAtticItems())
        self.house.b_setAtticWallpaper(self.getAtticWallpaper())
        self.house.b_setAtticWindows(self.getAtticWindows())
        self.house.b_setDeletedItems(self.getDeletedItems())

        self.house.b_setInteriorWallpaper(self.wallpaper.getBlob())
        self.house.b_setInteriorWindows(self.windows.getBlob())

        self.house.b_setInteriorItems(self.getItems())

    def applyWallpaper(self):
        self.interior.b_setWallpaper(self.wallpaper.getBlob())

    def applyWindows(self):
        self.interior.b_setWindows(self.windows.getBlob())

    def setItems(self, items):
        # Decode the blob:
        items = CatalogItemList(items, store=CatalogItem.Customization|CatalogItem.Location)

        # Throw out our old items:
        for item in self.items:
            item.destroy()
        self.items = []

        for item in items:
            if item.getFlags() & FLCloset:
                if self.house.gender is 0:
                    # If they have a male closet, we need to make it a female closet.
                    if item.furnitureType - 500 < 10:
                        item.furnitureType += 10
                elif item.furnitureType - 500 > 10:
                    # If they have a female closet, we need to make it a male closet.
                    item.furnitureType -= 10
                do = DistributedClosetAI(self.air, self, item)
            elif item.getFlags() & FLBank:
                do = DistributedBankAI(self.air, self, item)
            elif item.getFlags() & FLPhone:
                do = DistributedPhoneAI(self.air, self, item)
            elif item.getFlags() & FLTrunk:
                if self.house.gender is 0:
                    if item.furnitureType - 500 > 10:
                        item.furnitureType += 10
                elif item.furnitureType - 500 > 10:
                    item.furnitureType -=10
                do = DistributedTrunkAI(self.air, self, item)
            else:
                do = DistributedFurnitureItemAI(self.air, self, item)


            if self.isGenerated():
                do.generateWithRequired(self.zoneId)
            self.items.append(do)

    def getItems(self):
        items = CatalogItemList(store=CatalogItem.Customization|CatalogItem.Location)

        for item in self.items:
            items.append(item.catalogItem)

        return items.getBlob()

    def setOwnerId(self, ownerId):
        self.ownerId = ownerId

    def d_setOwnerId(self, ownerId):
        self.sendUpdate('setOwnerId', [ownerId])

    def b_setOwnerId(self, ownerId):
        self.setOwnerId(ownerId)
        self.d_setOwnerId(ownerId)

    def getOwnerId(self):
        return self.ownerId

    def setOwnerName(self, ownerName):
        self.ownerName = ownerName

    def d_setOwnerName(self, ownerName):
        self.sendUpdate('setOwnerName', [ownerName])

    def b_setOwnerName(self, ownerName):
        self.setOwnerName(ownerName)
        self.d_setOwnerName(ownerName)

    def getOwnerName(self):
        return self.ownerName

    def getInteriorId(self):
        return self.interior.doId

    def setAtticItems(self, items):
        self.atticItems = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticItems(self, items):
        self.sendUpdate('setAtticItems', [items])

    def b_setAtticItems(self, items):
        self.setAtticItems(items)
        if self.isGenerated():
            self.d_setAtticItems(items)

    def getAtticItems(self):
        return self.atticItems.getBlob()

    def setAtticWallpaper(self, items):
        self.atticWallpaper = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticWallpaper(self, items):
        self.sendUpdate('setAtticWallpaper', [items])

    def b_setAtticWallpaper(self, items):
        self.setAtticWallpaper(items)
        if self.isGenerated():
            self.d_setAtticWallpaper(items)

    def getAtticWallpaper(self):
        return self.atticWallpaper.getBlob()

    def setAtticWindows(self, items):
        self.atticWindows = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticWindows(self, items):
        self.sendUpdate('setAtticWindows', [items])

    def b_setAtticWindows(self, items):
        self.setAtticWindows(items)
        if self.isGenerated():
            self.d_setAtticWindows(items)

    def getAtticWindows(self):
        return self.atticWindows.getBlob()

    def setDeletedItems(self, items):
        self.deletedItems = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setDeletedItems(self, items):
        self.sendUpdate('setDeletedItems', [items])

    def b_setDeletedItems(self, items):
        self.setDeletedItems(items)
        if self.isGenerated():
            self.d_setDeletedItems(items)

    def getDeletedItems(self):
        return self.deletedItems.getBlob()

    def suggestDirector(self, directorId):
        senderId = self.air.getAvatarIdFromSender()

        if self.ownerId != senderId:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to move furniture, but not the house owner!')
            return

        if senderId != directorId and directorId != 0:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to make someone else (%d) move their furniture!' % directorId)
            return

        director = self.air.doId2do.get(directorId)
        if directorId and not director:
            self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to move furniture without being on the shard!')
            return

        if self.director:
            self.director.b_setGhostMode(0)
        if director:
            if director.zoneId != self.zoneId:
                self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to become director from another zone!')
                return
            director.b_setGhostMode(1)

        self.director = director
        self.sendUpdate('setDirector', [directorId])

        # Let's also save the furniture to the house (and thus to the DB) while
        # we're at it...
        self.saveToHouse()

    def avatarEnter(self):
        pass

    def avatarExit(self):
        pass


    # Furniture-manipulation:
    def moveItemToAttic(self, doId):
        item = self.getItemObject(doId)

        self.atticItems.append(item.catalogItem)
        self.d_setAtticItems(self.getAtticItems())

        item.destroy()
        self.items.remove(item)

        return ToontownGlobals.FM_MovedItem

    def moveItemFromAttic(self, index, x, y, z, h, p, r):
        item = self.getAtticFurniture(self.atticItems, index)

        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())

        item.posHpr = (x, y, z, h, p, r)

        # TODO: Add DistributedTrunkAI when accessories are enabled
        # TODO2: Is there any point in repeating this? Perhaps we should unify this (and the above)
        # into a single self.__getDOFromItem(item)?
        if item.getFlags() & FLCloset:
            if self.house.gender is 0:
                # If they have a male closet, we need to make it a female closet.
                if item.furnitureType - 500 < 10:
                    item.furnitureType += 10
            elif item.furnitureType - 500 > 10:
                # If they have a female closet, we need to make it a male closet.
                item.furnitureType -= 10
            do = DistributedClosetAI(self.air, self, item)
        elif item.getFlags() & FLBank:
            do = DistributedBankAI(self.air, self, item)
        elif item.getFlags() & FLPhone:
            do = DistributedPhoneAI(self.air, self, item)
        elif item.getFlags() & FLTrunk:
            if self.house.gender is 0:
                if item.furnitureType - 500 > 10:
                    item.furnitureType += 10
                elif item.furnitureType - 500 > 10:
                    item.furnitureType -=10
                do = DistributedTrunkAI(self.air, self, item)
        else:
            do = DistributedFurnitureItemAI(self.air, self, item)

        do.generateWithRequired(self.zoneId)
        self.items.append(do)

        return (ToontownGlobals.FM_MovedItem, do.doId)

    def deleteItemFromAttic(self, blob, index):
        item = self.getAtticFurniture(self.atticItems, index)
        if item is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid item at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex

        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())

        return ToontownGlobals.FM_DeletedItem

    def deleteItemFromRoom(self, blob, doId):
        pass

    def moveWallpaperFromAttic(self, index, room):
        # CatalogFlooringItem(1020, 0)
        # CatalogWainscotingItem(1000, 3)
        # CatalogFlooringItem(1020, 0)
        retcode = ToontownGlobals.FM_SwappedItem
        wallpaper = self.getAtticFurniture(self.atticWallpaper, index)
        if wallpaper is None:
            # rip
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Invalid wallpaper at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex

        if room > 1:
            # This is not a valid room!
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to apply a wallpaper in an invalid room %d!' % room)
            return ToontownGlobals.FM_InvalidItem
        interiorIndex = room*4
        if isinstance(wallpaper, CatalogMouldingItem):
            interiorIndex += 1
        elif isinstance(wallpaper, CatalogFlooringItem):
            interiorIndex += 2
        elif isinstance(wallpaper, CatalogWainscotingItem):
            interiorIndex += 3
        atticIndex = self.atticWallpaper.index(wallpaper)
        self.atticWallpaper[atticIndex] = self.wallpaper[interiorIndex]
        self.d_setAtticWallpaper(self.getAtticWallpaper())
        self.wallpaper[interiorIndex] = wallpaper
        self.applyWallpaper()

        return retcode

    def deleteWallpaperFromAttic(self, blob, index):
        wallpaper = self.getAtticFurniture(blob, index)
        self.atticWallpaper.remove(wallpaper)
        self.b_setAtticWallpaper(self.getAtticWallpaper())

    def moveWindowToAttic(self, slot):
        window = self.getWindow(slot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex

        self.windows.remove(window)
        self.applyWindows()
        self.atticWindows.append(window)
        self.d_setAtticWindows(self.getAtticWindows())

        return ToontownGlobals.FM_MovedItem

    def moveWindowFromAttic(self, index, slot):
        retcode = ToontownGlobals.FM_MovedItem

        window = self.getAtticFurniture(self.atticWindows, index)

        if slot > 5:
            # This is not a valid slot! HACKER!!!
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(),
                                      issue='Tried to move window to invalid slot %d!' % slot)
            return ToontownGlobals.FM_HouseFull

        if self.getWindow(slot):
            # Already a window there, swap 'er out.
            self.moveWindowToAttic(slot)
            retcode = ToontownGlobals.FM_SwappedItem

        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())
        window.placement = slot
        self.windows.append(window)
        self.applyWindows()

        return retcode

    def moveWindow(self, fromSlot, toSlot):
        retcode = ToontownGlobals.FM_MovedItem

        window = self.getWindow(fromSlot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex

        if toSlot > 5:
            # This is not a valid slot! HACKER!!!
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(),
                                      issue='DistributedfTried to move window to invalid slot %d!' % toSlot)
            return ToontownGlobals.FM_HouseFull

        if self.getWindow(toSlot):
            # Already a window there, swap 'er out.
            self.moveWindowToAttic(toSlot)
            retcode = ToontownGlobals.FM_SwappedItem

        window.placement = toSlot
        self.applyWindows()

        return retcode

    def deleteWindowFromAttic(self, blob, index):
        window = self.getAtticFurniture(self.atticWindows, index)
        if window is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid window at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex

        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())

        return ToontownGlobals.FM_DeletedItem

    def recoverDeletedItem(self, blob, index):
        pass

    # Network handlers for the above:
    def handleMessage(self, func, response, *args):
        context = args[-1]
        args = args[:-1]

        senderId = self.air.getAvatarIdFromSender()
        if not self.director or senderId != self.director.doId:
            self.air.writeServerEvent('suspicious', avId=senderId,
                                      issue='Sent furniture management request without'
                                      ' being the director.')
            retval = ToontownGlobals.FM_NotDirector
        else:
            try:
                retval = func(*args) or 0
            except FurnitureError as e:
                retval = e.code

        if response == 'moveItemFromAtticResponse':
            # This message actually includes a doId; we split the retval apart
            # if it's a tuple, otherwise it falls back to 0.
            if type(retval) == tuple:
                retval, doId = retval
            else:
                doId = 0

            # Brief delay; this is to give the State Server time to finish
            # processing the new furniture item appearing before we hit the
            # client with the doId:
            taskMgr.doMethodLater(1, self.sendUpdateToAvatarId,
                                  self.uniqueName('send-attic-response'),
                                  extraArgs=[senderId, response, [retval, doId, context]])
        else:
            self.sendUpdateToAvatarId(senderId, response, [retval, context])

    def moveItemToAtticMessage(self, doId, context):
        self.handleMessage(self.moveItemToAttic, 'moveItemToAtticResponse', doId, context)

    def moveItemFromAtticMessage(self, index, x, y, z, h, p, r, context):
        self.handleMessage(self.moveItemFromAttic, 'moveItemFromAtticResponse', index, x, y, z, h, p, r, context)

    def deleteItemFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteItemFromAttic, 'deleteItemFromAtticResponse', blob, index, context)

    def deleteItemFromRoomMessage(self, blob, doId, context):
        self.handleMessage(self.deleteItemFromRoom, 'deleteItemFromRoomResponse', blob, doId, context)

    def moveWallpaperFromAtticMessage(self, index, room, context):
        self.handleMessage(self.moveWallpaperFromAttic, 'moveWallpaperFromAtticResponse', index, room, context)

    def deleteWallpaperFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWallpaperFromAttic, 'deleteWallpaperFromAtticResponse', blob, index, context)

    def moveWindowToAtticMessage(self, slot, context):
        self.handleMessage(self.moveWindowToAttic, 'moveWindowToAtticResponse', slot, context)

    def moveWindowFromAtticMessage(self, index, slot, context):
        self.handleMessage(self.moveWindowFromAttic, 'moveWindowFromAtticResponse', index, slot, context)

    def moveWindowMessage(self, fromSlot, toSlot, context):
        self.handleMessage(self.moveWindow, 'moveWindowResponse', fromSlot, toSlot, context)

    def deleteWindowFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWindowFromAttic, 'deleteWindowFromAtticResponse', blob, index, context)

    def recoverDeletedItemMessage(self, blob, index, context):
        self.handleMessage(self.recoverDeletedItem, 'recoverDeletedItemResponse', blob, index, context)

    # Functions to safely process data off the wire:
    def getItemObject(self, doId):
        item = self.air.doId2do.get(doId)

        if item is None:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)

        if item not in self.items:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)

        return item

    def getAtticFurniture(self, attic, index):
        if index >= len(attic):
            raise FurnitureError(ToontownGlobals.FM_InvalidIndex)

        return attic[index]

    def getWindow(self, slot):
        for window in self.windows:
            if window.placement == slot:
                return window

        return None
Example #3
0
class DistributedFurnitureManagerAI(DistributedObjectAI):
    notify = directNotify.newCategory("DistributedFurnitureManagerAI")

    def __init__(self, air, house, interior):
        DistributedObjectAI.__init__(self, air)

        self.house = house
        self.interior = interior

        self.director = None

        self.ownerId = house.avatarId
        self.ownerName = house.name

        self.atticItems = None
        self.atticWallpaper = None
        self.wallpaper = None
        self.atticWindows = None
        self.windows = None
        self.deletedItems = None

        self.items = []

        # Initialize the above variables:
        self.loadFromHouse()

    def announceGenerate(self):
        DistributedObjectAI.announceGenerate(self)

        for item in self.items:
            item.generateWithRequired(self.zoneId)

    def delete(self):
        for item in self.items:
            item.destroy()

        DistributedObjectAI.delete(self)

    def loadFromHouse(self):
        self.b_setAtticItems(self.house.getAtticItems())
        self.b_setAtticWallpaper(self.house.getAtticWallpaper())
        self.b_setAtticWindows(self.house.getAtticWindows())
        self.b_setDeletedItems(self.house.getDeletedItems())

        self.wallpaper = CatalogItemList(self.house.getInteriorWallpaper(),
                                         store=CatalogItem.Customization)
        self.applyWallpaper()
        self.windows = CatalogItemList(self.house.getInteriorWindows(),
                                       store=CatalogItem.Customization |
                                             CatalogItem.WindowPlacement)
        self.applyWindows()

        self.setItems(self.house.getInteriorItems())

    def saveToHouse(self):
        self.house.b_setAtticItems(self.getAtticItems())
        self.house.b_setAtticWallpaper(self.getAtticWallpaper())
        self.house.b_setAtticWindows(self.getAtticWindows())
        self.house.b_setDeletedItems(self.getDeletedItems())

        self.house.b_setInteriorWallpaper(self.wallpaper.getBlob())
        self.house.b_setInteriorWindows(self.windows.getBlob())

        self.house.b_setInteriorItems(self.getItems())

    def applyWallpaper(self):
        self.interior.b_setWallpaper(self.wallpaper.getBlob())

    def applyWindows(self):
        self.interior.b_setWindows(self.windows.getBlob())

    def setItems(self, items):
        # Decode the blob:
        items = CatalogItemList(items, store=CatalogItem.Customization|CatalogItem.Location)

        # Throw out our old items:
        for item in self.items:
            item.destroy()
        self.items = []

        items.removeDuplicates(FLCloset)

        # Due to a bug, some people are missing their closets...
        hasCloset = False
        for item in items:
            if item.getFlags() & FLCloset:
                hasCloset = True
                break

        if not hasCloset and self.ownerId != 0:
            item = CatalogFurnitureItem(500)  # the basic closet...
            item.posHpr = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
            items.append(item)
            # Since we have modified the items list, should we save it back to the house?

        for item in items:
            if item.getFlags() & FLTrunk:
                if self.house.gender is 0:
                    if item.furnitureType - 4000 < 10:
                        item.furnitureType += 10
                elif item.furnitureType - 4000 > 10:
                    item.furnitureType -= 10
                do = DistributedTrunkAI(self.air, self, item)
            elif item.getFlags() & FLCloset:
                if self.house.gender is 0:
                    if item.furnitureType - 500 < 10:
                        item.furnitureType += 10
                elif item.furnitureType - 500 > 10:
                    item.furnitureType -= 10
                do = DistributedClosetAI(self.air, self, item)
            elif item.getFlags() & FLBank:
                continue # We dont want banks in the estates.
            elif item.getFlags() & FLPhone:
                do = DistributedPhoneAI(self.air, self, item)
            else:
                do = DistributedFurnitureItemAI(self.air, self, item)
            if self.isGenerated():
                do.generateWithRequired(self.zoneId)
            self.items.append(do)

    def getItems(self):
        items = CatalogItemList(store=CatalogItem.Customization|CatalogItem.Location)

        for item in self.items:
            items.append(item.catalogItem)

        return items.getBlob()

    def setOwnerId(self, ownerId):
        self.ownerId = ownerId

    def d_setOwnerId(self, ownerId):
        self.sendUpdate('setOwnerId', [ownerId])

    def b_setOwnerId(self, ownerId):
        self.setOwnerId(ownerId)
        self.d_setOwnerId(ownerId)

    def getOwnerId(self):
        return self.ownerId

    def setOwnerName(self, ownerName):
        self.ownerName = ownerName

    def d_setOwnerName(self, ownerName):
        self.sendUpdate('setOwnerName', [ownerName])

    def b_setOwnerName(self, ownerName):
        self.setOwnerName(ownerName)
        self.d_setOwnerName(ownerName)

    def getOwnerName(self):
        return self.ownerName

    def getInteriorId(self):
        return self.interior.doId

    def setAtticItems(self, items):
        self.atticItems = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticItems(self, items):
        self.sendUpdate('setAtticItems', [items])

    def b_setAtticItems(self, items):
        self.setAtticItems(items)
        if self.isGenerated():
            self.d_setAtticItems(items)

    def getAtticItems(self):
        return self.atticItems.getBlob()

    def setAtticWallpaper(self, items):
        self.atticWallpaper = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticWallpaper(self, items):
        self.sendUpdate('setAtticWallpaper', [items])

    def b_setAtticWallpaper(self, items):
        self.setAtticWallpaper(items)
        if self.isGenerated():
            self.d_setAtticWallpaper(items)

    def getAtticWallpaper(self):
        return self.atticWallpaper.getBlob()

    def setAtticWindows(self, items):
        self.atticWindows = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticWindows(self, items):
        self.sendUpdate('setAtticWindows', [items])

    def b_setAtticWindows(self, items):
        self.setAtticWindows(items)
        if self.isGenerated():
            self.d_setAtticWindows(items)

    def getAtticWindows(self):
        return self.atticWindows.getBlob()

    def setDeletedItems(self, items):
        self.deletedItems = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setDeletedItems(self, items):
        self.sendUpdate('setDeletedItems', [items])

    def b_setDeletedItems(self, items):
        self.setDeletedItems(items)
        if self.isGenerated():
            self.d_setDeletedItems(items)

    def getDeletedItems(self):
        return self.deletedItems.getBlob()

    def suggestDirector(self, directorId):
        senderId = self.air.getAvatarIdFromSender()

        if self.ownerId != senderId:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to move furniture, but not the house owner!')
            return

        if senderId != directorId and directorId != 0:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to make someone else (%d) move their furniture!' % directorId)
            return

        director = self.air.doId2do.get(directorId)
        if directorId and not director:
            self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to move furniture without being on the shard!')
            return

        if self.director:
            self.director.b_setGhostMode(0)
        if director:
            if director.zoneId != self.zoneId:
                self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to become director from another zone!')
                return
            director.b_setGhostMode(1)

        self.director = director
        self.sendUpdate('setDirector', [directorId])

        # Let's also save the furniture to the house (and thus to the DB) while
        # we're at it...
        self.saveToHouse()

    def avatarEnter(self):
        pass

    def avatarExit(self):
        pass

    # Furniture-manipulation:
    def moveItemToAttic(self, doId):
        item = self.getItemObject(doId)

        self.atticItems.append(item.catalogItem)
        self.d_setAtticItems(self.getAtticItems())

        item.destroy()
        self.items.remove(item)

        return ToontownGlobals.FM_MovedItem

    def moveItemFromAttic(self, index, x, y, z, h, p, r):
        item = self.getAtticFurniture(self.atticItems, index)

        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())

        item.posHpr = (x, y, z, h, p, r)

        if item.getFlags() & FLTrunk:
            if self.house.gender is 0:
                if item.furnitureType - 4000 < 10:
                    item.furnitureType += 10
            elif item.furnitureType - 4000 > 10:
                item.furnitureType -= 10
            do = DistributedTrunkAI(self.air, self, item)
        elif item.getFlags() & FLCloset:
            if self.house.gender is 0:
                if item.furnitureType - 500 < 10:
                    item.furnitureType += 10
            elif item.furnitureType - 500 > 10:
                item.furnitureType -= 10
            do = DistributedClosetAI(self.air, self, item)
        elif item.getFlags() & FLBank:
            pass # We don't want banks in the estates
        elif item.getFlags() & FLPhone:
            do = DistributedPhoneAI(self.air, self, item)
        else:
            do = DistributedFurnitureItemAI(self.air, self, item)

        do.generateWithRequired(self.zoneId)
        self.items.append(do)

        return (ToontownGlobals.FM_MovedItem, do.doId)

    def deleteItemFromAttic(self, blob, index):
        item = self.getAtticFurniture(self.atticItems, index)
        if item is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid item at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex

        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())

        return ToontownGlobals.FM_DeletedItem

    def deleteItemFromRoom(self, blob, doId):
        pass

    def moveWallpaperFromAttic(self, index, room):
        retcode = ToontownGlobals.FM_SwappedItem
        wallpaper = self.getAtticFurniture(self.atticWallpaper, index)
        if wallpaper is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Invalid wallpaper at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex

        if room > 1:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to apply a wallpaper in an invalid room %d!' % room)
            return ToontownGlobals.FM_InvalidItem
        interiorIndex = room*4
        if isinstance(wallpaper, CatalogMouldingItem):
            interiorIndex += 1
        elif isinstance(wallpaper, CatalogFlooringItem):
            interiorIndex += 2
        elif isinstance(wallpaper, CatalogWainscotingItem):
            interiorIndex += 3
        atticIndex = self.atticWallpaper.index(wallpaper)
        self.atticWallpaper[atticIndex] = self.wallpaper[interiorIndex]
        self.d_setAtticWallpaper(self.getAtticWallpaper())
        self.wallpaper[interiorIndex] = wallpaper
        self.applyWallpaper()

        return retcode

    def deleteWallpaperFromAttic(self, blob, index):
        wallpaper = self.getAtticFurniture(self.atticWallpaper, index)
        if wallpaper in self.atticWallpaper:
            self.atticWallpaper.remove(wallpaper)
        self.b_setAtticWallpaper(self.getAtticWallpaper())

    def moveWindowToAttic(self, slot):
        window = self.getWindow(slot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex
        self.windows.remove(window)
        self.applyWindows()
        self.atticWindows.append(window)
        self.d_setAtticWindows(self.getAtticWindows())
        return ToontownGlobals.FM_MovedItem

    def moveWindowFromAttic(self, index, slot):
        retcode = ToontownGlobals.FM_MovedItem
        window = self.getAtticFurniture(self.atticWindows, index)
        if slot > 5:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(),
                                      issue='Tried to move window to invalid slot %d!' % slot)
            return ToontownGlobals.FM_HouseFull
        if self.getWindow(slot):
            self.moveWindowToAttic(slot)
            retcode = ToontownGlobals.FM_SwappedItem
        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())
        window.placement = slot
        self.windows.append(window)
        self.applyWindows()
        return retcode

    def moveWindow(self, fromSlot, toSlot):
        retcode = ToontownGlobals.FM_MovedItem
        window = self.getWindow(fromSlot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex
        if toSlot > 5:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(),
                                      issue='DistributedfTried to move window to invalid slot %d!' % toSlot)
            return ToontownGlobals.FM_HouseFull
        if self.getWindow(toSlot):
            self.moveWindowToAttic(toSlot)
            retcode = ToontownGlobals.FM_SwappedItem
        window.placement = toSlot
        self.applyWindows()
        return retcode

    def deleteWindowFromAttic(self, blob, index):
        window = self.getAtticFurniture(self.atticWindows, index)
        if window is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid window at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex
        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())
        return ToontownGlobals.FM_DeletedItem

    def recoverDeletedItem(self, blob, index):
        pass


    def handleMessage(self, func, response, *args):
        context = args[-1]
        args = args[:-1]
        senderId = self.air.getAvatarIdFromSender()
        if not self.director or senderId != self.director.doId:
            self.air.writeServerEvent('suspicious', avId=senderId,
                                      issue='Sent furniture management request without'
                                      ' being the director.')
            retval = ToontownGlobals.FM_NotDirector
        else:
            try:
                retval = func(*args) or 0
            except FurnitureError as e:
                retval = e.code
        if response == 'moveItemFromAtticResponse':
            if type(retval) == tuple:
                retval, doId = retval
            else:
                doId = 0
            taskMgr.doMethodLater(1, self.sendUpdateToAvatarId,
                                  self.uniqueName('send-attic-response'),
                                  extraArgs=[senderId, response, [retval, doId, context]])
        else:
            self.sendUpdateToAvatarId(senderId, response, [retval, context])

    def moveItemToAtticMessage(self, doId, context):
        self.handleMessage(self.moveItemToAttic, 'moveItemToAtticResponse', doId, context)

    def moveItemFromAtticMessage(self, index, x, y, z, h, p, r, context):
        self.handleMessage(self.moveItemFromAttic, 'moveItemFromAtticResponse', index, x, y, z, h, p, r, context)

    def deleteItemFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteItemFromAttic, 'deleteItemFromAtticResponse', blob, index, context)

    def deleteItemFromRoomMessage(self, blob, doId, context):
        self.handleMessage(self.deleteItemFromRoom, 'deleteItemFromRoomResponse', blob, doId, context)

    def moveWallpaperFromAtticMessage(self, index, room, context):
        self.handleMessage(self.moveWallpaperFromAttic, 'moveWallpaperFromAtticResponse', index, room, context)

    def deleteWallpaperFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWallpaperFromAttic, 'deleteWallpaperFromAtticResponse', blob, index, context)

    def moveWindowToAtticMessage(self, slot, context):
        self.handleMessage(self.moveWindowToAttic, 'moveWindowToAtticResponse', slot, context)

    def moveWindowFromAtticMessage(self, index, slot, context):
        self.handleMessage(self.moveWindowFromAttic, 'moveWindowFromAtticResponse', index, slot, context)

    def moveWindowMessage(self, fromSlot, toSlot, context):
        self.handleMessage(self.moveWindow, 'moveWindowResponse', fromSlot, toSlot, context)

    def deleteWindowFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWindowFromAttic, 'deleteWindowFromAtticResponse', blob, index, context)

    def recoverDeletedItemMessage(self, blob, index, context):
        self.handleMessage(self.recoverDeletedItem, 'recoverDeletedItemResponse', blob, index, context)

    def getItemObject(self, doId):
        item = self.air.doId2do.get(doId)
        if item is None:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)
        if item not in self.items:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)
        return item

    def getAtticFurniture(self, attic, index):
        if index >= len(attic):
            raise FurnitureError(ToontownGlobals.FM_InvalidIndex)
        return attic[index]

    def getWindow(self, slot):
        for window in self.windows:
            if window.placement == slot:
                return window
        return None
class DistributedFurnitureManagerAI(DistributedObjectAI):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        "DistributedFurnitureManagerAI")

    def __init__(self, air, house, interior):
        DistributedObjectAI.__init__(self, air)

        self.house = house
        self.interior = interior

        self.director = None

        self.ownerId = house.avatarId
        self.ownerName = house.name

        self.atticItems = None
        self.atticWallpaper = None
        self.wallpaper = None
        self.atticWindows = None
        self.windows = None
        self.deletedItems = None

        self.items = []

        # Initialize the above variables:
        self.loadFromHouse()

    def announceGenerate(self):
        DistributedObjectAI.announceGenerate(self)
        for item in self.items:
            item.generateWithRequired(self.zoneId)

    def delete(self):
        for item in self.items:
            item.destroy()
        DistributedObjectAI.delete(self)

    def loadFromHouse(self):
        self.b_setAtticItems(self.house.getAtticItems())
        self.b_setAtticWallpaper(self.house.getAtticWallpaper())
        self.b_setAtticWindows(self.house.getAtticWindows())
        self.b_setDeletedItems(self.house.getDeletedItems())

        self.wallpaper = CatalogItemList(self.house.getInteriorWallpaper(),
                                         store=CatalogItem.Customization)
        self.applyWallpaper()
        self.windows = CatalogItemList(self.house.getInteriorWindows(),
                                       store=CatalogItem.Customization
                                       | CatalogItem.WindowPlacement)
        self.applyWindows()

        self.setItems(self.house.getInteriorItems())

    def saveToHouse(self):
        self.house.b_setAtticItems(self.getAtticItems())
        self.house.b_setAtticWallpaper(self.getAtticWallpaper())
        self.house.b_setAtticWindows(self.getAtticWindows())
        self.house.b_setDeletedItems(self.getDeletedItems())

        self.house.b_setInteriorWallpaper(self.wallpaper.getBlob())
        self.house.b_setInteriorWindows(self.windows.getBlob())

        self.house.b_setInteriorItems(self.getItems())

    def applyWallpaper(self):
        self.interior.b_setWallpaper(self.wallpaper.getBlob())

    def applyWindows(self):
        self.interior.b_setWindows(self.windows.getBlob())

    def setItems(self, items):
        # Decode the blob:
        items = CatalogItemList(items,
                                store=CatalogItem.Customization
                                | CatalogItem.Location)

        # Throw out our old items:
        for item in self.items:
            item.destroy()
        self.items = []

        for item in items:
            if item.getFlags() & FLCloset:
                if self.house.gender is 0:
                    # If they have a male closet, we need to make it a female closet.
                    if item.furnitureType - 500 < 10:
                        item.furnitureType += 10
                elif item.furnitureType - 500 > 10:
                    # If they have a female closet, we need to make it a male closet.
                    item.furnitureType -= 10
                do = DistributedClosetAI(self.air, self, item)
            elif item.getFlags() & FLBank:
                do = DistributedBankAI(self.air, self, item)
            elif item.getFlags() & FLPhone:
                do = DistributedPhoneAI(self.air, self, item)
            else:
                do = DistributedFurnitureItemAI(self.air, self, item)

            if self.isGenerated():
                do.generateWithRequired(self.zoneId)
            self.items.append(do)

    def getItems(self):
        items = CatalogItemList(store=CatalogItem.Customization
                                | CatalogItem.Location)

        for item in self.items:
            items.append(item.catalogItem)

        return items.getBlob()

    def setOwnerId(self, ownerId):
        self.ownerId = ownerId

    def d_setOwnerId(self, ownerId):
        self.sendUpdate('setOwnerId', [ownerId])

    def b_setOwnerId(self, ownerId):
        self.setOwnerId(ownerId)
        self.d_setOwnerId(ownerId)

    def getOwnerId(self):
        return self.ownerId

    def setOwnerName(self, ownerName):
        self.ownerName = ownerName

    def d_setOwnerName(self, ownerName):
        self.sendUpdate('setOwnerName', [ownerName])

    def b_setOwnerName(self, ownerName):
        self.setOwnerName(ownerName)
        self.d_setOwnerName(ownerName)

    def getOwnerName(self):
        return self.ownerName

    def getInteriorId(self):
        return self.interior.doId

    def setAtticItems(self, items):
        self.atticItems = CatalogItemList(items,
                                          store=CatalogItem.Customization)

    def d_setAtticItems(self, items):
        self.sendUpdate('setAtticItems', [items])

    def b_setAtticItems(self, items):
        self.setAtticItems(items)
        if self.isGenerated():
            self.d_setAtticItems(items)

    def getAtticItems(self):
        return self.atticItems.getBlob()

    def setAtticWallpaper(self, items):
        self.atticWallpaper = CatalogItemList(items,
                                              store=CatalogItem.Customization)

    def d_setAtticWallpaper(self, items):
        self.sendUpdate('setAtticWallpaper', [items])

    def b_setAtticWallpaper(self, items):
        self.setAtticWallpaper(items)
        if self.isGenerated():
            self.d_setAtticWallpaper(items)

    def getAtticWallpaper(self):
        return self.atticWallpaper.getBlob()

    def setAtticWindows(self, items):
        self.atticWindows = CatalogItemList(items,
                                            store=CatalogItem.Customization)

    def d_setAtticWindows(self, items):
        self.sendUpdate('setAtticWindows', [items])

    def b_setAtticWindows(self, items):
        self.setAtticWindows(items)
        if self.isGenerated():
            self.d_setAtticWindows(items)

    def getAtticWindows(self):
        return self.atticWindows.getBlob()

    def setDeletedItems(self, items):
        self.deletedItems = CatalogItemList(items,
                                            store=CatalogItem.Customization)

    def d_setDeletedItems(self, items):
        self.sendUpdate('setDeletedItems', [items])

    def b_setDeletedItems(self, items):
        self.setDeletedItems(items)
        if self.isGenerated():
            self.d_setDeletedItems(items)

    def getDeletedItems(self):
        return self.deletedItems.getBlob()

    def suggestDirector(self, directorId):
        senderId = self.air.getAvatarIdFromSender()

        if self.ownerId != senderId:
            self.air.writeServerEvent(
                'suspicious',
                avId=senderId,
                issue='Tried to move furniture, but not the house owner!')
            return

        if senderId != directorId and directorId != 0:
            self.air.writeServerEvent(
                'suspicious',
                avId=senderId,
                issue='Tried to make someone else (%d) move their furniture!' %
                directorId)
            return

        director = self.air.doId2do.get(directorId)
        if directorId and not director:
            self.air.writeServerEvent(
                'suspicious',
                avId=directorId,
                issue='Tried to move furniture without being on the shard!')
            return

        if self.director:
            self.director.b_setGhostMode(0)
        if director:
            if director.zoneId != self.zoneId:
                self.air.writeServerEvent(
                    'suspicious',
                    avId=directorId,
                    issue='Tried to become director from another zone!')
                return
            director.b_setGhostMode(1)

        self.director = director
        self.sendUpdate('setDirector', [directorId])

        # Let's also save the furniture to the house (and thus to the DB) while
        # we're at it...
        self.saveToHouse()

    def avatarEnter(self):
        pass

    def avatarExit(self):
        pass

    # Furniture-manipulation:
    def moveItemToAttic(self, doId):
        item = self.getItemObject(doId)

        self.atticItems.append(item.catalogItem)
        self.d_setAtticItems(self.getAtticItems())

        item.destroy()
        self.items.remove(item)

        return ToontownGlobals.FM_MovedItem

    def moveItemFromAttic(self, index, x, y, z, h, p, r):
        item = self.getAtticFurniture(self.atticItems, index)

        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())

        item.posHpr = (x, y, z, h, p, r)

        # TODO: Add DistributedTrunkAI when accessories are enabled
        # TODO2: Is there any point in repeating this? Perhaps we should unify this (and the above)
        # into a single self.__getDOFromItem(item)?
        if item.getFlags() & FLCloset:
            if self.house.gender is 0:
                # If they have a male closet, we need to make it a female closet.
                if item.furnitureType - 500 < 10:
                    item.furnitureType += 10
            elif item.furnitureType - 500 > 10:
                # If they have a female closet, we need to make it a male closet.
                item.furnitureType -= 10
            do = DistributedClosetAI(self.air, self, item)
        elif item.getFlags() & FLBank:
            do = DistributedBankAI(self.air, self, item)
        elif item.getFlags() & FLPhone:
            do = DistributedPhoneAI(self.air, self, item)
        else:
            do = DistributedFurnitureItemAI(self.air, self, item)

        do.generateWithRequired(self.zoneId)
        self.items.append(do)

        return (ToontownGlobals.FM_MovedItem, do.doId)

    def deleteItemFromAttic(self, blob, index):
        item = self.getAtticFurniture(self.atticItems, index)
        if item is None:
            self.air.writeServerEvent(
                'suspicious',
                avId=self.air.getAvatarIdFromSender(),
                issue='Tried to delete an invalid item at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex

        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())

        return ToontownGlobals.FM_DeletedItem

    def deleteItemFromRoom(self, blob, doId):
        pass

    def moveWallpaperFromAttic(self, index, room):
        # CatalogFlooringItem(1020, 0)
        # CatalogWainscotingItem(1000, 3)
        # CatalogFlooringItem(1020, 0)
        retcode = ToontownGlobals.FM_SwappedItem
        wallpaper = self.getAtticFurniture(self.atticWallpaper, index)
        if wallpaper is None:
            # rip
            self.air.writeServerEvent('suspicious',
                                      avId=self.air.getAvatarIdFromSender(),
                                      issue='Invalid wallpaper at index %s' %
                                      index)
            return ToontownGlobals.FM_InvalidIndex

        if room > 1:
            # This is not a valid room!
            self.air.writeServerEvent(
                'suspicious',
                avId=self.air.getAvatarIdFromSender(),
                issue='Tried to apply a wallpaper in an invalid room %d!' %
                room)
            return ToontownGlobals.FM_InvalidItem
        interiorIndex = room * 4
        if isinstance(wallpaper, CatalogMouldingItem):
            interiorIndex += 1
        elif isinstance(wallpaper, CatalogFlooringItem):
            interiorIndex += 2
        elif isinstance(wallpaper, CatalogWainscotingItem):
            interiorIndex += 3
        atticIndex = self.atticWallpaper.index(wallpaper)
        self.atticWallpaper[atticIndex] = self.wallpaper[interiorIndex]
        self.d_setAtticWallpaper(self.getAtticWallpaper())
        self.wallpaper[interiorIndex] = wallpaper
        self.applyWallpaper()

        return retcode

    def deleteWallpaperFromAttic(self, blob, index):
        wallpaper = self.getAtticFurniture(blob, index)
        if wallpaper in self.atticWallpaper:
            self.atticWallpaper.remove(wallpaper)
        self.b_setAtticWallpaper(self.getAtticWallpaper())

    def moveWindowToAttic(self, slot):
        window = self.getWindow(slot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex

        self.windows.remove(window)
        self.applyWindows()
        self.atticWindows.append(window)
        self.d_setAtticWindows(self.getAtticWindows())

        return ToontownGlobals.FM_MovedItem

    def moveWindowFromAttic(self, index, slot):
        retcode = ToontownGlobals.FM_MovedItem

        window = self.getAtticFurniture(self.atticWindows, index)

        if slot > 5:
            # This is not a valid slot! HACKER!!!
            self.air.writeServerEvent(
                'suspicious',
                avId=self.air.getAvatarIdFromSender(),
                issue='Tried to move window to invalid slot %d!' % slot)
            return ToontownGlobals.FM_HouseFull

        if self.getWindow(slot):
            # Already a window there, swap 'er out.
            self.moveWindowToAttic(slot)
            retcode = ToontownGlobals.FM_SwappedItem

        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())
        window.placement = slot
        self.windows.append(window)
        self.applyWindows()

        return retcode

    def moveWindow(self, fromSlot, toSlot):
        retcode = ToontownGlobals.FM_MovedItem

        window = self.getWindow(fromSlot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex

        if toSlot > 5:
            # This is not a valid slot! HACKER!!!
            self.air.writeServerEvent(
                'suspicious',
                avId=self.air.getAvatarIdFromSender(),
                issue='DistributedfTried to move window to invalid slot %d!' %
                toSlot)
            return ToontownGlobals.FM_HouseFull

        if self.getWindow(toSlot):
            # Already a window there, swap 'er out.
            self.moveWindowToAttic(toSlot)
            retcode = ToontownGlobals.FM_SwappedItem

        window.placement = toSlot
        self.applyWindows()

        return retcode

    def deleteWindowFromAttic(self, blob, index):
        window = self.getAtticFurniture(self.atticWindows, index)
        if window is None:
            self.air.writeServerEvent(
                'suspicious',
                avId=self.air.getAvatarIdFromSender(),
                issue='Tried to delete an invalid window at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex

        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())

        return ToontownGlobals.FM_DeletedItem

    def recoverDeletedItem(self, blob, index):
        pass

    # Network handlers for the above:
    def handleMessage(self, func, response, *args):
        context = args[-1]
        args = args[:-1]

        senderId = self.air.getAvatarIdFromSender()
        if not self.director or senderId != self.director.doId:
            self.air.writeServerEvent(
                'suspicious',
                avId=senderId,
                issue='Sent furniture management request without'
                ' being the director.')
            retval = ToontownGlobals.FM_NotDirector
        else:
            try:
                retval = func(*args) or 0
            except FurnitureError as e:
                retval = e.code

        if response == 'moveItemFromAtticResponse':
            # This message actually includes a doId; we split the retval apart
            # if it's a tuple, otherwise it falls back to 0.
            if type(retval) == tuple:
                retval, doId = retval
            else:
                doId = 0

            # Brief delay; this is to give the State Server time to finish
            # processing the new furniture item appearing before we hit the
            # client with the doId:
            taskMgr.doMethodLater(
                1,
                self.sendUpdateToAvatarId,
                self.uniqueName('send-attic-response'),
                extraArgs=[senderId, response, [retval, doId, context]])
        else:
            self.sendUpdateToAvatarId(senderId, response, [retval, context])

    def moveItemToAtticMessage(self, doId, context):
        self.handleMessage(self.moveItemToAttic, 'moveItemToAtticResponse',
                           doId, context)

    def moveItemFromAtticMessage(self, index, x, y, z, h, p, r, context):
        self.handleMessage(self.moveItemFromAttic, 'moveItemFromAtticResponse',
                           index, x, y, z, h, p, r, context)

    def deleteItemFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteItemFromAttic,
                           'deleteItemFromAtticResponse', blob, index, context)

    def deleteItemFromRoomMessage(self, blob, doId, context):
        self.handleMessage(self.deleteItemFromRoom,
                           'deleteItemFromRoomResponse', blob, doId, context)

    def moveWallpaperFromAtticMessage(self, index, room, context):
        self.handleMessage(self.moveWallpaperFromAttic,
                           'moveWallpaperFromAtticResponse', index, room,
                           context)

    def deleteWallpaperFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWallpaperFromAttic,
                           'deleteWallpaperFromAtticResponse', blob, index,
                           context)

    def moveWindowToAtticMessage(self, slot, context):
        self.handleMessage(self.moveWindowToAttic, 'moveWindowToAtticResponse',
                           slot, context)

    def moveWindowFromAtticMessage(self, index, slot, context):
        self.handleMessage(self.moveWindowFromAttic,
                           'moveWindowFromAtticResponse', index, slot, context)

    def moveWindowMessage(self, fromSlot, toSlot, context):
        self.handleMessage(self.moveWindow, 'moveWindowResponse', fromSlot,
                           toSlot, context)

    def deleteWindowFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWindowFromAttic,
                           'deleteWindowFromAtticResponse', blob, index,
                           context)

    def recoverDeletedItemMessage(self, blob, index, context):
        self.handleMessage(self.recoverDeletedItem,
                           'recoverDeletedItemResponse', blob, index, context)

    # Functions to safely process data off the wire:
    def getItemObject(self, doId):
        item = self.air.doId2do.get(doId)

        if item is None:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)

        if item not in self.items:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)

        return item

    def getAtticFurniture(self, attic, index):
        if index >= len(attic):
            raise FurnitureError(ToontownGlobals.FM_InvalidIndex)

        return attic[index]

    def getWindow(self, slot):
        for window in self.windows:
            if window.placement == slot:
                return window

        return None
Example #5
0
class DistributedFurnitureManagerAI(DistributedObjectAI):
    notify = DirectNotifyGlobal.directNotify.newCategory(
        'DistributedFurnitureManagerAI')
    serverDataFolder = simbase.config.GetString('server-data-folder', '')

    def __init__(self, air, house, interior):
        DistributedObjectAI.__init__(self, air)
        self.house = house
        self.interior = interior
        self.ownerId = 0
        self.ownerName = ''
        self.interiorId = 0
        self.atticItems = CatalogItemList(store=CatalogItem.Customization)
        self.atticWallpaper = CatalogItemList(store=CatalogItem.Customization)
        self.atticWindows = CatalogItemList(store=CatalogItem.Customization)
        self.deletedItems = CatalogItemList(store=CatalogItem.Customization)
        self.wallpaper = CatalogItemList(store=CatalogItem.Customization)
        self.windows = CatalogItemList(store=CatalogItem.Customization
                                       | CatalogItem.WindowPlacement)
        self.items = []
        self.director = 0
        self.deletedItemsFilename = ''
        self.day2deletedItems = {}

    def announceGenerate(self):
        DistributedObjectAI.announceGenerate(self)
        self.deletedItemsFilename = self.getDeletedItemsFilename()
        self.day2deletedItems = self.loadDeletedItems()
        taskMgr.add(self.__deletedItemsTask,
                    self.uniqueName('deleted-items-task'))

    def delete(self):
        for item in self.items[:]:
            item.requestDelete()
            self.items.remove(item)

        taskMgr.remove(self.uniqueName('deleted-items-task'))
        DistributedObjectAI.delete(self)

    def setOwnerId(self, ownerId):
        self.ownerId = ownerId

    def d_setOwnerId(self, ownerId):
        self.sendUpdate('setOwnerId', [ownerId])

    def b_setOwnerId(self, ownerId):
        self.setOwnerId(ownerId)
        self.d_setOwnerId(ownerId)

    def getOwnerId(self):
        return self.ownerId

    def setOwnerName(self, ownerName):
        self.ownerName = ownerName

    def d_setOwnerName(self, ownerName):
        self.sendUpdate('setOwnerName', [ownerName])

    def b_setOwnerName(self, ownerName):
        self.setOwnerName(ownerName)
        self.d_setOwnerName(ownerName)

    def getOwnerName(self):
        return self.ownerName

    def setInteriorId(self, interiorId):
        self.interiorId = interiorId

    def d_setInteriorId(self, interiorId):
        self.sendUpdate('setInteriorId', [interiorId])

    def b_setInteriorId(self, interiorId):
        self.setInteriorId(interiorId)
        self.d_setInteriorId(interiorId)

    def getInteriorId(self):
        return self.interiorId

    def setAtticItems(self, atticItems):
        self.atticItems = CatalogItemList(atticItems,
                                          store=CatalogItem.Customization)

    def d_setAtticItems(self, atticItems):
        self.sendUpdate('setAtticItems', [atticItems])

    def b_setAtticItems(self, atticItems):
        self.setAtticItems(atticItems)
        self.d_setAtticItems(atticItems)

    def getAtticItems(self):
        return self.atticItems.getBlob()

    def setAtticWallpaper(self, atticWallpaper):
        self.atticWallpaper = CatalogItemList(atticWallpaper,
                                              store=CatalogItem.Customization)

    def d_setAtticWallpaper(self, atticWallpaper):
        self.sendUpdate('setAtticWallpaper', [atticWallpaper])

    def b_setAtticWallpaper(self, atticWallpaper):
        self.setAtticWallpaper(atticWallpaper)
        self.d_setAtticWallpaper(atticWallpaper)

    def getAtticWallpaper(self):
        return self.atticWallpaper.getBlob()

    def setAtticWindows(self, atticWindows):
        self.atticWindows = CatalogItemList(atticWindows,
                                            store=CatalogItem.Customization)

    def d_setAtticWindows(self, atticWindows):
        self.sendUpdate('setAtticWindows', [atticWindows])

    def b_setAtticWindows(self, atticWindows):
        self.setAtticWindows(atticWindows)
        self.d_setAtticWindows(atticWindows)

    def getAtticWindows(self):
        return self.atticWindows.getBlob()

    def setDeletedItems(self, deletedItems):
        self.deletedItems = CatalogItemList(deletedItems,
                                            store=CatalogItem.Customization)

    def d_setDeletedItems(self, deletedItems):
        self.sendUpdate('setDeletedItems', [deletedItems])

    def b_setDeletedItems(self, deletedItems):
        self.setDeletedItems(deletedItems)
        self.d_setDeletedItems(deletedItems)

    def getDeletedItems(self):
        return self.deletedItems.getBlob()

    def suggestDirector(self, avId):
        if avId and avId != self.ownerId:
            self.air.writeServerEvent(
                'suspicious', avId,
                "av tried to manage furniture in someone else's house")
            return
        av = self.air.doId2do.get(avId)
        director = self.air.doId2do.get(self.director)
        if director:
            director.b_setGhostMode(0)
        else:
            av.b_setGhostMode(1)
        self.b_setDirector(avId)
        self.saveFurniture()

    def setDirector(self, director):
        self.director = director

    def d_setDirector(self, director):
        self.sendUpdate('setDirector', [director])

    def b_setDirector(self, director):
        self.setDirector(director)
        self.d_setDirector(director)

    def getDirector(self):
        return self.director

    def moveItemToAtticMessage(self, itemId, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'moveItemToAttic', context)
        if not owner:
            return
        item = self.air.doId2do.get(itemId)
        if not item or item not in self.items:
            self.sendUpdateToAvatarId(
                avId, 'moveItemToAtticResponse',
                [ToontownGlobals.FM_InvalidItem, context])
        self.atticItems.append(item.catalogItem)
        self.b_setAtticItems(self.getAtticItems())
        item.requestDelete()
        self.items.remove(item)
        self.sendUpdateToAvatarId(avId, 'moveItemToAtticResponse',
                                  [ToontownGlobals.FM_MovedItem, context])

    def moveItemFromAtticMessage(self, index, x, y, z, h, p, r, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'moveItemFromAttic', context)
        if not owner:
            return
        try:
            item = self.atticItems[index]
        except KeyError:
            self.air.writeServerEvent(
                'suspicious', avId, 'av tried to move nonexistent attic item')
            self.sendUpdateToAvatarId(
                avId, 'moveItemFromAtticResponse',
                [ToontownGlobals.FM_InvalidIndex, context])
            return

        del self.atticItems[index]
        self.b_setAtticItems(self.getAtticItems())
        item.posHpr = (x, y, z, h, p, r)
        obj = self.generateItem(item)
        self.sendUpdateToAvatarId(
            avId, 'moveItemFromAtticResponse',
            [ToontownGlobals.FM_MovedItem, obj.doId, context])

    def deleteItemFromAtticMessage(self, blob, index, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'deleteItemFromAttic', context)
        if not owner:
            return
        try:
            item = self.atticItems[index]
        except KeyError:
            self.air.writeServerEvent(
                'suspicious', avId,
                'av tried to delete nonexistent attic item')
            self.sendUpdateToAvatarId(
                avId, 'deleteItemFromAtticResponse',
                [ToontownGlobals.FM_InvalidIndex, context])
            return

        if not item.isDeletable():
            self.sendUpdateToAvatarId(
                avId, 'deleteItemFromAtticResponse',
                [ToontownGlobals.FM_NondeletableItem, context])
            return
        del self.atticItems[index]
        self.b_setAtticItems(self.getAtticItems())
        self._deleteItem(item)
        self.sendUpdateToAvatarId(avId, 'deleteItemFromAtticResponse',
                                  [ToontownGlobals.FM_DeletedItem, context])

    def deleteItemFromRoomMessage(self, blob, itemId, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'deleteItemFromRoom', context)
        if not owner:
            return
        item = self.air.doId2do.get(itemId)
        if not item or item not in self.items:
            self.sendUpdateToAvatarId(
                avId, 'deleteItemFromRoomResponse',
                [ToontownGlobals.FM_InvalidItem, context])
            return
        if not item.catalogItem.isDeletable():
            self.sendUpdateToAvatarId(
                avId, 'deleteItemFromRoomResponse',
                [ToontownGlobals.FM_NondeletableItem, context])
            return
        self.items.remove(item)
        self._deleteItem(item.catalogItem)
        item.requestDelete()
        self.sendUpdateToAvatarId(avId, 'deleteItemFromRoomResponse',
                                  [ToontownGlobals.FM_DeletedItem, context])

    def moveWallpaperFromAtticMessage(self, index, room, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'moveWallpaperFromAttic', context)
        if not owner:
            return
        try:
            item = self.atticWallpaper[index]
        except KeyError:
            self.air.writeServerEvent(
                'suspicious', avId, 'av tried to move nonexistent wallpaper')
            self.sendUpdateToAvatarId(
                avId, 'moveWallpaperFromAtticResponse',
                [ToontownGlobals.FM_InvalidIndex, context])
            return

        slot = room * CatalogSurfaceItem.NUM_ST_TYPES + item.getSurfaceType()
        self.atticWallpaper[index] = self.wallpaper[slot]
        self.b_setAtticWallpaper(self.getAtticWallpaper())
        self.wallpaper[slot] = item
        self.house.interior.b_setWallpaper(self.wallpaper.getBlob())
        self.sendUpdateToAvatarId(avId, 'moveWallpaperFromAtticResponse',
                                  [ToontownGlobals.FM_SwappedItem, context])

    def deleteWallpaperFromAtticMessage(self, blob, index, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'deleteWallpaperFromAttic', context)
        if not owner:
            return
        try:
            item = self.atticWallpaper[index]
        except KeyError:
            self.air.writeServerEvent(
                'suspicious', avId, 'av tried to delete nonexistent wallpaper')
            self.sendUpdateToAvatarId(
                avId, 'deleteWallpaperFromAtticResponse',
                [ToontownGlobals.FM_InvalidIndex, context])
            return

        del self.atticWallpaper[index]
        self.b_setAtticWallpaper(self.getAtticWallpaper())
        self._deleteItem(item)
        self.sendUpdateToAvatarId(avId, 'deleteWallpaperFromAtticResponse',
                                  [ToontownGlobals.FM_DeletedItem, context])

    def moveWindowToAtticMessage(self, slot, context):
        pass

    def moveWindowFromAtticMessage(self, index, slot, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'moveWindowFromAttic', context)
        if not owner:
            return
        else:
            try:
                item = self.atticWindows[index]
            except KeyError:
                self.air.writeServerEvent(
                    'suspicious', avId, 'av tried to move nonexistent window')
                self.sendUpdateToAvatarId(
                    avId, 'moveWindowFromAtticResponse',
                    [ToontownGlobals.FM_InvalidIndex, context])
                return

            if slot > 5:
                self.air.writeServerEvent(
                    'suspicious', avId,
                    'av tried to put window in invalid slot!')
                return
            item.placement = slot
            oldWindow = None
            windowIndex = None
            for i, window in enumerate(self.windows):
                if window.placement == slot:
                    oldWindow = window
                    windowIndex = i

            if not oldWindow:
                self.sendUpdateToAvatarId(
                    avId, 'moveWindowFromAtticResponse',
                    [ToontownGlobals.FM_InvalidIndex, context])
                return
            self.atticWindows[index] = oldWindow
            self.d_setAtticWindows(self.getAtticWindows())
            self.windows[windowIndex] = item
            self.house.interior.b_setWindows(self.windows.getBlob())
            self.sendUpdateToAvatarId(
                avId, 'moveWindowFromAtticResponse',
                [ToontownGlobals.FM_SwappedItem, context])
            return

    def moveWindowMessage(self, fromSlot, toSlot, context):
        pass

    def deleteWindowFromAtticMessage(self, blob, index, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'deleteWindowFromAttic', context)
        if not owner:
            return
        try:
            item = self.atticWindows[index]
        except KeyError:
            self.air.writeServerEvent('suspicious', avId,
                                      'av tried to delete nonexistent window')
            self.sendUpdateToAvatarId(
                avId, 'deleteWindowFromAtticResponse',
                [ToontownGlobals.FM_InvalidIndex, context])
            return

        del self.atticWindows[index]
        self.d_setAtticWindows(self.getAtticWindows())
        self._deleteItem(item)
        self.sendUpdateToAvatarId(avId, 'deleteWindowFromAtticResponse',
                                  [ToontownGlobals.FM_DeletedItem, context])

    def recoverDeletedItemMessage(self, blob, index, context):
        avId = self.air.getAvatarIdFromSender()
        owner = self._verifyOwner(avId, 'recoverDeletedItem', context)
        if not owner:
            return
        numItems = len(self.atticItems) + len(self.atticWallpaper) + len(
            self.atticWindows) + len(self.items)
        if numItems + 1 > ToontownGlobals.MaxHouseItems:
            self.sendUpdateToAvatarId(avId, 'recoverDeletedItemResponse',
                                      [ToontownGlobals.FM_HouseFull, context])
            return
        try:
            item = self.deletedItems[index]
        except KeyError:
            self.air.writeServerEvent('suspicious', avId,
                                      'av tried to recover nonexistent item')
            self.sendUpdateToAvatarId(
                avId, 'recoverDeletedItemResponse',
                [ToontownGlobals.FM_InvalidIndex, context])
            return

        type2attic = {
            CatalogSurfaceItem.CatalogSurfaceItem:
            (self.atticWallpaper, self.b_setAtticWallpaper,
             self.getAtticWallpaper),
            CatalogFurnitureItem.CatalogFurnitureItem:
            (self.atticItems, self.b_setAtticItems, self.getAtticItems),
            CatalogWindowItem.CatalogWindowItem:
            (self.atticWindows, self.b_setAtticWindows, self.getAtticWindows)
        }
        for itemType in type2attic.keys():
            if isinstance(item, itemType):
                attic, setter, getter = type2attic[itemType]
                attic.append(item)
                setter(getter())
                del self.deletedItems[index]
                self.b_setDeletedItems(self.getDeletedItems())
                self.removeDeletedItemBlob(item.getBlob().encode('base64'))
                self.sendUpdateToAvatarId(
                    avId, 'recoverDeletedItemResponse',
                    [ToontownGlobals.FM_RecoveredItem, context])

    def loadFurniture(self):
        for item in CatalogItemList(self.house.getInteriorItems(),
                                    store=CatalogItem.Customization
                                    | CatalogItem.Location):
            self.generateItem(item)

        self.wallpaper = CatalogItemList(self.house.getInteriorWallpaper(),
                                         store=CatalogItem.Customization)
        self.house.b_setInteriorWallpaper(self.house.getInteriorWallpaper())
        self.interior.b_setWallpaper(self.house.getInteriorWallpaper())
        self.windows = CatalogItemList(self.house.getInteriorWindows(),
                                       store=CatalogItem.Customization
                                       | CatalogItem.WindowPlacement)
        self.house.b_setInteriorWindows(self.house.getInteriorWindows())
        self.interior.b_setWindows(self.house.getInteriorWindows())
        self.b_setAtticItems(self.house.getAtticItems())
        self.b_setAtticWallpaper(self.house.getAtticWallpaper())
        self.b_setAtticWindows(self.house.getAtticWindows())
        self.b_setDeletedItems(self.house.getDeletedItems())

    def generateItem(self, item):
        itemId = item.furnitureType
        if itemId == 1399:
            furnitureClass = DistributedPhoneAI
        elif item.getFlags() & CatalogFurnitureItem.FLCloset:
            furnitureClass = DistributedClosetAI
        elif item.getFlags() & CatalogFurnitureItem.FLBank:
            furnitureClass = DistributedBankAI
        elif item.getFlags() & CatalogFurnitureItem.FLTrunk:
            furnitureClass = DistributedTrunkAI
        else:
            furnitureClass = DistributedFurnitureItemAI
        obj = furnitureClass(self.air, self.house, self, item)
        obj.generateWithRequired(self.interior.zoneId)
        self.items.append(obj)
        return obj

    def saveFurniture(self):
        self.house.b_setInteriorItems(self.getNewItems())
        self.house.b_setInteriorWallpaper(self.wallpaper.getBlob())
        self.house.b_setInteriorWindows(self.windows.getBlob())
        self.interior.b_setWindows(self.windows.getBlob())
        self.interior.b_setWallpaper(self.wallpaper.getBlob())
        self.house.b_setAtticItems(self.getAtticItems())
        self.b_setAtticItems(self.getAtticItems())
        self.house.b_setAtticWallpaper(self.getAtticWallpaper())
        self.b_setAtticWallpaper(self.getAtticWallpaper())
        self.house.b_setAtticWindows(self.getAtticWindows())
        self.b_setAtticWindows(self.getAtticWindows())
        self.house.b_setDeletedItems(self.getDeletedItems())
        self.b_setDeletedItems(self.getDeletedItems())

    def getNewItems(self):
        items = CatalogItemList(store=CatalogItem.Customization
                                | CatalogItem.Location)
        for item in self.items:
            items.append(item.catalogItem)

        return items.getBlob()

    def _verifyOwner(self, avId, message, context):
        if self.house.avatarId != avId:
            self.air.writeServerEvent(
                'suspicious', avId,
                'av tried to send perform furniture management operation %s while not owner!'
                % message)
            self.sendUpdateToAvatarId(avId, message + 'Response',
                                      [ToontownGlobals.FM_NotOwner, context])
            return False
        if self.director != avId:
            self.sendUpdateToAvatarId(
                avId, message + 'Response',
                [ToontownGlobals.FM_NotDirector, context])
            return False
        return True

    def _deleteItem(self, item):
        self.deletedItems.append(item)
        if len(self.deletedItems) > ToontownGlobals.ExtraDeletedItems:
            del self.deletedItems[0]
        self.b_setDeletedItems(self.getDeletedItems())
        self.addDeletedItemBlob(item.getBlob().encode('base64'))

    def getNumItems(self):
        numItems = len(self.house.interiorItems) + len(
            self.house.atticItems) + len(self.house.atticWallpaper) + len(
                self.house.atticWindows) + len(
                    self.house.interiorWallpaper) + len(
                        self.house.interiorWindows)
        return numItems

    def getDeletedItemsFilename(self):
        return '%s%s%s%s.json' % (self.serverDataFolder, 'houses/',
                                  'deletedItems_', self.house.getDoId())

    def loadDeletedItems(self):
        try:
            deletedItemsFile = open(self.deletedItemsFilename, 'r')
            deletedItemsData = json.load(deletedItemsFile)
            return deletedItemsData
        except:
            return {}

    def updateDeletedItemsFile(self):
        try:
            if not os.path.exists(os.path.dirname(self.deletedItemsFilename)):
                os.makedirs(os.path.dirname(self.deletedItemsFilename))
            deletedItemsFile = open(self.deletedItemsFilename, 'w')
            deletedItemsFile.seek(0)
            json.dump(self.day2deletedItems, deletedItemsFile)
            deletedItemsFile.close()
        except:
            pass

    def addDeletedItemBlob(self, deletedItemBlob, dayId=None):
        if dayId is None:
            dayId = getDayId()
        dayId = str(dayId)
        if dayId not in self.day2deletedItems.keys():
            self.day2deletedItems[dayId] = []
        self.day2deletedItems[dayId].append(deletedItemBlob)
        self.updateDeletedItemsFile()
        return

    def removeDeletedItemBlob(self, deletedItemBlob, dayId=None):
        if not self.day2deletedItems.keys():
            return
        else:
            if dayId is None:
                dayId = getDayId()
            dayId = str(dayId)
            if dayId not in self.day2deletedItems.keys():
                dayId = min(self.day2deletedItems.keys())
            elif not self.day2deletedItems[dayId]:
                del self.day2deletedItems[dayId]
                dayId = min(self.day2deletedItems.keys())
            dayId = str(dayId)
            if dayId in self.day2deletedItems.keys(
            ) and deletedItemBlob in self.day2deletedItems[dayId]:
                self.day2deletedItems[dayId].remove(deletedItemBlob)
            if not self.day2deletedItems[dayId]:
                del self.day2deletedItems[dayId]
            self.updateDeletedItemsFile()
            return

    def __deletedItemsTask(self, task):
        changesMade = False
        dayId = getDayId()
        for deletedItemDay, deletedItemBlobs in self.day2deletedItems.items():
            if not deletedItemBlobs or type(deletedItemBlobs) != list:
                del self.day2deletedItems[deletedItemDay]
                changesMade = True
                continue
            try:
                deletedItemDayId = int(deletedItemDay)
            except:
                del self.day2deletedItems[deletedItemDay]
                changesMade = True
                continue

            if deletedItemDayId + int(
                    ToontownGlobals.DeletedItemLifetime / 60 / 24) <= dayId:
                for deletedItemBlob in deletedItemBlobs[:]:
                    try:
                        deletedItem = CatalogItem.getItem(
                            deletedItemBlob.decode('base64'))
                    except:
                        self.day2deletedItems[deletedItemDay].remove(
                            deletedItemBlob)
                        if not self.day2deletedItems[deletedItemDay]:
                            del self.day2deletedItems[deletedItemDay]
                        changesMade = True
                        continue

                    if isinstance(deletedItem,
                                  CatalogInvalidItem.CatalogInvalidItem):
                        self.day2deletedItems[deletedItemDay].remove(
                            deletedItemBlob)
                        if not self.day2deletedItems[deletedItemDay]:
                            del self.day2deletedItems[deletedItemDay]
                        changesMade = True
                        continue
                    if deletedItem not in self.deletedItems:
                        self.day2deletedItems[deletedItemDay].remove(
                            deletedItemBlob)
                        if not self.day2deletedItems[deletedItemDay]:
                            del self.day2deletedItems[deletedItemDay]
                        changesMade = True
                        continue
                    index = self.deletedItems.index(deletedItem)
                    del self.deletedItems[index]
                    self.b_setDeletedItems(self.getDeletedItems())
                    self.day2deletedItems[deletedItemDay].remove(
                        deletedItemBlob)
                    if not self.day2deletedItems[deletedItemDay]:
                        del self.day2deletedItems[deletedItemDay]
                    changesMade = True

        if changesMade:
            self.updateDeletedItemsFile()
        tomorrow = self.air.toontownTimeManager.getCurServerDateTime().now(
            tz=self.air.toontownTimeManager.serverTimeZone
        ) + datetime.timedelta(1)
        midnight = datetime.datetime(
            year=tomorrow.year,
            month=tomorrow.month,
            day=tomorrow.day,
            hour=0,
            minute=0,
            second=0,
            tzinfo=self.air.toontownTimeManager.serverTimeZone)
        secondsUntilMidnight = (
            midnight - self.air.toontownTimeManager.getCurServerDateTime().now(
                tz=self.air.toontownTimeManager.serverTimeZone)).seconds
        task.delayTime = secondsUntilMidnight
        return task.again
class DistributedFurnitureManagerAI(DistributedObjectAI):
    notify = directNotify.newCategory("DistributedFurnitureManagerAI")

    def __init__(self, air, house, interior):
        DistributedObjectAI.__init__(self, air)

        self.house = house
        self.interior = interior

        self.director = None

        self.ownerId = house.avatarId
        self.ownerName = house.name

        self.atticItems = None
        self.atticWallpaper = None
        self.wallpaper = None
        self.atticWindows = None
        self.windows = None
        self.deletedItems = None

        self.items = []

        # Initialize the above variables:
        self.loadFromHouse()

    def announceGenerate(self):
        DistributedObjectAI.announceGenerate(self)

        for item in self.items:
            item.generateWithRequired(self.zoneId)

    def delete(self):
        for item in self.items:
            item.destroy()

        DistributedObjectAI.delete(self)

    def loadFromHouse(self):
        self.b_setAtticItems(self.house.getAtticItems())
        self.b_setAtticWallpaper(self.house.getAtticWallpaper())
        self.b_setAtticWindows(self.house.getAtticWindows())
        self.b_setDeletedItems(self.house.getDeletedItems())

        self.wallpaper = CatalogItemList(self.house.getInteriorWallpaper(),
                                         store=CatalogItem.Customization)
        self.applyWallpaper()
        self.windows = CatalogItemList(self.house.getInteriorWindows(),
                                       store=CatalogItem.Customization |
                                             CatalogItem.WindowPlacement)
        self.applyWindows()

        self.setItems(self.house.getInteriorItems())

    def saveToHouse(self):
        self.house.b_setAtticItems(self.getAtticItems())
        self.house.b_setAtticWallpaper(self.getAtticWallpaper())
        self.house.b_setAtticWindows(self.getAtticWindows())
        self.house.b_setDeletedItems(self.getDeletedItems())

        self.house.b_setInteriorWallpaper(self.wallpaper.getBlob())
        self.house.b_setInteriorWindows(self.windows.getBlob())

        self.house.b_setInteriorItems(self.getItems())

    def applyWallpaper(self):
        self.interior.b_setWallpaper(self.wallpaper.getBlob())

    def applyWindows(self):
        self.interior.b_setWindows(self.windows.getBlob())

    def setItems(self, items):
        # Decode the blob:
        items = CatalogItemList(items, store=CatalogItem.Customization|CatalogItem.Location)

        # Throw out our old items:
        for item in self.items:
            item.destroy()
        self.items = []

        items.removeDuplicates(FLCloset)

        # Due to a bug, some people are missing their closets...
        hasCloset = False
        for item in items:
            if item.getFlags() & FLCloset:
                hasCloset = True
                break

        if not hasCloset:
            item = CatalogFurnitureItem(500)  # the basic closet...
            item.posHpr = (0.0, 0.0, 0.0, 0.0, 0.0, 0.0)
            items.append(item)
            # Since we have modified the items list, should we save it back to the house?

        for item in items:
            if item.getFlags() & FLTrunk:
                if self.house.gender is 0:
                    if item.furnitureType - 4000 < 10:
                        item.furnitureType += 10
                elif item.furnitureType - 4000 > 10:
                    item.furnitureType -= 10
                do = DistributedTrunkAI(self.air, self, item)
            elif item.getFlags() & FLCloset:
                if self.house.gender is 0:
                    if item.furnitureType - 500 < 10:
                        item.furnitureType += 10
                elif item.furnitureType - 500 > 10:
                    item.furnitureType -= 10
                do = DistributedClosetAI(self.air, self, item)
            elif item.getFlags() & FLBank:
                continue # We dont want banks in the estates.
            elif item.getFlags() & FLPhone:
                do = DistributedPhoneAI(self.air, self, item)
            else:
                do = DistributedFurnitureItemAI(self.air, self, item)
            if self.isGenerated():
                do.generateWithRequired(self.zoneId)
            self.items.append(do)

    def getItems(self):
        items = CatalogItemList(store=CatalogItem.Customization|CatalogItem.Location)

        for item in self.items:
            items.append(item.catalogItem)

        return items.getBlob()

    def setOwnerId(self, ownerId):
        self.ownerId = ownerId

    def d_setOwnerId(self, ownerId):
        self.sendUpdate('setOwnerId', [ownerId])

    def b_setOwnerId(self, ownerId):
        self.setOwnerId(ownerId)
        self.d_setOwnerId(ownerId)

    def getOwnerId(self):
        return self.ownerId

    def setOwnerName(self, ownerName):
        self.ownerName = ownerName

    def d_setOwnerName(self, ownerName):
        self.sendUpdate('setOwnerName', [ownerName])

    def b_setOwnerName(self, ownerName):
        self.setOwnerName(ownerName)
        self.d_setOwnerName(ownerName)

    def getOwnerName(self):
        return self.ownerName

    def getInteriorId(self):
        return self.interior.doId

    def setAtticItems(self, items):
        self.atticItems = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticItems(self, items):
        self.sendUpdate('setAtticItems', [items])

    def b_setAtticItems(self, items):
        self.setAtticItems(items)
        if self.isGenerated():
            self.d_setAtticItems(items)

    def getAtticItems(self):
        return self.atticItems.getBlob()

    def setAtticWallpaper(self, items):
        self.atticWallpaper = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticWallpaper(self, items):
        self.sendUpdate('setAtticWallpaper', [items])

    def b_setAtticWallpaper(self, items):
        self.setAtticWallpaper(items)
        if self.isGenerated():
            self.d_setAtticWallpaper(items)

    def getAtticWallpaper(self):
        return self.atticWallpaper.getBlob()

    def setAtticWindows(self, items):
        self.atticWindows = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setAtticWindows(self, items):
        self.sendUpdate('setAtticWindows', [items])

    def b_setAtticWindows(self, items):
        self.setAtticWindows(items)
        if self.isGenerated():
            self.d_setAtticWindows(items)

    def getAtticWindows(self):
        return self.atticWindows.getBlob()

    def setDeletedItems(self, items):
        self.deletedItems = CatalogItemList(items, store=CatalogItem.Customization)

    def d_setDeletedItems(self, items):
        self.sendUpdate('setDeletedItems', [items])

    def b_setDeletedItems(self, items):
        self.setDeletedItems(items)
        if self.isGenerated():
            self.d_setDeletedItems(items)

    def getDeletedItems(self):
        return self.deletedItems.getBlob()

    def suggestDirector(self, directorId):
        senderId = self.air.getAvatarIdFromSender()

        if self.ownerId != senderId:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to move furniture, but not the house owner!')
            return

        if senderId != directorId and directorId != 0:
            self.air.writeServerEvent('suspicious', avId=senderId, issue='Tried to make someone else (%d) move their furniture!' % directorId)
            return

        director = self.air.doId2do.get(directorId)
        if directorId and not director:
            self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to move furniture without being on the shard!')
            return

        if self.director:
            self.director.b_setGhostMode(0)
        if director:
            if director.zoneId != self.zoneId:
                self.air.writeServerEvent('suspicious', avId=directorId, issue='Tried to become director from another zone!')
                return
            director.b_setGhostMode(1)

        self.director = director
        self.sendUpdate('setDirector', [directorId])

        # Let's also save the furniture to the house (and thus to the DB) while
        # we're at it...
        self.saveToHouse()

    def avatarEnter(self):
        pass

    def avatarExit(self):
        pass

    # Furniture-manipulation:
    def moveItemToAttic(self, doId):
        item = self.getItemObject(doId)

        self.atticItems.append(item.catalogItem)
        self.d_setAtticItems(self.getAtticItems())

        item.destroy()
        self.items.remove(item)

        return ToontownGlobals.FM_MovedItem

    def moveItemFromAttic(self, index, x, y, z, h, p, r):
        item = self.getAtticFurniture(self.atticItems, index)

        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())

        item.posHpr = (x, y, z, h, p, r)

        if item.getFlags() & FLTrunk:
            if self.house.gender is 0:
                if item.furnitureType - 4000 < 10:
                    item.furnitureType += 10
            elif item.furnitureType - 4000 > 10:
                item.furnitureType -= 10
            do = DistributedTrunkAI(self.air, self, item)
        elif item.getFlags() & FLCloset:
            if self.house.gender is 0:
                if item.furnitureType - 500 < 10:
                    item.furnitureType += 10
            elif item.furnitureType - 500 > 10:
                item.furnitureType -= 10
            do = DistributedClosetAI(self.air, self, item)
        elif item.getFlags() & FLBank:
            pass # We don't want banks in the estates
        elif item.getFlags() & FLPhone:
            do = DistributedPhoneAI(self.air, self, item)
        else:
            do = DistributedFurnitureItemAI(self.air, self, item)

        do.generateWithRequired(self.zoneId)
        self.items.append(do)

        return (ToontownGlobals.FM_MovedItem, do.doId)

    def deleteItemFromAttic(self, blob, index):
        item = self.getAtticFurniture(self.atticItems, index)
        if item is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid item at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex

        self.atticItems.remove(item)
        self.d_setAtticItems(self.getAtticItems())

        return ToontownGlobals.FM_DeletedItem

    def deleteItemFromRoom(self, blob, doId):
        pass

    def moveWallpaperFromAttic(self, index, room):
        return ToontownGlobals.FM_InvalidIndex

        retcode = ToontownGlobals.FM_SwappedItem
        wallpaper = self.getAtticFurniture(self.atticWallpaper, index)
        if wallpaper is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Invalid wallpaper at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex
        if room > 1:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to apply a wallpaper in an invalid room %d!' % room)
            return ToontownGlobals.FM_InvalidItem
        interiorIndex = room + wallpaper.getSurfaceType()
        interiorIndex += wallpaper.getSurfaceType() - 1
        print room
        print interiorIndex
        atticIndex = self.atticWallpaper.index(wallpaper)
        self.atticWallpaper[atticIndex] = self.wallpaper[interiorIndex]
        self.d_setAtticWallpaper(self.getAtticWallpaper())
        self.wallpaper[interiorIndex] = wallpaper
        self.applyWallpaper()
        return retcode

    def deleteWallpaperFromAttic(self, blob, index):
        return # TODO

        wallpaper = self.getAtticFurniture(blob, index)
        self.atticWallpaper.remove(wallpaper)
        self.b_setAtticWallpaper(self.getAtticWallpaper())

    def moveWindowToAttic(self, slot):
        window = self.getWindow(slot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex
        self.windows.remove(window)
        self.applyWindows()
        self.atticWindows.append(window)
        self.d_setAtticWindows(self.getAtticWindows())
        return ToontownGlobals.FM_MovedItem

    def moveWindowFromAttic(self, index, slot):
        retcode = ToontownGlobals.FM_MovedItem
        window = self.getAtticFurniture(self.atticWindows, index)
        if slot > 5:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(),
                                      issue='Tried to move window to invalid slot %d!' % slot)
            return ToontownGlobals.FM_HouseFull
        if self.getWindow(slot):
            self.moveWindowToAttic(slot)
            retcode = ToontownGlobals.FM_SwappedItem
        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())
        window.placement = slot
        self.windows.append(window)
        self.applyWindows()
        return retcode

    def moveWindow(self, fromSlot, toSlot):
        retcode = ToontownGlobals.FM_MovedItem
        window = self.getWindow(fromSlot)
        if window is None:
            return ToontownGlobals.FM_InvalidIndex
        if toSlot > 5:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(),
                                      issue='DistributedfTried to move window to invalid slot %d!' % toSlot)
            return ToontownGlobals.FM_HouseFull
        if self.getWindow(toSlot):
            self.moveWindowToAttic(toSlot)
            retcode = ToontownGlobals.FM_SwappedItem
        window.placement = toSlot
        self.applyWindows()
        return retcode

    def deleteWindowFromAttic(self, blob, index):
        window = self.getAtticFurniture(self.atticWindows, index)
        if window is None:
            self.air.writeServerEvent('suspicious', avId=self.air.getAvatarIdFromSender(), issue='Tried to delete an invalid window at index %s' % index)
            return ToontownGlobals.FM_InvalidIndex
        self.atticWindows.remove(window)
        self.d_setAtticWindows(self.getAtticWindows())
        return ToontownGlobals.FM_DeletedItem

    def recoverDeletedItem(self, blob, index):
        pass


    def handleMessage(self, func, response, *args):
        context = args[-1]
        args = args[:-1]
        senderId = self.air.getAvatarIdFromSender()
        if not self.director or senderId != self.director.doId:
            self.air.writeServerEvent('suspicious', avId=senderId,
                                      issue='Sent furniture management request without'
                                      ' being the director.')
            retval = ToontownGlobals.FM_NotDirector
        else:
            try:
                retval = func(*args) or 0
            except FurnitureError as e:
                retval = e.code
        if response == 'moveItemFromAtticResponse':
            if type(retval) == tuple:
                retval, doId = retval
            else:
                doId = 0
            taskMgr.doMethodLater(1, self.sendUpdateToAvatarId,
                                  self.uniqueName('send-attic-response'),
                                  extraArgs=[senderId, response, [retval, doId, context]])
        else:
            self.sendUpdateToAvatarId(senderId, response, [retval, context])

    def moveItemToAtticMessage(self, doId, context):
        self.handleMessage(self.moveItemToAttic, 'moveItemToAtticResponse', doId, context)

    def moveItemFromAtticMessage(self, index, x, y, z, h, p, r, context):
        self.handleMessage(self.moveItemFromAttic, 'moveItemFromAtticResponse', index, x, y, z, h, p, r, context)

    def deleteItemFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteItemFromAttic, 'deleteItemFromAtticResponse', blob, index, context)

    def deleteItemFromRoomMessage(self, blob, doId, context):
        self.handleMessage(self.deleteItemFromRoom, 'deleteItemFromRoomResponse', blob, doId, context)

    def moveWallpaperFromAtticMessage(self, index, room, context):
        self.handleMessage(self.moveWallpaperFromAttic, 'moveWallpaperFromAtticResponse', index, room, context)

    def deleteWallpaperFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWallpaperFromAttic, 'deleteWallpaperFromAtticResponse', blob, index, context)

    def moveWindowToAtticMessage(self, slot, context):
        self.handleMessage(self.moveWindowToAttic, 'moveWindowToAtticResponse', slot, context)

    def moveWindowFromAtticMessage(self, index, slot, context):
        self.handleMessage(self.moveWindowFromAttic, 'moveWindowFromAtticResponse', index, slot, context)

    def moveWindowMessage(self, fromSlot, toSlot, context):
        self.handleMessage(self.moveWindow, 'moveWindowResponse', fromSlot, toSlot, context)

    def deleteWindowFromAtticMessage(self, blob, index, context):
        self.handleMessage(self.deleteWindowFromAttic, 'deleteWindowFromAtticResponse', blob, index, context)

    def recoverDeletedItemMessage(self, blob, index, context):
        self.handleMessage(self.recoverDeletedItem, 'recoverDeletedItemResponse', blob, index, context)

    def getItemObject(self, doId):
        item = self.air.doId2do.get(doId)
        if item is None:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)
        if item not in self.items:
            raise FurnitureError(ToontownGlobals.FM_InvalidItem)
        return item

    def getAtticFurniture(self, attic, index):
        if index >= len(attic):
            raise FurnitureError(ToontownGlobals.FM_InvalidIndex)
        return attic[index]

    def getWindow(self, slot):
        for window in self.windows:
            if window.placement == slot:
                return window
        return None