from direct.directtools.DirectSelection import * from direct.directtools.DirectUtil import ROUND_TO from direct.directtools.DirectGeometry import LineNodePath from direct.gui.DirectGui import * from panda3d.core import * from direct.showbase.DirectObject import DirectObject from toontown.toonbase import ToontownGlobals from direct.directnotify import DirectNotifyGlobal from direct.task import Task from toontown.catalog import CatalogFurnitureItem from toontown.catalog import CatalogItemTypes from direct.showbase import PythonUtil from toontown.toontowngui import TTDialog from toontown.toonbase import TTLocalizer from otp.otpbase import OTPLocalizer camPos50 = (Point3(0.0, -10.0, 50.0), Point3(0.0, -9.66, 49.06), Point3(0.0, 1.5, 12.38), Point3(0.0, 1.5, -3.1), 1) camPos40 = (Point3(0.0, -15.0, 40.0), Point3(0.0, -14.5, 39.13), Point3(0.0, 1.5, 12.38), Point3(0.0, 1.5, -3.1), 1) camPos30 = (Point3(0.0, -20.0, 30.0), Point3(0.0, -19.29, 29.29), Point3(0.0, 1.5, 12.38), Point3(0.0, 1.5, -3.1), 1) camPos20 = (Point3(0.0, -20.0, 20.0), Point3(0.0, -19.13, 19.5), Point3(0.0, 1.5, 12.38), Point3(0.0, 1.5, -3.1), 1) camPosList = [camPos20, camPos30, camPos40, camPos50] DEFAULT_CAM_INDEX = 2 NormalPickerPanelColor = (1, 0.9, 0.745, 1) DisabledPickerPanelColor = (0.7, 0.65, 0.58, 1) DeletePickerPanelColor = (1, 0.4, 0.4, 1) DisabledDeletePickerPanelColor = (0.7, 0.3, 0.3, 1) class FurnitureItemPanel(DirectButton): def __init__(self, item, itemId, command = None, deleteMode = 0, withinFunc = None, helpCategory = None): self.item = item self.itemId = itemId self.command = command self.origHelpCategory = helpCategory self.deleteMode = deleteMode if self.deleteMode: framePanelColor = DeletePickerPanelColor else: framePanelColor = NormalPickerPanelColor DirectButton.__init__(self, relief=DGG.RAISED, frameSize=(-0.25, 0.25, -0.2, 0.2), frameColor=framePanelColor, borderWidth=(0.02, 0.02), command=self.clicked) if self.deleteMode: helpCategory = 'FurnitureItemPanelDelete' self.bindHelpText(helpCategory) if withinFunc: self.bind(DGG.WITHIN, lambda event: withinFunc(self.itemId)) self.initialiseoptions(FurnitureItemPanel) self.load() def show(self): DirectFrame.show(self) if self.ival: self.ival.resume() def hide(self): DirectFrame.hide(self) if self.ival: self.ival.pause() def load(self): panelWidth = 7 panelCenter = 0 self.picture, self.ival = self.item.getPicture(base.localAvatar) if self.picture: self.picture.reparentTo(self) self.picture.setScale(0.14) self.picture.setPos(0, 0, -0.02) text = self.item.getName() text_pos = (0, -0.1, 0) else: text = self.item.getTypeName() + ': ' + self.item.getName() text_pos = (0, -0.3, 0) if self.ival: self.ival.loop() self.ival.pause() self.nameLabel = DirectLabel(parent=self, relief=None, pos=(0, 0, 0.17), scale=0.45, text=text, text_scale=0.15, text_fg=(0, 0, 0, 1), text_pos=text_pos, text_font=ToontownGlobals.getInterfaceFont(), text_wordwrap=panelWidth) return def clicked(self): self.command(self.item, self.itemId) def unload(self): if self.item.hasPicture: self.item.cleanupPicture() del self.item self.nameLabel.destroy() del self.nameLabel if self.ival: self.ival.finish() del self.ival del self.picture self.command = None return def destroy(self): self.unload() DirectButton.destroy(self) def bindHelpText(self, category): self.unbind(DGG.ENTER) self.unbind(DGG.EXIT) if category is None: category = self.origHelpCategory self.bind(DGG.ENTER, base.cr.objectManager.showHelpText, extraArgs=[category, self.item.getName()]) self.bind(DGG.EXIT, base.cr.objectManager.hideHelpText) return def setDeleteMode(self, deleteMode): self.deleteMode = deleteMode self.__updateAppearance() def enable(self, enabled): if enabled: self['state'] = DGG.NORMAL else: self['state'] = DGG.DISABLED self.__updateAppearance() def __updateAppearance(self): color = NormalPickerPanelColor relief = DGG.RAISED if self.deleteMode: if self['state'] == DGG.DISABLED: color = DisabledDeletePickerPanelColor relief = DGG.SUNKEN else: color = DeletePickerPanelColor relief = DGG.RAISED elif self['state'] == DGG.DISABLED: color = DisabledPickerPanelColor relief = DGG.SUNKEN else: color = NormalPickerPanelColor relief = DGG.RAISED self['frameColor'] = color class MovableObject(NodePath, DirectObject): def __init__(self, dfitem, parent = render): NodePath.__init__(self) self.assign(dfitem) self.dfitem = dfitem dfitem.transmitRelativeTo = dfitem.getParent() self.reparentTo(parent) self.setTag('movableObject', '1') self.builtInCNodes = self.findAllMatches('**/+CollisionNode') self.numBuiltInNodes = self.builtInCNodes.getNumPaths() self.stashBuiltInCollisionNodes() shadows = self.findAllMatches('**/*shadow*') shadows.addPathsFrom(self.findAllMatches('**/*Shadow*')) shadows.stash() flags = self.dfitem.item.getFlags() if flags & CatalogFurnitureItem.FLPainting: self.setOnFloor(0) self.setOnWall(1) else: self.setOnFloor(1) self.setOnWall(0) if flags & CatalogFurnitureItem.FLOnTable: self.setOnTable(1) else: self.setOnTable(0) if flags & CatalogFurnitureItem.FLRug: self.setIsRug(1) else: self.setIsRug(0) if flags & CatalogFurnitureItem.FLIsTable: self.setIsTable(1) else: self.setIsTable(0) m = self.getTransform() self.setPosHpr(0, 0, 0, 0, 0, 0) bMin, bMax = self.bounds = self.getTightBounds() bMin -= Vec3(0.1, 0.1, 0) bMax += Vec3(0.1, 0.1, 0) self.c0 = Point3(bMin[0], bMin[1], 0.2) self.c1 = Point3(bMax[0], bMin[1], 0.2) self.c2 = Point3(bMax[0], bMax[1], 0.2) self.c3 = Point3(bMin[0], bMax[1], 0.2) self.center = (bMin + bMax) / 2.0 if flags & CatalogFurnitureItem.FLPainting: self.dragPoint = Vec3(self.center[0], bMax[1], self.center[2]) else: self.dragPoint = Vec3(self.center[0], self.center[1], bMin[2]) delta = self.dragPoint - self.c0 self.radius = min(delta[0], delta[1]) if self.getOnWall(): self.setWallOffset(0.1) else: self.setWallOffset(self.radius + 0.1) self.makeCollisionBox() self.setTransform(m) self.unstashBuiltInCollisionNodes() shadows.unstash() def resetMovableObject(self): self.unstashBuiltInCollisionNodes() self.collisionNodePath.removeNode() self.clearTag('movableObject') def setOnFloor(self, fOnFloor): self.fOnFloor = fOnFloor def getOnFloor(self): return self.fOnFloor def setOnWall(self, fOnWall): self.fOnWall = fOnWall def getOnWall(self): return self.fOnWall def setOnTable(self, fOnTable): self.fOnTable = fOnTable def getOnTable(self): return self.fOnTable def setIsRug(self, fIsRug): self.fIsRug = fIsRug def getIsRug(self): return self.fIsRug def setIsTable(self, fIsTable): self.fIsTable = fIsTable def getIsTable(self): return self.fIsTable def setWallOffset(self, offset): self.wallOffset = offset def getWallOffset(self): return self.wallOffset def destroy(self): self.removeNode() def stashBuiltInCollisionNodes(self): self.builtInCNodes.stash() def unstashBuiltInCollisionNodes(self): self.builtInCNodes.unstash() def getFloorBitmask(self): if self.getOnTable(): return ToontownGlobals.FloorBitmask | ToontownGlobals.FurnitureTopBitmask else: return ToontownGlobals.FloorBitmask def getWallBitmask(self): if self.getIsRug() or self.getOnWall(): return ToontownGlobals.WallBitmask else: return ToontownGlobals.WallBitmask | ToontownGlobals.FurnitureSideBitmask def makeCollisionBox(self): self.collisionNodePath = self.attachNewNode('furnitureCollisionNode') if self.getIsRug() or self.getOnWall(): return mx = self.bounds[0][0] - 0.01 Mx = self.bounds[1][0] + 0.01 my = self.bounds[0][1] - 0.01 My = self.bounds[1][1] + 0.01 mz = self.bounds[0][2] Mz = self.bounds[1][2] cn = CollisionNode('sideCollisionNode') cn.setIntoCollideMask(ToontownGlobals.FurnitureSideBitmask) self.collisionNodePath.attachNewNode(cn) cp = CollisionPolygon(Point3(mx, My, mz), Point3(mx, my, mz), Point3(mx, my, Mz), Point3(mx, My, Mz)) cn.addSolid(cp) cp = CollisionPolygon(Point3(Mx, my, mz), Point3(Mx, My, mz), Point3(Mx, My, Mz), Point3(Mx, my, Mz)) cn.addSolid(cp) cp = CollisionPolygon(Point3(mx, my, mz), Point3(Mx, my, mz), Point3(Mx, my, Mz), Point3(mx, my, Mz)) cn.addSolid(cp) cp = CollisionPolygon(Point3(Mx, My, mz), Point3(mx, My, mz), Point3(mx, My, Mz), Point3(Mx, My, Mz)) cn.addSolid(cp) if self.getIsTable(): cn = CollisionNode('topCollisionNode') cn.setIntoCollideMask(ToontownGlobals.FurnitureTopBitmask) self.collisionNodePath.attachNewNode(cn) cp = CollisionPolygon(Point3(mx, my, Mz), Point3(Mx, my, Mz), Point3(Mx, My, Mz), Point3(mx, My, Mz)) cn.addSolid(cp) class ObjectManager(NodePath, DirectObject): notify = DirectNotifyGlobal.directNotify.newCategory('ObjectManager') def __init__(self): NodePath.__init__(self) self.assign(render.attachNewNode('objectManager')) self.objectDict = {} self.selectedObject = None self.movingObject = 0 self.deselectEvent = None self.startPose = render.attachNewNode('startPose') self.dragPointNP = self.attachNewNode('dragPoint') self.gridSnapNP = self.dragPointNP.attachNewNode('gridSnap') self.collisionOffsetNP = self.gridSnapNP.attachNewNode('collisionResponse') self.iRay = SelectionRay() self.iSegment = SelectionSegment(numSegments=6) self.iSegment4 = SelectionSegment(numSegments=4) self.iSphere = SelectionSphere() self.houseExtents = None self.doorBlocker = None cp = CollisionPolygon(Point3(-100, -100, 0), Point3(100, -100, 0), Point3(100, 100, 0), Point3(-100, 100, 0)) cn = CollisionNode('dragCollisionNode') cn.addSolid(cp) cn.setIntoCollideMask(ToontownGlobals.FurnitureDragBitmask) self.collisionNP = NodePath(cn) self.lnp = LineNodePath() self.fRecenter = 0 self.gridSpacing = None self.firstTime = 0 guiModels = loader.loadModel('phase_5.5/models/gui/house_design_gui') self.createSelectedObjectPanel(guiModels) self.createMainControls(guiModels) self.furnitureManager = None self.atticPicker = None self.inRoomPicker = None self.inTrashPicker = None self.dialog = None self.deleteMode = 0 self.nonDeletableItem = None self.verifyFrame = None self.deleteItemText = None self.okButton = None self.cancelButton = None self.itemIval = None self.itemPanel = None self.guiInterval = None self.accept('enterFurnitureMode', self.enterFurnitureMode) self.accept('exitFurnitureMode', self.exitFurnitureMode) return def enterFurnitureMode(self, furnitureManager, fDirector): if not fDirector: if self.furnitureManager: self.exitFurnitureMode(self.furnitureManager) return if furnitureManager == self.furnitureManager: return if self.furnitureManager != None: self.exitFurnitureMode(self.furnitureManager) self.notify.info('enterFurnitureMode, fDirector = %s' % fDirector) self.furnitureManager = furnitureManager self.furnitureManager.d_avatarEnter() house = furnitureManager.getInteriorObject() house.hideExteriorWindows() self.setTargetNodePath(house.interior) self.createAtticPicker() self.initializeDistributedFurnitureItems(furnitureManager.dfitems) self.setCamPosIndex(DEFAULT_CAM_INDEX) base.localAvatar.setGhostMode(1) taskMgr.remove('editModeTransition') self.orientCamH(base.localAvatar.getH(self.targetNodePath)) self.accept('mouse1', self.moveObjectStart) self.accept('mouse1-up', self.moveObjectStop) self.furnitureGui.show() self.deleteMode = 0 self.__updateDeleteButtons() self.showAtticPicker() base.localAvatar.laffMeter.stop() base.setCellsAvailable(base.leftCells + [base.bottomCells[0]], 0) if self.guiInterval: self.guiInterval.finish() self.guiInterval = self.furnitureGui.posHprScaleInterval(1.0, Point3(-1.16, 1, -0.03), Vec3(0), Vec3(0.06), startPos=Point3(-1.19, 1, 0.33), startHpr=Vec3(0), startScale=Vec3(0.04), blendType='easeInOut', name='lerpFurnitureButton') self.guiInterval.start() taskMgr.add(self.recenterButtonFrameTask, 'recenterButtonFrameTask', 10) messenger.send('wakeup') return def exitFurnitureMode(self, furnitureManager): if furnitureManager != self.furnitureManager: return self.notify.info('exitFurnitureMode') house = furnitureManager.getInteriorObject() if house: house.showExteriorWindows() self.furnitureManager.d_avatarExit() self.furnitureManager = None base.localAvatar.setCameraPositionByIndex(0) self.exitDeleteMode() self.houseExtents.detachNode() self.doorBlocker.detachNode() self.deselectObject() self.ignore('mouse1') self.ignore('mouse1-up') if self.atticPicker: self.atticPicker.destroy() self.atticPicker = None if self.inRoomPicker: self.inRoomPicker.destroy() self.inRoomPicker = None if self.inTrashPicker: self.inTrashPicker.destroy() self.inTrashPicker = None self.__cleanupVerifyDelete() self.furnitureGui.hide() base.setCellsAvailable(base.leftCells + [base.bottomCells[0]], 1) base.localAvatar.laffMeter.start() taskMgr.remove('recenterButtonFrameTask') self.cleanupDialog() taskMgr.remove('showHelpTextDoLater') messenger.send('wakeup') return def initializeDistributedFurnitureItems(self, dfitems): self.objectDict = {} for item in dfitems: mo = MovableObject(item, parent=self.targetNodePath) self.objectDict[mo.id()] = mo def setCamPosIndex(self, index): self.camPosIndex = index base.localAvatar.setCameraSettings(camPosList[index]) def zoomCamIn(self): self.setCamPosIndex(max(0, self.camPosIndex - 1)) messenger.send('wakeup') def zoomCamOut(self): self.setCamPosIndex(min(len(camPosList) - 1, self.camPosIndex + 1)) messenger.send('wakeup') def rotateCamCW(self): self.orientCamH(base.localAvatar.getH(self.targetNodePath) - 90) messenger.send('wakeup') def rotateCamCCW(self): self.orientCamH(base.localAvatar.getH(self.targetNodePath) + 90) messenger.send('wakeup') def orientCamH(self, toonH): targetH = ROUND_TO(toonH, 90) base.localAvatar.hprInterval(duration=1, hpr=Vec3(targetH, 0, 0), other=self.targetNodePath, blendType='easeInOut', name='editModeTransition').start() def setTargetNodePath(self, nodePath): self.targetNodePath = nodePath if self.houseExtents: self.houseExtents.removeNode() if self.doorBlocker: self.doorBlocker.removeNode() self.makeHouseExtentsBox() self.makeDoorBlocker() self.collisionNP.reparentTo(self.targetNodePath) def loadObject(self, filename): mo = MovableObject(filename, parent=self.targetNodePath) self.objectDict[mo.id()] = mo self.selectObject(mo) return mo def pickObject(self): self.iRay.setParentNP(base.cam) entry = self.iRay.pickGeom(targetNodePath=self.targetNodePath, skipFlags=SKIP_ALL) if entry: nodePath = entry.getIntoNodePath() if self.isMovableObject(nodePath): self.selectObject(self.findObject(nodePath)) return self.deselectObject() def pickInRoom(self, objectId): self.selectObject(self.objectDict.get(objectId)) def selectObject(self, selectedObject): messenger.send('wakeup') if self.selectedObject: self.deselectObject() if selectedObject: self.selectedObject = selectedObject self.deselectEvent = self.selectedObject.dfitem.uniqueName('disable') self.acceptOnce(self.deselectEvent, self.deselectObject) self.lnp.reset() self.lnp.reparentTo(selectedObject) self.lnp.moveTo(selectedObject.c0) self.lnp.drawTo(selectedObject.c1) self.lnp.drawTo(selectedObject.c2) self.lnp.drawTo(selectedObject.c3) self.lnp.drawTo(selectedObject.c0) self.lnp.create() self.buttonFrame.show() self.enableButtonFrameTask() self.sendToAtticButton.show() self.atticRoof.hide() def deselectObject(self): self.moveObjectStop() if self.deselectEvent: self.ignore(self.deselectEvent) self.deselectEvent = None self.selectedObject = None self.lnp.detachNode() self.buttonFrame.hide() self.disableButtonFrameTask() self.sendToAtticButton.hide() self.atticRoof.show() return def isMovableObject(self, nodePath): return nodePath.hasNetTag('movableObject') def findObject(self, nodePath): np = nodePath.findNetTag('movableObject') if np.isEmpty(): return None else: return self.objectDict.get(np.id(), None) return None def moveObjectStop(self, *args): if self.movingObject: self.movingObject = 0 taskMgr.remove('moveObjectTask') if self.selectedObject: self.selectedObject.wrtReparentTo(self.targetNodePath) self.selectedObject.collisionNodePath.unstash() self.selectedObject.dfitem.stopAdjustPosHpr() for object in list(self.objectDict.values()): object.unstashBuiltInCollisionNodes() self.centerMarker['image'] = [self.grabUp, self.grabDown, self.grabRollover] self.centerMarker.configure(text=['', TTLocalizer.HDMoveLabel], text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=0.3) def moveObjectStart(self): self.moveObjectStop() self.pickObject() self.moveObjectContinue() def moveObjectContinue(self, *args): messenger.send('wakeup') if self.selectedObject: for object in list(self.objectDict.values()): object.stashBuiltInCollisionNodes() self.selectedObject.collisionNodePath.stash() self.selectedObject.dfitem.startAdjustPosHpr() self.firstTime = 1 self.setPosHpr(0, 0, 0, 0, 0, 0) self.startPoseValid = 0 self.centerMarker['image'] = self.grabDown self.centerMarker.configure(text=TTLocalizer.HDMoveLabel, text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image_scale=0.3) taskMgr.add(self.moveObjectTask, 'moveObjectTask') self.movingObject = 1 def setLnpColor(self, r, g, b): for i in range(5): self.lnp.lineSegs.setVertexColor(i, r, g, b) def markNewPosition(self, isValid): if not isValid: if self.startPoseValid: self.collisionOffsetNP.setPosHpr(self.startPose, self.selectedObject.dragPoint, Vec3(0)) else: self.startPoseValid = 1 def moveObjectTask(self, state): so = self.selectedObject target = self.targetNodePath self.startPose.setPosHpr(so, 0, 0, 0, 0, 0, 0) self.iRay.setParentNP(base.cam) entry = self.iRay.pickBitMask(bitMask=ToontownGlobals.FurnitureDragBitmask, targetNodePath=target, skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) if not entry: return Task.cont self.setPos(base.cam, entry.getSurfacePoint(base.cam)) if self.firstTime: self.moveObjectInit() self.firstTime = 0 else: self.gridSnapNP.setPos(0, 0, 0) self.collisionOffsetNP.setPosHpr(0, 0, 0, 0, 0, 0) if self.gridSpacing: pos = self.dragPointNP.getPos(target) self.gridSnapNP.setPos(target, ROUND_TO(pos[0], self.gridSpacing), ROUND_TO(pos[1], self.gridSpacing), pos[2]) self.iRay.setParentNP(base.cam) entry = self.iRay.pickBitMask3D(bitMask=so.getWallBitmask(), targetNodePath=target, dir=Vec3(self.getNearProjectionPoint(self.gridSnapNP)), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) fWall = 0 if not so.getOnTable(): while entry: intoMask = entry.getIntoNodePath().node().getIntoCollideMask() fClosest = (intoMask & ToontownGlobals.WallBitmask).isZero() if self.alignObject(entry, target, fClosest=fClosest): fWall = 1 break entry = self.iRay.findNextCollisionEntry(skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) if so.getOnWall(): self.markNewPosition(fWall) return Task.cont self.iRay.setParentNP(target) entry = self.iRay.pickBitMask3D(bitMask=so.getFloorBitmask(), targetNodePath=target, origin=Point3(self.gridSnapNP.getPos(target) + Vec3(0, 0, 10)), dir=Vec3(0, 0, -1), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) if not entry: self.markNewPosition(0) return Task.cont nodePath = entry.getIntoNodePath() if self.isMovableObject(nodePath): self.gridSnapNP.setPos(target, Point3(entry.getSurfacePoint(target))) else: self.gridSnapNP.setPos(target, Point3(entry.getSurfacePoint(target) + Vec3(0, 0, ToontownGlobals.FloorOffset))) if not fWall: self.iSphere.setParentNP(self.gridSnapNP) self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=target, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) if entry: self.alignObject(entry, target, fClosest=1) isValid = self.collisionTest() self.markNewPosition(isValid) return Task.cont def collisionTest(self): so = self.selectedObject target = self.targetNodePath entry = self.segmentCollision() if not entry: return 1 offsetDict = {} while entry: offset = self.computeSegmentOffset(entry) if offset: eid = entry.getInto() maxOffsetVec = offsetDict.get(eid, Vec3(0)) if offset.length() > maxOffsetVec.length(): maxOffsetVec.assign(offset) offsetDict[eid] = maxOffsetVec entry = self.iSegment.findNextCollisionEntry(skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) if offsetDict: keys = list(offsetDict.keys()) ortho1 = offsetDict[keys[0]] ortho2 = Vec3(0) v1 = Vec3(ortho1) v1.normalize() for key in keys[1:]: offset = offsetDict[key] v2 = Vec3(offset) v2.normalize() dp = v1.dot(v2) if abs(dp) > 0.95: if offset.length() > ortho1.length(): ortho1.assign(offset) elif abs(dp) < 0.05: if offset.length() > ortho2.length(): ortho2.assign(offset) else: o1Len = ortho1.length() parallelVec = Vec3(ortho1 * offset.dot(ortho1) / (o1Len * o1Len)) perpVec = Vec3(offset - parallelVec) if parallelVec.length() > o1Len: ortho1.assign(parallelVec) if perpVec.length() > ortho2.length(): ortho2.assign(perpVec) totalOffset = ortho1 + ortho2 self.collisionOffsetNP.setPos(self.collisionOffsetNP, totalOffset) if not self.segmentCollision(): return 1 m = self.startPose.getMat(so) deltaMove = Vec3(m.getRow3(3)) if deltaMove.length() == 0: return 1 self.iSegment4.setParentNP(so) entry = self.iSegment4.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=target, endPointList=[(so.c0, Point3(m.xformPoint(so.c0))), (so.c1, Point3(m.xformPoint(so.c1))), (so.c2, Point3(m.xformPoint(so.c2))), (so.c3, Point3(m.xformPoint(so.c3)))], skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) maxLen = 0 maxOffset = None while entry: offset = Vec3(entry.getSurfacePoint(entry.getFromNodePath()) - entry.getFrom().getPointA()) offsetLen = Vec3(offset).length() if offsetLen > maxLen: maxLen = offsetLen maxOffset = offset entry = self.iSegment4.findNextCollisionEntry(skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) if maxOffset: self.collisionOffsetNP.setPos(self.collisionOffsetNP, maxOffset) if not self.segmentCollision(): return 1 return 0 def segmentCollision(self): so = self.selectedObject self.iSegment.setParentNP(so) entry = self.iSegment.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, endPointList=[(so.c0, so.c1), (so.c1, so.c2), (so.c2, so.c3), (so.c3, so.c0), (so.c0, so.c2), (so.c1, so.c3)], skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) return entry def computeSegmentOffset(self, entry): fromNodePath = entry.getFromNodePath() if entry.hasSurfaceNormal(): normal = entry.getSurfaceNormal(fromNodePath) else: return None hitPoint = entry.getSurfacePoint(fromNodePath) m = self.selectedObject.getMat(self.startPose) hp = Point3(m.xformPoint(hitPoint)) hpn = Vec3(m.xformVec(normal)) hitPointVec = Vec3(hp - self.selectedObject.dragPoint) if hitPointVec.dot(hpn) > 0: return None nLen = normal.length() offsetVecA = hitPoint - entry.getFrom().getPointA() offsetA = normal * offsetVecA.dot(normal) / (nLen * nLen) if offsetA.dot(normal) > 0: return offsetA * 1.01 else: offsetVecB = hitPoint - entry.getFrom().getPointB() offsetB = normal * offsetVecB.dot(normal) / (nLen * nLen) return offsetB * 1.01 return None def alignObject(self, entry, target, fClosest = 0, wallOffset = None): if not entry.hasSurfaceNormal(): return 0 normal = entry.getSurfaceNormal(target) if abs(normal.dot(Vec3(0, 0, 1))) < 0.1: tempNP = target.attachNewNode('temp') normal.setZ(0) normal.normalize() lookAtNormal = Point3(normal) lookAtNormal *= -1 tempNP.lookAt(lookAtNormal) realAngle = ROUND_TO(self.gridSnapNP.getH(tempNP), 90.0) if fClosest: angle = realAngle else: angle = 0 self.gridSnapNP.setHpr(tempNP, angle, 0, 0) hitPoint = entry.getSurfacePoint(target) tempNP.setPos(hitPoint) if wallOffset == None: wallOffset = self.selectedObject.getWallOffset() self.gridSnapNP.setPos(tempNP, 0, -wallOffset, 0) tempNP.removeNode() if realAngle == 180.0: self.gridSnapNP.setH(self.gridSnapNP.getH() + 180.0) return 1 return 0 def rotateLeft(self): if not self.selectedObject: return so = self.selectedObject so.dfitem.startAdjustPosHpr() self.setPosHpr(so, 0, 0, 0, 0, 0, 0) self.moveObjectInit() if so.getOnWall(): startR = self.gridSnapNP.getR() newR = ROUND_TO(startR + 22.5, 22.5) self.gridSnapNP.setR(newR) else: startH = self.gridSnapNP.getH(self.targetNodePath) newH = ROUND_TO(startH - 22.5, 22.5) self.iSphere.setParentNP(self.gridSnapNP) self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) if not entry: self.gridSnapNP.setHpr(self.targetNodePath, newH, 0, 0) self.collisionTest() so.wrtReparentTo(self.targetNodePath) self.disableButtonFrameTask() so.dfitem.stopAdjustPosHpr() def rotateRight(self): if not self.selectedObject: return so = self.selectedObject so.dfitem.startAdjustPosHpr() self.setPosHpr(so, 0, 0, 0, 0, 0, 0) self.moveObjectInit() if so.getOnWall(): startR = self.gridSnapNP.getR() newR = ROUND_TO(startR - 22.5, 22.5) self.gridSnapNP.setR(newR) else: startH = self.gridSnapNP.getH(self.targetNodePath) newH = ROUND_TO(startH + 22.5, 22.5) % 360.0 self.iSphere.setParentNP(self.gridSnapNP) self.iSphere.setCenterRadius(0, Point3(0), so.radius * 1.25) entry = self.iSphere.pickBitMask(bitMask=so.getWallBitmask(), targetNodePath=self.targetNodePath, skipFlags=SKIP_CAMERA | SKIP_UNPICKABLE) if not entry: self.gridSnapNP.setHpr(self.targetNodePath, newH, 0, 0) self.collisionTest() so.wrtReparentTo(self.targetNodePath) self.disableButtonFrameTask() so.dfitem.stopAdjustPosHpr() def moveObjectInit(self): self.dragPointNP.setPosHpr(self.selectedObject, self.selectedObject.dragPoint, Vec3(0)) self.gridSnapNP.setPosHpr(0, 0, 0, 0, 0, 0) self.collisionOffsetNP.setPosHpr(0, 0, 0, 0, 0, 0) self.selectedObject.wrtReparentTo(self.collisionOffsetNP) def resetFurniture(self): for o in list(self.objectDict.values()): o.resetMovableObject() self.objectDict = {} self.deselectObject() self.buttonFrame.hide() def destroy(self): self.ignore('enterFurnitureMode') self.ignore('exitFurnitureMode') if self.guiInterval: self.guiInterval.finish() if self.furnitureManager: self.exitFurnitureMode(self.furnitureManager) self.cleanupDialog() self.resetFurniture() self.buttonFrame.destroy() self.furnitureGui.destroy() if self.houseExtents: self.houseExtents.removeNode() if self.doorBlocker: self.doorBlocker.removeNode() self.removeNode() if self.verifyFrame: self.verifyFrame.destroy() self.verifyFrame = None self.deleteItemText = None self.okButton = None self.cancelButton = None return def createSelectedObjectPanel(self, guiModels): self.buttonFrame = DirectFrame(scale=0.5) self.grabUp = guiModels.find('**/handup') self.grabDown = guiModels.find('**/handdown') self.grabRollover = guiModels.find('**/handrollover') self.centerMarker = DirectButton(parent=self.buttonFrame, text=['', TTLocalizer.HDMoveLabel], text_pos=(0, 1), text_scale=0.7, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), image=[self.grabUp, self.grabDown, self.grabRollover], image_scale=0.3, relief=None, scale=0.12) self.centerMarker.bind(DGG.B1PRESS, self.moveObjectContinue) self.centerMarker.bind(DGG.B1RELEASE, self.moveObjectStop) guiCCWArrowUp = guiModels.find('**/LarrowUp') guiCCWArrowDown = guiModels.find('**/LarrowDown') guiCCWArrowRollover = guiModels.find('**/LarrowRollover') self.rotateLeftButton = DirectButton(parent=self.buttonFrame, relief=None, image=(guiCCWArrowUp, guiCCWArrowDown, guiCCWArrowRollover, guiCCWArrowUp), image_pos=(0, 0, 0.1), image_scale=0.15, image3_color=Vec4(0.5, 0.5, 0.5, 0.75), text=('', TTLocalizer.HDRotateCCWLabel, TTLocalizer.HDRotateCCWLabel, ''), text_pos=(0.135, -0.1), text_scale=0.1, text_align=TextNode.ARight, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(-.125, 0, -.2), scale=0.7, command=self.rotateLeft) self.rotateLeftButton.bind(DGG.EXIT, self.enableButtonFrameTask) guiCWArrowUp = guiModels.find('**/RarrowUp') guiCWArrowDown = guiModels.find('**/RarrowDown') guiCWArrowRollover = guiModels.find('**/RarrowRollover') self.rotateRightButton = DirectButton(parent=self.buttonFrame, relief=None, image=(guiCWArrowUp, guiCWArrowDown, guiCWArrowRollover, guiCWArrowUp), image_pos=(0, 0, 0.1), image_scale=0.15, image3_color=Vec4(0.5, 0.5, 0.5, 0.75), text=('', TTLocalizer.HDRotateCWLabel, TTLocalizer.HDRotateCWLabel, ''), text_pos=(-0.135, -0.1), text_scale=0.1, text_align=TextNode.ALeft, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), pos=(0.125, 0, -0.2), scale=0.7, command=self.rotateRight) self.rotateRightButton.bind(DGG.EXIT, self.enableButtonFrameTask) self.buttonFrame.hide() return def recenterButtonFrameTask(self, state): if self.selectedObject and self.fRecenter: self.buttonFrame.setPos(self.getSelectedObjectScreenXY()) return Task.cont def disableButtonFrameTask(self, event = None): self.fRecenter = 0 def enableButtonFrameTask(self, event = None): self.fRecenter = 1 def getNearProjectionPoint(self, nodePath): origin = nodePath.getPos(camera) if origin[1] != 0.0: return origin * (base.camLens.getNear() / origin[1]) else: return Point3(0, base.camLens.getNear(), 0) def getSelectedObjectScreenXY(self): tNodePath = self.selectedObject.attachNewNode('temp') tNodePath.setPos(self.selectedObject.center) nearVec = self.getNearProjectionPoint(tNodePath) nearVec *= base.camLens.getFocalLength() / base.camLens.getNear() render2dX = CLAMP(nearVec[0] / (base.camLens.getFilmSize()[0] / 2.0), -.9, 0.9) aspect2dX = render2dX * base.getAspectRatio() aspect2dZ = CLAMP(nearVec[2] / (base.camLens.getFilmSize()[1] / 2.0), -.8, 0.9) tNodePath.removeNode() return Vec3(aspect2dX, 0, aspect2dZ) def createMainControls(self, guiModels): attic = guiModels.find('**/attic') self.furnitureGui = DirectFrame(relief=None, pos=(-1.19, 1, 0.33), scale=0.04, image=attic) bMoveStopUp = guiModels.find('**/bu_atticX/bu_attic_up') bMoveStopDown = guiModels.find('**/bu_atticX/bu_attic_down') bMoveStopRollover = guiModels.find('**/bu_atticX/bu_attic_rollover') self.bStopMoveFurniture = DirectButton(parent=self.furnitureGui, relief=None, image=[bMoveStopUp, bMoveStopDown, bMoveStopRollover, bMoveStopUp], text=['', TTLocalizer.HDStopMoveFurnitureButton, TTLocalizer.HDStopMoveFurnitureButton], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_font=ToontownGlobals.getInterfaceFont(), pos=(-0.3, 0, 9.4), command=base.localAvatar.stopMoveFurniture) self.bindHelpText(self.bStopMoveFurniture, 'DoneMoving') self.atticRoof = DirectLabel(parent=self.furnitureGui, relief=None, image=guiModels.find('**/rooftile')) self.itemBackgroundFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/item_backgroun'), image_pos=(0, 0, -22), image_scale=(1, 1, 5)) self.scrollUpFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/scrollup'), pos=(0, 0, -0.58)) self.camButtonFrame = DirectFrame(parent=self.furnitureGui, relief=None, image=guiModels.find('**/low'), pos=(0, 0, -11.69)) tagUp = guiModels.find('**/tag_up') tagDown = guiModels.find('**/tag_down') tagRollover = guiModels.find('**/tag_rollover') self.inAtticButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInAtticLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, 4), scale=0.8, command=self.showAtticPicker) self.bindHelpText(self.inAtticButton, 'Attic') self.inRoomButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInRoomLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, 1.1), scale=0.8, command=self.showInRoomPicker) self.bindHelpText(self.inRoomButton, 'Room') self.inTrashButton = DirectButton(parent=self.itemBackgroundFrame, relief=None, text=TTLocalizer.HDInTrashLabel, text_pos=(-0.1, -0.25), image=[tagUp, tagDown, tagRollover], pos=(2.85, 0, -1.8), scale=0.8, command=self.showInTrashPicker) self.bindHelpText(self.inTrashButton, 'Trash') for i in range(4): self.inAtticButton.component('text%d' % i).setR(-90) self.inRoomButton.component('text%d' % i).setR(-90) self.inTrashButton.component('text%d' % i).setR(-90) backInAtticUp = guiModels.find('**/bu_backinattic_up1') backInAtticDown = guiModels.find('**/bu_backinattic_down1') backInAtticRollover = guiModels.find('**/bu_backinattic_rollover2') self.sendToAtticButton = DirectButton(parent=self.furnitureGui, relief=None, pos=(0.4, 0, 12.8), text=['', TTLocalizer.HDToAtticLabel], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_pos=(1.2, -0.3), image=[backInAtticUp, backInAtticDown, backInAtticRollover], command=self.sendItemToAttic) self.sendToAtticButton.hide() self.bindHelpText(self.sendToAtticButton, 'SendToAttic') zoomInUp = guiModels.find('**/bu_RzoomOut_up') zoomInDown = guiModels.find('**/bu_RzoomOut_down') zoomInRollover = guiModels.find('**/bu_RzoomOut_rollover') self.zoomInButton = DirectButton(parent=self.camButtonFrame, image=[zoomInUp, zoomInDown, zoomInRollover], relief=None, pos=(0.9, 0, -0.75), command=self.zoomCamIn) self.bindHelpText(self.zoomInButton, 'ZoomIn') zoomOutUp = guiModels.find('**/bu_LzoomIn_up') zoomOutDown = guiModels.find('**/bu_LzoomIn_down') zoomOutRollover = guiModels.find('**/buLzoomIn_rollover') self.zoomOutButton = DirectButton(parent=self.camButtonFrame, image=[zoomOutUp, zoomOutDown, zoomOutRollover], relief=None, pos=(-1.4, 0, -0.75), command=self.zoomCamOut) self.bindHelpText(self.zoomOutButton, 'ZoomOut') camCCWUp = guiModels.find('**/bu_Rarrow_up1') camCCWDown = guiModels.find('**/bu_Rarrow_down1') camCCWRollover = guiModels.find('**/bu_Rarrow_orllover') self.rotateCamLeftButton = DirectButton(parent=self.camButtonFrame, image=[camCCWUp, camCCWDown, camCCWRollover], relief=None, pos=(0.9, 0, -3.0), command=self.rotateCamCCW) self.bindHelpText(self.rotateCamLeftButton, 'RotateLeft') camCWUp = guiModels.find('**/bu_Larrow_up1') camCWDown = guiModels.find('**/bu_Larrow_down1') camCWRollover = guiModels.find('**/bu_Larrow_rollover2') self.rotateCamRightButton = DirectButton(parent=self.camButtonFrame, image=[camCWUp, camCWDown, camCWRollover], relief=None, pos=(-1.4, 0, -3.0), command=self.rotateCamCW) self.bindHelpText(self.rotateCamRightButton, 'RotateRight') trashcanGui = loader.loadModel('phase_3/models/gui/trashcan_gui') trashcanUp = trashcanGui.find('**/TrashCan_CLSD') trashcanDown = trashcanGui.find('**/TrashCan_OPEN') trashcanRollover = trashcanGui.find('**/TrashCan_RLVR') self.deleteEnterButton = DirectButton(parent=self.furnitureGui, image=(trashcanUp, trashcanDown, trashcanRollover, trashcanUp), text=['', TTLocalizer.InventoryDelete, TTLocalizer.InventoryDelete, ''], text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_align=TextNode.ACenter, text_pos=(0, -0.12), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=0, relief=None, pos=(3.7, 0.0, -13.8), scale=7.13, command=self.enterDeleteMode) self.bindHelpText(self.deleteEnterButton, 'DeleteEnter') self.deleteExitButton = DirectButton(parent=self.furnitureGui, image=(trashcanUp, trashcanDown, trashcanRollover, trashcanUp), text=('', TTLocalizer.InventoryDone, TTLocalizer.InventoryDone, ''), text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_align=TextNode.ACenter, text_pos=(0, -0.12), text_font=ToontownGlobals.getInterfaceFont(), textMayChange=0, relief=None, pos=(3.7, 0.0, -13.8), scale=7.13, command=self.exitDeleteMode) self.bindHelpText(self.deleteExitButton, 'DeleteExit') self.deleteExitButton.hide() self.trashcanBase = DirectLabel(parent=self.furnitureGui, image=guiModels.find('**/trashcan_base'), relief=None, pos=(0, 0, -11.64)) self.furnitureGui.hide() self.helpText = DirectLabel(parent=self.furnitureGui, relief=DGG.SUNKEN, frameSize=(-0.5, 10, -3, 0.9), frameColor=(0.2, 0.2, 0.2, 0.5), borderWidth=(0.01, 0.01), text='', text_wordwrap=12, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.8, pos=(3, 0.0, -7), scale=1, text_align=TextNode.ALeft) self.helpText.hide() return def createAtticPicker(self): self.atticItemPanels = [] for itemIndex in range(len(self.furnitureManager.atticItems)): panel = FurnitureItemPanel(self.furnitureManager.atticItems[itemIndex], itemIndex, command=self.bringItemFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') self.atticItemPanels.append(panel) self.atticWallpaperPanels = [] for itemIndex in range(len(self.furnitureManager.atticWallpaper)): panel = FurnitureItemPanel(self.furnitureManager.atticWallpaper[itemIndex], itemIndex, command=self.bringWallpaperFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') self.atticWallpaperPanels.append(panel) self.atticWindowPanels = [] for itemIndex in range(len(self.furnitureManager.atticWindows)): panel = FurnitureItemPanel(self.furnitureManager.atticWindows[itemIndex], itemIndex, command=self.bringWindowFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') self.atticWindowPanels.append(panel) self.regenerateAtticPicker() def regenerateAtticPicker(self): selectedIndex = 0 if self.atticPicker: selectedIndex = self.atticPicker.getSelectedIndex() for panel in self.atticItemPanels: panel.detachNode() for panel in self.atticWallpaperPanels: panel.detachNode() for panel in self.atticWindowPanels: panel.detachNode() self.atticPicker.destroy() self.atticPicker = None itemList = self.atticItemPanels + self.atticWallpaperPanels + self.atticWindowPanels if self.deleteMode: text = TTLocalizer.HDDeletePickerLabel else: text = TTLocalizer.HDAtticPickerLabel self.atticPicker = self.createScrolledList(itemList, text, 'atticPicker', selectedIndex) if self.inRoomPicker or self.inTrashPicker: self.atticPicker.hide() else: self.atticPicker.show() return def createInRoomPicker(self): self.inRoomPanels = [] for objectId, object in list(self.objectDict.items()): panel = FurnitureItemPanel(object.dfitem.item, objectId, command=self.requestReturnToAttic, deleteMode=self.deleteMode, withinFunc=self.pickInRoom, helpCategory='FurnitureItemPanelRoom') self.inRoomPanels.append(panel) self.regenerateInRoomPicker() def regenerateInRoomPicker(self): selectedIndex = 0 if self.inRoomPicker: selectedIndex = self.inRoomPicker.getSelectedIndex() for panel in self.inRoomPanels: panel.detachNode() self.inRoomPicker.destroy() self.inRoomPicker = None if self.deleteMode: text = TTLocalizer.HDDeletePickerLabel else: text = TTLocalizer.HDInRoomPickerLabel self.inRoomPicker = self.createScrolledList(self.inRoomPanels, text, 'inRoomPicker', selectedIndex) return def createInTrashPicker(self): self.inTrashPanels = [] for itemIndex in range(len(self.furnitureManager.deletedItems)): panel = FurnitureItemPanel(self.furnitureManager.deletedItems[itemIndex], itemIndex, command=self.requestReturnToAtticFromTrash, helpCategory='FurnitureItemPanelTrash') self.inTrashPanels.append(panel) self.regenerateInTrashPicker() def regenerateInTrashPicker(self): selectedIndex = 0 if self.inTrashPicker: selectedIndex = self.inTrashPicker.getSelectedIndex() for panel in self.inTrashPanels: panel.detachNode() self.inTrashPicker.destroy() self.inTrashPicker = None text = TTLocalizer.HDInTrashPickerLabel self.inTrashPicker = self.createScrolledList(self.inTrashPanels, text, 'inTrashPicker', selectedIndex) return def createScrolledList(self, itemList, text, name, selectedIndex): gui = loader.loadModel('phase_3.5/models/gui/friendslist_gui') picker = DirectScrolledList(parent=self.furnitureGui, pos=(-0.38, 0.0, 3), scale=7.125, relief=None, items=itemList, numItemsVisible=5, text=text, text_fg=(1, 1, 1, 1), text_shadow=(0, 0, 0, 1), text_scale=0.1, text_pos=(0, 0.4), decButton_image=(gui.find('**/FndsLst_ScrollUp'), gui.find('**/FndsLst_ScrollDN'), gui.find('**/FndsLst_ScrollUp_Rllvr'), gui.find('**/FndsLst_ScrollUp')), decButton_relief=None, decButton_scale=(1.5, 1.5, 1.5), decButton_pos=(0, 0, 0.3), decButton_image3_color=Vec4(1, 1, 1, 0.1), incButton_image=(gui.find('**/FndsLst_ScrollUp'), gui.find('**/FndsLst_ScrollDN'), gui.find('**/FndsLst_ScrollUp_Rllvr'), gui.find('**/FndsLst_ScrollUp')), incButton_relief=None, incButton_scale=(1.5, 1.5, -1.5), incButton_pos=(0, 0, -1.878), incButton_image3_color=Vec4(1, 1, 1, 0.1)) picker.setName(name) picker.scrollTo(selectedIndex) return picker def reset(): self.destroy() furnitureMenu.destroy() def showAtticPicker(self): if self.inRoomPicker: self.inRoomPicker.destroy() self.inRoomPicker = None if self.inTrashPicker: self.inTrashPicker.destroy() self.inTrashPicker = None self.atticPicker.show() self.inAtticButton['image_color'] = Vec4(1, 1, 1, 1) self.inRoomButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) self.inTrashButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) self.deleteExitButton['state'] = 'normal' self.deleteEnterButton['state'] = 'normal' return def showInRoomPicker(self): messenger.send('wakeup') if not self.inRoomPicker: self.createInRoomPicker() self.atticPicker.hide() if self.inTrashPicker: self.inTrashPicker.destroy() self.inTrashPicker = None self.inAtticButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) self.inRoomButton['image_color'] = Vec4(1, 1, 1, 1) self.inTrashButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) self.deleteExitButton['state'] = 'normal' self.deleteEnterButton['state'] = 'normal' return def showInTrashPicker(self): messenger.send('wakeup') if not self.inTrashPicker: self.createInTrashPicker() self.atticPicker.hide() if self.inRoomPicker: self.inRoomPicker.destroy() self.inRoomPicker = None self.inAtticButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) self.inRoomButton['image_color'] = Vec4(0.8, 0.8, 0.8, 1) self.inTrashButton['image_color'] = Vec4(1, 1, 1, 1) self.deleteExitButton['state'] = 'disabled' self.deleteEnterButton['state'] = 'disabled' return def sendItemToAttic(self): if base.config.GetBool('want-qa-regression', 0): self.notify.info('QA-REGRESSION: ESTATE: Send Item to Attic') messenger.send('wakeup') if self.selectedObject: callback = PythonUtil.Functor(self.__sendItemToAtticCallback, self.selectedObject.id()) self.furnitureManager.moveItemToAttic(self.selectedObject.dfitem, callback) self.deselectObject() def __sendItemToAtticCallback(self, objectId, retcode, item): self.__enableItemButtons(1) if retcode < 0: self.notify.info('Unable to send item %s to attic, reason %s.' % (item.getName(), retcode)) return del self.objectDict[objectId] if self.selectedObject != None and self.selectedObject.id() == objectId: self.selectedObject.detachNode() self.deselectObject() itemIndex = len(self.atticItemPanels) panel = FurnitureItemPanel(item, itemIndex, command=self.bringItemFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') self.atticItemPanels.append(panel) self.regenerateAtticPicker() if self.inRoomPicker: for i in range(len(self.inRoomPanels)): if self.inRoomPanels[i].itemId == objectId: del self.inRoomPanels[i] self.regenerateInRoomPicker() return return def cleanupDialog(self, buttonValue = None): if self.dialog: self.dialog.cleanup() self.dialog = None self.__enableItemButtons(1) return def enterDeleteMode(self): self.deleteMode = 1 self.__updateDeleteMode() def exitDeleteMode(self): self.deleteMode = 0 self.__updateDeleteMode() def __updateDeleteMode(self): if not self.atticPicker: return self.notify.debug('__updateDeleteMode deleteMode=%s' % self.deleteMode) if self.deleteMode: framePanelColor = DeletePickerPanelColor atticText = TTLocalizer.HDDeletePickerLabel inRoomText = TTLocalizer.HDDeletePickerLabel helpCategory = 'FurnitureItemPanelDelete' else: framePanelColor = NormalPickerPanelColor atticText = TTLocalizer.HDAtticPickerLabel inRoomText = TTLocalizer.HDInRoomPickerLabel helpCategory = None if self.inRoomPicker: self.inRoomPicker['text'] = inRoomText for panel in self.inRoomPicker['items']: panel.setDeleteMode(self.deleteMode) panel.bindHelpText(helpCategory) if self.atticPicker: self.atticPicker['text'] = atticText for panel in self.atticPicker['items']: panel.setDeleteMode(self.deleteMode) panel.bindHelpText(helpCategory) self.__updateDeleteButtons() return def __updateDeleteButtons(self): if self.deleteMode: self.deleteExitButton.show() self.deleteEnterButton.hide() else: self.deleteEnterButton.show() self.deleteExitButton.hide() def deleteItemFromRoom(self, dfitem, objectId, itemIndex): messenger.send('wakeup') callback = PythonUtil.Functor(self.__deleteItemFromRoomCallback, objectId, itemIndex) self.furnitureManager.deleteItemFromRoom(dfitem, callback) def __deleteItemFromRoomCallback(self, objectId, itemIndex, retcode, item): self.__enableItemButtons(1) if retcode < 0: self.notify.info('Unable to delete item %s from room, reason %s.' % (item.getName(), retcode)) return del self.objectDict[objectId] if self.selectedObject != None and self.selectedObject.id() == objectId: self.selectedObject.detachNode() self.deselectObject() if self.inRoomPicker and itemIndex is not None: del self.inRoomPanels[itemIndex] self.regenerateInRoomPicker() return def bringItemFromAttic(self, item, itemIndex): if base.config.GetBool('want-qa-regression', 0): self.notify.info('QA-REGRESSION: ESTATE: Place Item in Room') messenger.send('wakeup') self.__enableItemButtons(0) if self.deleteMode: self.requestDelete(item, itemIndex, self.deleteItemFromAttic) return pos = self.targetNodePath.getRelativePoint(base.localAvatar, Point3(0, 2, 0)) hpr = Point3(0, 0, 0) if abs(pos[0]) > 3000 or abs(pos[1]) > 3000 or abs(pos[2]) > 300: self.notify.warning('bringItemFromAttic extreme pos targetNodePath=%s avatar=%s %s' % (repr(self.targetNodePath.getPos(render)), repr(base.localAvatar.getPos(render)), repr(pos))) if item.getFlags() & CatalogFurnitureItem.FLPainting: for object in list(self.objectDict.values()): object.stashBuiltInCollisionNodes() self.gridSnapNP.setPosHpr(0, 0, 0, 0, 0, 0) target = self.targetNodePath self.iRay.setParentNP(base.localAvatar) entry = self.iRay.pickBitMask3D(bitMask=ToontownGlobals.WallBitmask, targetNodePath=target, origin=Point3(0, 0, 6), dir=Vec3(0, 1, 0), skipFlags=SKIP_BACKFACE | SKIP_CAMERA | SKIP_UNPICKABLE) for object in list(self.objectDict.values()): object.unstashBuiltInCollisionNodes() if entry: self.alignObject(entry, target, fClosest=0, wallOffset=0.1) pos = self.gridSnapNP.getPos(target) hpr = self.gridSnapNP.getHpr(target) else: self.notify.warning('wall not found for painting') self.furnitureManager.moveItemFromAttic(itemIndex, (pos[0], pos[1], pos[2], hpr[0], hpr[1], hpr[2]), self.__bringItemFromAtticCallback) def __bringItemFromAtticCallback(self, retcode, dfitem, itemIndex): self.__enableItemButtons(1) if retcode < 0: self.notify.info('Unable to bring furniture item %s into room, reason %s.' % (itemIndex, retcode)) return mo = self.loadObject(dfitem) objectId = mo.id() self.atticItemPanels[itemIndex].destroy() del self.atticItemPanels[itemIndex] for i in range(itemIndex, len(self.atticItemPanels)): self.atticItemPanels[i].itemId -= 1 self.regenerateAtticPicker() if self.inRoomPicker: panel = FurnitureItemPanel(dfitem.item, objectId, command=self.requestReturnToAttic, helpCategory='FurnitureItemPanelRoom') self.inRoomPanels.append(panel) self.regenerateInRoomPicker() def deleteItemFromAttic(self, item, itemIndex): messenger.send('wakeup') self.furnitureManager.deleteItemFromAttic(item, itemIndex, self.__deleteItemFromAtticCallback) def __deleteItemFromAtticCallback(self, retcode, item, itemIndex): self.__enableItemButtons(1) if retcode < 0: self.notify.info('Unable to delete furniture item %s, reason %s.' % (itemIndex, retcode)) return self.atticItemPanels[itemIndex].destroy() del self.atticItemPanels[itemIndex] for i in range(itemIndex, len(self.atticItemPanels)): self.atticItemPanels[i].itemId -= 1 self.regenerateAtticPicker() def bringWallpaperFromAttic(self, item, itemIndex): messenger.send('wakeup') self.__enableItemButtons(0) if self.deleteMode: self.requestDelete(item, itemIndex, self.deleteWallpaperFromAttic) return if base.localAvatar.getY() < 2.3: room = 0 else: room = 1 self.furnitureManager.moveWallpaperFromAttic(itemIndex, room, self.__bringWallpaperFromAtticCallback) def __bringWallpaperFromAtticCallback(self, retcode, itemIndex, room): self.__enableItemButtons(1) if retcode < 0: self.notify.info('Unable to bring wallpaper %s into room %s, reason %s.' % (itemIndex, room, retcode)) return self.atticWallpaperPanels[itemIndex].destroy() item = self.furnitureManager.atticWallpaper[itemIndex] panel = FurnitureItemPanel(item, itemIndex, command=self.bringWallpaperFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') self.atticWallpaperPanels[itemIndex] = panel self.regenerateAtticPicker() def deleteWallpaperFromAttic(self, item, itemIndex): messenger.send('wakeup') self.furnitureManager.deleteWallpaperFromAttic(item, itemIndex, self.__deleteWallpaperFromAtticCallback) def __deleteWallpaperFromAtticCallback(self, retcode, item, itemIndex): self.__enableItemButtons(1) if retcode < 0: self.notify.info('Unable to delete wallpaper %s, reason %s.' % (itemIndex, retcode)) return self.atticWallpaperPanels[itemIndex].destroy() del self.atticWallpaperPanels[itemIndex] for i in range(itemIndex, len(self.atticWallpaperPanels)): self.atticWallpaperPanels[i].itemId -= 1 self.regenerateAtticPicker() def bringWindowFromAttic(self, item, itemIndex): messenger.send('wakeup') self.__enableItemButtons(0) if self.deleteMode: self.requestDelete(item, itemIndex, self.deleteWindowFromAttic) return if base.localAvatar.getY() < 2.3: slot = 2 else: slot = 4 self.furnitureManager.moveWindowFromAttic(itemIndex, slot, self.__bringWindowFromAtticCallback) def __bringWindowFromAtticCallback(self, retcode, itemIndex, slot): self.__enableItemButtons(1) if retcode < 0: self.notify.info('Unable to bring window %s into slot %s, reason %s.' % (itemIndex, slot, retcode)) return if retcode == ToontownGlobals.FM_SwappedItem: self.atticWindowPanels[itemIndex].destroy() item = self.furnitureManager.atticWindows[itemIndex] panel = FurnitureItemPanel(item, itemIndex, command=self.bringWindowFromAttic, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') self.atticWindowPanels[itemIndex] = panel else: self.atticWindowPanels[itemIndex].destroy() del self.atticWindowPanels[itemIndex] for i in range(itemIndex, len(self.atticWindowPanels)): self.atticWindowPanels[i].itemId -= 1 self.regenerateAtticPicker() def deleteWindowFromAttic(self, item, itemIndex): messenger.send('wakeup') self.furnitureManager.deleteWindowFromAttic(item, itemIndex, self.__deleteWindowFromAtticCallback) def __deleteWindowFromAtticCallback(self, retcode, item, itemIndex): self.__enableItemButtons(1) if retcode < 0: self.notify.info('Unable to delete window %s, reason %s.' % (itemIndex, retcode)) return self.atticWindowPanels[itemIndex].destroy() del self.atticWindowPanels[itemIndex] for i in range(itemIndex, len(self.atticWindowPanels)): self.atticWindowPanels[i].itemId -= 1 self.regenerateAtticPicker() def setGridSpacingString(self, spacingStr): spacing = eval(spacingStr) self.setGridSpacing(spacing) def setGridSpacing(self, gridSpacing): self.gridSpacing = gridSpacing def makeHouseExtentsBox(self): houseGeom = self.targetNodePath.findAllMatches('**/group*') targetBounds = houseGeom.getTightBounds() self.houseExtents = self.targetNodePath.attachNewNode('furnitureCollisionNode') mx = targetBounds[0][0] Mx = targetBounds[1][0] my = targetBounds[0][1] My = targetBounds[1][1] mz = targetBounds[0][2] Mz = targetBounds[1][2] cn = CollisionNode('extentsCollisionNode') cn.setIntoCollideMask(ToontownGlobals.GhostBitmask) self.houseExtents.attachNewNode(cn) cp = CollisionPolygon(Point3(mx, my, mz), Point3(mx, My, mz), Point3(mx, My, Mz), Point3(mx, my, Mz)) cn.addSolid(cp) cp = CollisionPolygon(Point3(Mx, My, mz), Point3(Mx, my, mz), Point3(Mx, my, Mz), Point3(Mx, My, Mz)) cn.addSolid(cp) cp = CollisionPolygon(Point3(Mx, my, mz), Point3(mx, my, mz), Point3(mx, my, Mz), Point3(Mx, my, Mz)) cn.addSolid(cp) cp = CollisionPolygon(Point3(mx, My, mz), Point3(Mx, My, mz), Point3(Mx, My, Mz), Point3(mx, My, Mz)) cn.addSolid(cp) def makeDoorBlocker(self): self.doorBlocker = self.targetNodePath.attachNewNode('doorBlocker') cn = CollisionNode('doorBlockerCollisionNode') cn.setIntoCollideMask(ToontownGlobals.FurnitureSideBitmask) self.doorBlocker.attachNewNode(cn) cs = CollisionSphere(Point3(-12, -33, 0), 7.5) cn.addSolid(cs) def createVerifyDialog(self, item, verifyText, okFunc, cancelFunc): if self.verifyFrame == None: buttons = loader.loadModel('phase_3/models/gui/dialog_box_buttons_gui') okButtonImage = (buttons.find('**/ChtBx_OKBtn_UP'), buttons.find('**/ChtBx_OKBtn_DN'), buttons.find('**/ChtBx_OKBtn_Rllvr')) cancelButtonImage = (buttons.find('**/CloseBtn_UP'), buttons.find('**/CloseBtn_DN'), buttons.find('**/CloseBtn_Rllvr')) self.verifyFrame = DirectFrame(pos=(-0.4, 0.1, 0.3), scale=0.75, relief=None, image=DGG.getDefaultDialogGeom(), image_color=ToontownGlobals.GlobalDialogColor, image_scale=(1.2, 1, 1.3), text='', text_wordwrap=19, text_scale=0.06, text_pos=(0, 0.5), textMayChange=1, sortOrder=DGG.NO_FADE_SORT_INDEX) self.okButton = DirectButton(parent=self.verifyFrame, image=okButtonImage, relief=None, text=OTPLocalizer.DialogOK, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(-0.22, 0.0, -0.5)) self.cancelButton = DirectButton(parent=self.verifyFrame, image=cancelButtonImage, relief=None, text=OTPLocalizer.DialogCancel, text_scale=0.05, text_pos=(0.0, -0.1), textMayChange=0, pos=(0.22, 0.0, -0.5)) self.deleteItemText = DirectLabel(parent=self.verifyFrame, relief=None, text='', text_wordwrap=16, pos=(0.0, 0.0, -0.4), scale=0.09) self.verifyFrame['text'] = verifyText self.deleteItemText['text'] = item.getName() self.okButton['command'] = okFunc self.cancelButton['command'] = cancelFunc self.verifyFrame.show() self.itemPanel, self.itemIval = item.getPicture(base.localAvatar) if self.itemPanel: self.itemPanel.reparentTo(self.verifyFrame, -1) self.itemPanel.setPos(0, 0, 0.05) self.itemPanel.setScale(0.35) self.deleteItemText.setPos(0.0, 0.0, -0.4) else: self.deleteItemText.setPos(0, 0, 0.07) if self.itemIval: self.itemIval.loop() return def __handleVerifyDeleteOK(self): if base.config.GetBool('want-qa-regression', 0): self.notify.info('QA-REGRESSION: ESTATE: Send Item to Trash') deleteFunction = self.verifyItems[0] deleteFunctionArgs = self.verifyItems[1:] self.__cleanupVerifyDelete() deleteFunction(*deleteFunctionArgs) def __cleanupVerifyDelete(self, *args): if self.nonDeletableItem: self.nonDeletableItem.cleanup() self.nonDeletableItem = None if self.verifyFrame: self.verifyFrame.hide() if self.itemIval: self.itemIval.finish() self.itemIval = None if self.itemPanel: self.itemPanel.destroy() self.itemPanel = None self.verifyItems = None return def __enableItemButtons(self, enabled): self.notify.debug('__enableItemButtons %d' % enabled) if enabled: buttonState = DGG.NORMAL else: buttonState = DGG.DISABLED if hasattr(self, 'inAtticButton'): self.inAtticButton['state'] = buttonState if hasattr(self, 'inRoomButton'): self.inRoomButton['state'] = buttonState if hasattr(self, 'inTrashButton'): self.inTrashButton['state'] = buttonState pickers = [self.atticPicker, self.inRoomPicker, self.inTrashPicker] for picker in pickers: if picker: for panel in picker['items']: if not panel.isEmpty(): panel.enable(enabled) def __resetAndCleanup(self, *args): self.__enableItemButtons(1) self.__cleanupVerifyDelete() def requestDelete(self, item, itemIndex, deleteFunction): self.__cleanupVerifyDelete() if self.furnitureManager.ownerId != base.localAvatar.doId or not item.isDeletable(): self.warnNonDeletableItem(item) return self.createVerifyDialog(item, TTLocalizer.HDDeleteItem, self.__handleVerifyDeleteOK, self.__resetAndCleanup) self.verifyItems = (deleteFunction, item, itemIndex) def requestRoomDelete(self, dfitem, objectId, itemIndex): self.__cleanupVerifyDelete() item = dfitem.item if self.furnitureManager.ownerId != base.localAvatar.doId or not item.isDeletable(): self.warnNonDeletableItem(item) return self.createVerifyDialog(item, TTLocalizer.HDDeleteItem, self.__handleVerifyDeleteOK, self.__resetAndCleanup) self.verifyItems = (self.deleteItemFromRoom, dfitem, objectId, itemIndex) def warnNonDeletableItem(self, item): message = TTLocalizer.HDNonDeletableItem if not item.isDeletable(): if item.getFlags() & CatalogFurnitureItem.FLBank: message = TTLocalizer.HDNonDeletableBank elif item.getFlags() & CatalogFurnitureItem.FLCloset: message = TTLocalizer.HDNonDeletableCloset elif item.getFlags() & CatalogFurnitureItem.FLPhone: message = TTLocalizer.HDNonDeletablePhone elif item.getFlags() & CatalogFurnitureItem.FLTrunk: message = TTLocalizer.HDNonDeletableTrunk if self.furnitureManager.ownerId != base.localAvatar.doId: message = TTLocalizer.HDNonDeletableNotOwner % self.furnitureManager.ownerName self.nonDeletableItem = TTDialog.TTDialog(text=message, style=TTDialog.Acknowledge, fadeScreen=0, command=self.__resetAndCleanup) self.nonDeletableItem.show() def requestReturnToAttic(self, item, objectId): self.__cleanupVerifyDelete() itemIndex = None for i in range(len(self.inRoomPanels)): if self.inRoomPanels[i].itemId == objectId: itemIndex = i self.__enableItemButtons(0) break if self.deleteMode: dfitem = self.objectDict[objectId].dfitem self.requestRoomDelete(dfitem, objectId, itemIndex) return self.createVerifyDialog(item, TTLocalizer.HDReturnVerify, self.__handleVerifyReturnOK, self.__resetAndCleanup) self.verifyItems = (item, objectId) return def __handleVerifyReturnOK(self): item, objectId = self.verifyItems self.__cleanupVerifyDelete() self.pickInRoom(objectId) self.sendItemToAttic() def requestReturnToAtticFromTrash(self, item, itemIndex): self.__cleanupVerifyDelete() self.__enableItemButtons(0) self.createVerifyDialog(item, TTLocalizer.HDReturnFromTrashVerify, self.__handleVerifyReturnFromTrashOK, self.__resetAndCleanup) self.verifyItems = (item, itemIndex) def __handleVerifyReturnFromTrashOK(self): if base.config.GetBool('want-qa-regression', 0): self.notify.info('QA-REGRESSION: ESTATE: Send Item to Attic') item, itemIndex = self.verifyItems self.__cleanupVerifyDelete() self.recoverDeletedItem(item, itemIndex) def recoverDeletedItem(self, item, itemIndex): messenger.send('wakeup') self.furnitureManager.recoverDeletedItem(item, itemIndex, self.__recoverDeletedItemCallback) def __recoverDeletedItemCallback(self, retcode, item, itemIndex): self.__cleanupVerifyDelete() if retcode < 0: if retcode == ToontownGlobals.FM_HouseFull: self.showHouseFullDialog() self.notify.info('Unable to recover deleted item %s, reason %s.' % (itemIndex, retcode)) return self.__enableItemButtons(1) self.inTrashPanels[itemIndex].destroy() del self.inTrashPanels[itemIndex] for i in range(itemIndex, len(self.inTrashPanels)): self.inTrashPanels[i].itemId -= 1 self.regenerateInTrashPicker() itemType = item.getTypeCode() if itemType == CatalogItemTypes.WALLPAPER_ITEM or itemType == CatalogItemTypes.FLOORING_ITEM or itemType == CatalogItemTypes.MOULDING_ITEM or itemType == CatalogItemTypes.WAINSCOTING_ITEM: itemIndex = len(self.atticWallpaperPanels) bringCommand = self.bringWallpaperFromAttic elif itemType == CatalogItemTypes.WINDOW_ITEM: itemIndex = len(self.atticWindowPanels) bringCommand = self.bringWindowFromAttic else: itemIndex = len(self.atticItemPanels) bringCommand = self.bringItemFromAttic panel = FurnitureItemPanel(item, itemIndex, command=bringCommand, deleteMode=self.deleteMode, helpCategory='FurnitureItemPanelAttic') if itemType == CatalogItemTypes.WALLPAPER_ITEM or itemType == CatalogItemTypes.FLOORING_ITEM or itemType == CatalogItemTypes.MOULDING_ITEM or itemType == CatalogItemTypes.WAINSCOTING_ITEM: self.atticWallpaperPanels.append(panel) elif itemType == CatalogItemTypes.WINDOW_ITEM: self.atticWindowPanels.append(panel) else: self.atticItemPanels.append(panel) self.regenerateAtticPicker() def showHouseFullDialog(self): self.cleanupDialog() self.dialog = TTDialog.TTDialog(style=TTDialog.Acknowledge, text=TTLocalizer.HDHouseFull, text_wordwrap=15, command=self.cleanupDialog) self.dialog.show() def bindHelpText(self, button, category): button.bind(DGG.ENTER, self.showHelpText, extraArgs=[category, None]) button.bind(DGG.EXIT, self.hideHelpText) return def showHelpText(self, category, itemName, xy): def showIt(task): helpText = TTLocalizer.HDHelpDict.get(category) if helpText: if itemName: helpText = helpText % itemName self.helpText['text'] = helpText self.helpText.show() else: print('category: %s not found') taskMgr.doMethodLater(0.75, showIt, 'showHelpTextDoLater') def hideHelpText(self, xy): taskMgr.remove('showHelpTextDoLater') self.helpText['text'] = '' self.helpText.hide()