from panda3d.core import Point3, Vec3, VBase3 from direct.tkwidgets.AppShell import * from direct.showbase.TkGlobal import * from direct.tkwidgets.Tree import * from direct.tkwidgets import Slider, Floater from tkinter.simpledialog import askstring from tkinter.messagebox import showwarning, askyesno from tkinter import * from direct.showbase.PythonUtil import Functor, list2dict from direct.gui.DirectGui import DGG import tkinter.filedialog from direct.showbase import DirectObject import math import operator from direct.tkwidgets import Valuator from direct.tkwidgets import VectorWidgets from otp.level import LevelConstants from direct.directtools import DirectSession import Pmw class InGameEditor(AppShell): appname = 'In-Game Editor' frameWidth = 900 frameHeight = 475 usecommandarea = 1 usestatusarea = 1 contactname = 'Darren Ranalli' contactphone = '(818) 623-3904' contactemail = 'darren.ranalli@disney.com' WantUndo = False def __init__(self, level, doneEvent, requestSaveEvent, saveAsEvent, undoEvent, redoEvent, wireframeEvent, oobeEvent, csEvent, runEvent, texEvent, **kw): DGG.INITOPT = Pmw.INITOPT optiondefs = (('title', 'In-Game ' + level.getName() + ' Editor', None),) self.defineoptions(kw, optiondefs) self.level = level self.doneEvent = doneEvent self.requestSaveEvent = requestSaveEvent self.saveAsEvent = saveAsEvent self.undoEvent = undoEvent self.redoEvent = redoEvent self.wireframeEvent = wireframeEvent self.oobeEvent = oobeEvent self.csEvent = csEvent self.runEvent = runEvent self.texEvent = texEvent self.entityCopy = None self.curEntId = None self.curEntWidgets = {} self.visZonesEditor = None AppShell.__init__(self) self.initialiseoptions(self.__class__) self.accept(self.level.getAttribChangeEventName(), self.handleAttribChange) self.accept('DIRECT_selectedNodePath', self.selectedNodePathHook) self.accept('DIRECT_manipulateObjectCleanup', self.manipCleanupHook) self.accept('DIRECT_undo', self.manipCleanupHook) self.accept('DIRECT_redo', self.manipCleanupHook) return def getEventMsgName(self, event): return 'InGameEditor%s_%s' % (self.level.getLevelId(), event) def getEntityName(self, entId): return self.level.levelSpec.getEntitySpec(entId)['name'] def appInit(self): from direct.directtools import DirectSession direct.enableLight() direct.disable() bboard.post(DirectSession.DirectSession.DIRECTdisablePost) def createMenuBar(self): menuBar = self.menuBar menuBar.addmenuitem('File', 'command', 'Save the level spec on the AI', label='Save on AI', command=self.handleRequestSave) menuBar.addmenuitem('File', 'command', 'Save the level spec locally', label='Save Locally As...', command=self.handleSaveAs) menuBar.addmenuitem('File', 'separator') menuBar.addmenuitem('File', 'command', 'Export a single entity', label='Export Entity...', command=self.level.handleExportEntity) menuBar.addmenuitem('File', 'command', 'Export a tree of entities', label='Export Entity Tree...', command=self.level.handleExportEntityTree) menuBar.addmenuitem('File', 'separator') menuBar.addmenuitem('File', 'command', 'Import a tree of entities', label='Import Entities...', command=self.level.handleImportEntities) menuBar.addmenuitem('File', 'separator') if InGameEditor.WantUndo: menuBar.addmenu('Edit', 'Edit Operations') menuBar.addmenuitem('Edit', 'command', 'Undo the last edit', label='Undo', command=self.doUndo) menuBar.addmenuitem('Edit', 'command', 'Redo the last undo', label='Redo', command=self.doRedo) menuBar.addmenu('Entity', 'Entity Operations') menuBar.addmenuitem('Entity', 'command', 'Duplicate Selected Entity', label='Duplicate Selected Entity', command=self.level.duplicateSelectedEntity) menuBar.addmenuitem('Entity', 'separator') permanentTypes = self.level.entTypeReg.getPermanentTypeNames() entTypes = list(self.level.entTypes) for permanentType in permanentTypes: entTypes.remove(permanentType) entTypes.sort() numEntities = len(entTypes) cascadeMenu = '' for index in range(numEntities): type = entTypes[index] if index % 10 == 0: lastIndex = min(index + 9, numEntities - 1) cascadeMenu = '%s->%s' % (entTypes[index], entTypes[lastIndex]) menuBar.addcascademenu('Entity', cascadeMenu) def doInsert(self = self, type = type): self.level.insertEntity(type) self.explorer.update(fUseCachedChildren=0) menuBar.addmenuitem(cascadeMenu, 'command', 'Insert %s' % type, label='Insert %s' % type, command=doInsert) menuBar.addmenuitem('Entity', 'separator') menuBar.addmenuitem('Entity', 'command', 'Remove Selected Entity', label='Remove Selected Entity', command=self.removeSelectedEntity) menuBar.addmenuitem('Entity', 'command', 'Remove Selected EntityTree', label='Remove Selected Entity Tree', command=self.removeSelectedEntityTree) menuBar.addmenuitem('Entity', 'separator') menuBar.addmenuitem('Entity', 'command', 'Go To Selected Entity', label='Go To Selected Entity', command=self.level.moveAvToSelected) menuBar.addmenuitem('Entity', 'command', 'Refresh Entity Explorer', label='Refresh Entity Explorer', command=self.refreshExplorer) self.menuBar.addmenu('DIRECT', 'Direct Session Panel Operations') self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Enabled', label='Enable', variable=direct.panel.directEnabled, command=direct.panel.toggleDirect) self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Grid Enabled', label='Enable Grid', variable=direct.panel.directGridEnabled, command=direct.panel.toggleDirectGrid) self.menuBar.addmenuitem('DIRECT', 'command', 'Toggle Object Handles Visability', label='Toggle Widget Viz', command=direct.toggleWidgetVis) self.menuBar.addmenuitem('DIRECT', 'command', 'Toggle Widget Move/COA Mode', label='Toggle Widget Mode', command=direct.manipulationControl.toggleObjectHandlesMode) self.menuBar.addmenuitem('DIRECT', 'checkbutton', 'DIRECT Widget On Top', label='Widget On Top', variable=direct.panel.directWidgetOnTop, command=direct.panel.toggleWidgetOnTop) self.menuBar.addmenuitem('DIRECT', 'command', 'Deselect All', label='Deselect All', command=direct.deselectAll) if InGameEditor.WantUndo: undoFrame = Frame(self.menuFrame) self.redoButton = Button(undoFrame, text='Redo', command=self.doRedo) self.redoButton.pack(side=RIGHT, expand=0) self.undoButton = Button(undoFrame, text='Undo', command=self.doUndo) self.undoButton.pack(side=RIGHT, expand=0) undoFrame.pack(side=RIGHT, expand=0) toggleFrame = Frame(self.menuFrame) self.wireframeButton = Button(toggleFrame, text='Wire', command=self.doWireframe) self.wireframeButton.pack(side=RIGHT, expand=0) self.texButton = Button(toggleFrame, text='Tex', command=self.doTex) self.texButton.pack(side=RIGHT, expand=0) self.csButton = Button(toggleFrame, text='Cs', command=self.doCs) self.csButton.pack(side=RIGHT, expand=0) self.runButton = Button(toggleFrame, text='Run', command=self.doRun) self.runButton.pack(side=RIGHT, expand=0) self.oobeButton = Button(toggleFrame, text='Oobe', command=self.doOobe) self.oobeButton.pack(side=RIGHT, expand=0) toggleFrame.pack(side=RIGHT, expand=0, padx=5) AppShell.createMenuBar(self) def createInterface(self): interior = self.interior() mainFrame = Frame(interior) self.framePane = Pmw.PanedWidget(mainFrame, orient=DGG.HORIZONTAL) self.explorerFrame = self.framePane.add('left', min=250) self.widgetFrame = self.framePane.add('right', min=300) self.explorer = LevelExplorer(parent=self.explorerFrame, editor=self) self.explorer.pack(fill=BOTH, expand=1) self.explorerFrame.pack(fill=BOTH, expand=1) self.pageOneFrame = Pmw.ScrolledFrame(self.widgetFrame, borderframe_relief=GROOVE, horizflex='elastic') self.pageOneFrame.pack(expand=1, fill=BOTH) self.widgetFrame.pack(fill=BOTH, expand=1) self.framePane.pack(fill=BOTH, expand=1) mainFrame.pack(fill=BOTH, expand=1) self.createButtons() self.initialiseoptions(self.__class__) self.attribWidgets = [] def selectedNodePathHook(self, nodePath): np = nodePath.findNetTag('entity') if not np.isEmpty(): if np.id() != nodePath.id(): np.select() else: self.findEntityFromNP(np) def findEntityFromNP(self, nodePath): entId = self.level.nodePathId2EntId.get(nodePath.id()) if entId: self.selectEntity(entId) else: for entId in self.level.levelSpec.getAllEntIds(): np = self.level.getEntInstanceNP(entId) if np: if np.id() == nodePath.id(): self.selectEntity(entId) return def manipCleanupHook(self, nodePathList): if not nodePathList: return entId = self.level.nodePathId2EntId.get(nodePathList[0].id()) if entId: t = nodePathList[0].getTransform() entSpec = self.level.levelSpec.getEntitySpec(entId) entPos = entSpec.get('pos') if entPos and t.getPos() != entPos: self.level.setAttribEdit(entId, 'pos', Point3(t.getPos())) entHpr = entSpec.get('hpr') if entHpr and t.getHpr() != entHpr: self.level.setAttribEdit(entId, 'hpr', Vec3(t.getHpr())) entScale = entSpec.get('scale') if entScale and t.getScale() != entScale: self.level.setAttribEdit(entId, 'scale', Vec3(t.getScale())) def refreshExplorer(self): self.explorer.update() def selectEntity(self, entId): node = self.explorer._node.find(entId) if node: node.reveal() self.explorer._node.deselecttree() node.select() def removeSelectedEntity(self): if self.level.selectedEntity: parentEntId = self.level.selectedEntity.parentEntId else: parentEntId = None if self.level.removeSelectedEntity() != -1: self.explorer.update(fUseCachedChildren=0) if parentEntId: self.selectEntity(parentEntId) return def removeSelectedEntityTree(self): if self.level.selectedEntity: parentEntId = self.level.selectedEntity.parentEntId else: parentEntId = None if self.level.removeSelectedEntityTree() != -1: self.explorer.update(fUseCachedChildren=0) if parentEntId: self.selectEntity(parentEntId) return def clearAttribEditPane(self): for widg in self.attribWidgets: widg.destroy() if self.visZonesEditor: self.visZonesEditor.destroy() self.attribWidgets = [] self.curEntId = None self.curEntWidgets = {} self.cbDict = {} return def updateEntityCopy(self, entId): if self.entityCopy == None: self.entityCopy = self.level.getEntInstanceNPCopy(entId) if self.entityCopy is not None: self.entityCopy.setRenderModeWireframe() self.entityCopy.setTextureOff(1) self.entityCopy.setColor(1, 0, 0) return def updateAttribEditPane(self, entId, levelSpec, entTypeReg): self.clearAttribEditPane() self.curEntId = entId widgetSetter = None entSpec = levelSpec.getEntitySpec(entId) typeDesc = entTypeReg.getTypeDesc(entSpec['type']) attribNames = typeDesc.getAttribNames() attribDescs = typeDesc.getAttribDescDict() for attribName in attribNames: desc = attribDescs[attribName] params = desc.getParams() datatype = desc.getDatatype() if datatype == 'int': self.addIntWidget(levelSpec, entSpec, entId, attribName, params) elif datatype == 'float': self.addFloatWidget(levelSpec, entSpec, entId, attribName, params) elif datatype == 'bool': self.addBoolWidget(levelSpec, entSpec, entId, attribName, params) elif datatype == 'choice': self.addChoiceWidget(levelSpec, entSpec, entId, attribName, params) elif datatype == 'multiChoice': self.addMultiChoiceWidget(levelSpec, entSpec, entId, attribName, params) elif datatype in ['pos', 'hpr', 'scale']: self.addVec3Widget(levelSpec, entSpec, entId, attribName, params, datatype) elif datatype == 'color': self.addColorWidget(levelSpec, entSpec, entId, attribName, params) elif datatype == 'bamfilename': self.addFileWidget(levelSpec, entSpec, entId, attribName, params) elif datatype == 'visZoneList': self.addVisZoneWidget(levelSpec, entSpec, entId, attribName, params) elif datatype == 'entId': self.addEntIdWidget(levelSpec, entSpec, entId, attribName, params, entTypeReg) elif datatype == 'string': self.addStringWidget(levelSpec, entSpec, entId, attribName, params) elif datatype == 'const': self.addConstWidget(levelSpec, entSpec, entId, attribName, params) else: self.addPythonWidget(levelSpec, entSpec, entId, attribName, params) return def addIntWidget(self, levelSpec, entSpec, entId, attribName, params): minVal = params.get('min', None) maxVal = params.get('max', None) if minVal is not None and maxVal is not None: widgClass = Slider.Slider else: widgClass = Floater.Floater widg = widgClass(self.pageOneFrame.interior(), text=attribName, value=entSpec[attribName], numDigits=0, label_width=15, label_anchor=W, label_justify=LEFT, label_font=None, min=minVal, max=maxVal, resolution=1.0) def clientIntCommand(val): entity = self.level.getEntInstance(entId) if entity: entity.handleAttribChange(attribName, int(val)) def finalIntCommand(): self.level.setAttribEdit(entId, attribName, int(widg.get())) widg['command'] = clientIntCommand widg['postCallback'] = finalIntCommand widg.pack(fill=X, expand=1) self.attribWidgets.append(widg) self.curEntWidgets[attribName] = lambda x: widg.set(x, 0) return def addFloatWidget(self, levelSpec, entSpec, entId, attribName, params): minVal = params.get('min', None) maxVal = params.get('max', None) widg = Floater.Floater(self.pageOneFrame.interior(), text=attribName, value=entSpec[attribName], label_width=15, label_anchor=W, label_justify=LEFT, label_font=None, min=minVal, max=maxVal) def clientFloatCommand(val): entity = self.level.getEntInstance(entId) if entity: entity.handleAttribChange(attribName, val) def finalFloatCommand(): self.level.setAttribEdit(entId, attribName, widg.get()) widg['command'] = clientFloatCommand widg['postCallback'] = finalFloatCommand widg.pack(fill=X, expand=1) self.attribWidgets.append(widg) self.curEntWidgets[attribName] = lambda x: widg.set(x, 0) return def addBoolWidget(self, levelSpec, entSpec, entId, attribName, params): flag = BooleanVar() flag.set(entSpec[attribName]) def booleanCommand(booleanVar = flag): self.level.setAttribEdit(entId, attribName, flag.get()) frame = Frame(self.pageOneFrame.interior()) label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) label.pack(side=LEFT, expand=0) trueButton = Radiobutton(frame, text='True', value=1, variable=flag, command=booleanCommand) trueButton.pack(side=LEFT, expand=0) falseButton = Radiobutton(frame, text='False', value=0, variable=flag, command=booleanCommand) falseButton.pack(side=LEFT, expand=0) frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) self.curEntWidgets[attribName] = flag.set def addChoiceWidget(self, levelSpec, entSpec, entId, attribName, params): if attribName in entSpec: attributeValue = entSpec.get(attribName) else: typeDesc = self.level.entTypeReg.getTypeDesc(entSpec['type']) attribDesc = typeDesc.getAttribDescDict()[attribName] attributeValue = attribDesc.getDefaultValue() valueDict = params.get('valueDict', {}) for key, value in list(valueDict.items()): if value == attributeValue: attributeValue = key break radioVar = StringVar() radioVar.set(attributeValue) def radioCommand(radioVar = radioVar): value = valueDict.get(radioVar.get(), radioVar.get()) self.level.setAttribEdit(entId, attribName, value) frame = Frame(self.pageOneFrame.interior(), relief=GROOVE, borderwidth=2) label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) label.pack(side=LEFT, expand=0) for choice in params.get('choiceSet', []): if type(choice) is str: choiceStr = choice else: choiceStr = repr(choice) if choiceStr not in valueDict: valueDict[choiceStr] = choice choiceButton = Radiobutton(frame, text=choiceStr, value=choiceStr, variable=radioVar, command=radioCommand) choiceButton.pack(side=LEFT, expand=0) frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) def setRadioVar(attributeValue): for key, value in list(valueDict.items()): if value == attributeValue: attributeValue = key break radioVar.set(attributeValue) self.curEntWidgets[attribName] = setRadioVar def addMultiChoiceWidget(self, levelSpec, entSpec, entId, attribName, params): frame = Frame(self.pageOneFrame.interior(), relief=GROOVE, borderwidth=2) label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) label.pack(side=LEFT, expand=0) valueDict = params.get('valueDict', {}) self.cbDict[attribName] = list2dict(entSpec[attribName], value=1) checkbuttonDict = {} base.cbButtons = [] base.checkbuttonDict = checkbuttonDict for choice in params.get('choiceSet', []): trueValue = valueDict.get(choice, choice) cbVar = IntVar() cbVar.set(trueValue in self.cbDict[attribName]) checkbuttonDict[trueValue] = cbVar def cbCommand(var, trueValue = trueValue): vd = self.cbDict[attribName] print(vd) if var.get(): print('got it', trueValue, vd) vd[trueValue] = 1 else: print('not it', trueValue, vd) if trueValue in vd: del vd[trueValue] value = list(vd.keys()) print('SENDING', value) self.level.setAttribEdit(entId, attribName, value) if type(choice) is str: labelStr = choice else: labelStr = repr(choice) func = Functor(cbCommand, cbVar) choiceButton = Checkbutton(frame, text=labelStr, variable=cbVar, command=lambda : func()) choiceButton.pack(side=LEFT, expand=0) base.cbButtons.append(choiceButton) frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) def setCheckbuttonVar(attributeValueList): print('COMING BACK', attributeValueList) for attributeValue, cb in list(checkbuttonDict.items()): if attributeValue in attributeValueList: cb.set(1) else: cb.set(0) self.curEntWidgets[attribName] = setCheckbuttonVar def addVec3Widget(self, levelSpec, entSpec, entId, attribName, params, datatype): def veCommand(poslist): self.level.setAttribEdit(entId, attribName, Point3(*poslist)) vec = entSpec[attribName] if not isinstance(vec, VBase3): vec = Vec3(vec) value = [vec[0], vec[1], vec[2]] minVal = maxVal = None if datatype == 'pos': floaterLabels = ['x', 'y', 'z'] floaterType = 'floater' elif datatype == 'hpr': floaterLabels = ['h', 'p', 'r'] floaterType = 'angledial' else: floaterLabels = ['sx', 'sy', 'sz'] floaterType = 'slider' minVal = 0 maxVal = 1000 widg = VectorWidgets.VectorEntry(self.pageOneFrame.interior(), text=attribName, value=value, type=floaterType, bd=0, relief=None, min=minVal, max=maxVal, label_justify=LEFT, label_anchor=W, label_width=14, label_bd=0, labelIpadx=0, floaterGroup_labels=floaterLabels) widg['command'] = veCommand widg.pack(fill=X, expand=1) if attribName in ('pos', 'hpr', 'scale'): def placeEntity(): selectedEntityNP = self.level.getEntInstanceNP(entId) if selectedEntityNP is not None: selectedEntityNP.place() return widg.menu.add_command(label='Place...', command=placeEntity) def adjustCopy(vec): self.updateEntityCopy(entId) if self.entityCopy is not None: if datatype == 'pos': self.entityCopy.setPos(vec[0], vec[1], vec[2]) elif datatype == 'hpr': self.entityCopy.setHpr(vec[0], vec[1], vec[2]) elif datatype == 'scale': self.entityCopy.setScale(vec[0], vec[1], vec[2]) widg.set(vec, 0) return widg._floaters['command'] = adjustCopy widg._floaters.component('valuatorGroup')['postCallback'] = lambda x, y, z: veCommand([x, y, z]) self.attribWidgets.append(widg) self.curEntWidgets[attribName] = lambda x: widg.set(x, 0) return def addColorWidget(self, levelSpec, entSpec, entId, attribName, params): def veCommand(colorlist): self.level.setAttribEdit(entId, attribName, Vec4(*colorlist) / 255.0) vec = entSpec[attribName] value = [vec[0] * 255.0, vec[1] * 255.0, vec[2] * 255.0, vec[3] * 255.0] floaterLabels = ['r', 'g', 'b', 'a'] widg = VectorWidgets.ColorEntry(self.pageOneFrame.interior(), text=attribName, type='slider', relief=None, bd=0, label_justify=LEFT, label_anchor=W, label_width=14, label_bd=0, labelIpadx=0, floaterGroup_labels=floaterLabels, value=value, floaterGroup_value=value) widg['command'] = veCommand widg.pack(fill=X, expand=1) def adjustEntity(vec): entity = self.level.getEntInstance(entId) if entity is not None: entity.setColor(vec[0] / 255.0, vec[1] / 255.0, vec[2] / 255.0, vec[3] / 255.0) widg.set(vec, 0) return widg._floaters['command'] = adjustEntity widg._floaters.component('valuatorGroup')['postCallback'] = lambda x, y, z, a: veCommand([x, y, z, a]) self.attribWidgets.append(widg) self.curEntWidgets[attribName] = lambda x: widg.set(x * 255.0, 0) return def addFileWidget(self, levelSpec, entSpec, entId, attribName, params): text = StringVar() text.set(repr(entSpec[attribName])) def handleReturn(event): self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec) def askFilename(callback = handleReturn): if text.get() == 'None': initialDir = Filename.expandFrom('$TTMODELS/built/').toOsSpecific() else: initialDir = Filename.expandFrom('$TTMODELS/built/%s' % text.get()[1:-1]).toOsSpecific() print(text, text.get()[1:-1], initialDir) rawFilename = askopenfilename(defaultextension='*', initialdir=initialDir, filetypes=(('Bam Files', '*.bam'), ('Egg Files', '*.egg'), ('Maya Binaries', '*.mb'), ('All files', '*')), title='Load Model File', parent=self.interior()) if rawFilename != '': filename = Filename.fromOsSpecific(rawFilename) filename.findOnSearchpath(getModelPath().getValue()) text.set("'%s'" % repr(filename)) handleReturn(None) return frame = Frame(self.pageOneFrame.interior()) label = Button(frame, text=attribName, width=14, borderwidth=0, anchor=W, justify=LEFT) label['command'] = askFilename label.pack(side=LEFT, expand=0) def enterWidget(event, widg = label): widg['background'] = '#909090' def leaveWidget(event, widg = label): widg['background'] = 'SystemButtonFace' label.bind('', enterWidget) label.bind('', leaveWidget) widg = Entry(frame, textvariable=text) widg.bind('', handleReturn) widg.pack(side=LEFT, fill=X, expand=1) frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) self.curEntWidgets[attribName] = lambda x: text.set(repr(x)) def addVisZoneWidget(self, levelSpec, entSpec, entId, attribName, params): text = StringVar() text.set(repr(entSpec[attribName])) def handleReturn(event): self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec) def handleUpdate(visZoneList): self.level.setAttribEdit(entId, attribName, visZoneList) def getZoneList(callback = handleReturn): zoneEntIds = list(self.level.entType2ids['zone']) zoneEntIds.remove(LevelConstants.UberZoneEntId) zoneEntIds.sort() self.visZonesEditor = LevelVisZonesEditor(self, entId, entSpec[attribName], modelZones=zoneEntIds, updateCommand=handleUpdate) frame = Frame(self.pageOneFrame.interior()) label = Button(frame, text=attribName, width=14, borderwidth=0, anchor=W, justify=LEFT) label['command'] = getZoneList label.pack(side=LEFT, expand=0) def enterWidget(event, widg = label): widg['background'] = '#909090' def leaveWidget(event, widg = label): widg['background'] = 'SystemButtonFace' label.bind('', enterWidget) label.bind('', leaveWidget) widg = Entry(frame, textvariable=text) widg.bind('', handleReturn) widg.pack(side=LEFT, fill=X, expand=1) frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) def refreshWidget(visZoneList): text.set(repr(visZoneList)) if self.visZonesEditor: self.visZonesEditor.setVisible(visZoneList) self.curEntWidgets[attribName] = refreshWidget def addEntIdWidget(self, levelSpec, entSpec, entId, attribName, params, entTypeReg): text = StringVar() text.set(repr(entSpec[attribName])) def handleReturn(event): self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec) def handleMenu(id): text.set(id) self.level.setAttribEdit(entId, attribName, id) frame = Frame(self.pageOneFrame.interior()) label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) label.pack(side=LEFT, expand=0) widg = Entry(frame, textvariable=text) widg.bind('', handleReturn) widg.pack(side=LEFT, fill=X, expand=1) if attribName is 'parentEntId': buttonText = 'Reparent To' else: buttonText = 'Select Entity' entButton = Menubutton(frame, width=8, text=buttonText, relief=RAISED, borderwidth=2) entMenu = Menu(entButton, tearoff=0) entButton['menu'] = entMenu entButton.pack(side=LEFT, fill='x', expand=1) entType = params.get('type') entOutput = params.get('output') idDict = {} if entType == 'grid': for eType in entTypeReg.getDerivedTypeNames('grid'): idDict[eType] = self.level.entType2ids.get(eType, []) elif entType == 'nodepath': for eType in entTypeReg.getDerivedTypeNames('nodepath'): idDict[eType] = self.level.entType2ids.get(eType, []) elif entOutput == 'bool': for eType in entTypeReg.getTypeNamesFromOutputType('bool'): idDict[eType] = self.level.entType2ids.get(eType, []) else: for eType in list(self.level.entType2ids.keys()): idDict[eType] = self.level.entType2ids.get(eType, []) typeKeys = list(idDict.keys()) typeKeys.sort() def getChildEntIds(entity): entIds = [] for child in entity.getChildren(): entIds.append(child.entId) entIds.extend(getChildEntIds(child)) return entIds thisEntity = self.level.getEntity(entId) forbiddenEntIds = [entId, thisEntity.parentEntId] forbiddenEntIds.extend(getChildEntIds(thisEntity)) for eType in typeKeys: idList = list(idDict[eType]) for forbiddenId in forbiddenEntIds: if forbiddenId in idList: idList.remove(forbiddenId) if len(idList) == 0: continue subMenu = Menu(entMenu, tearoff=0) entMenu.add_cascade(label=eType, menu=subMenu) idList.sort() numIds = len(idList) idIndex = 0 for id in idList: if idIndex % 10 == 0: if numIds > 10: m = Menu(subMenu, tearoff=0) firstId = idList[idIndex] lastIndex = min(idIndex + 9, numIds - 1) lastId = idList[lastIndex] subMenu.add_cascade(label='Ids %d-%d' % (firstId, lastId), menu=m) else: m = subMenu m.add_command(label='%d: %s' % (id, self.getEntityName(id)), command=lambda id = id, h = handleMenu: h(id)) idIndex += 1 frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) self.curEntWidgets[attribName] = text.set def addStringWidget(self, levelSpec, entSpec, entId, attribName, params): text = StringVar() text.set(str(entSpec[attribName])) def handleReturn(event): self.level.setAttribEdit(entId, attribName, text.get()) frame = Frame(self.pageOneFrame.interior()) label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) label.pack(side=LEFT, expand=0) widg = Entry(frame, textvariable=text) widg.bind('', handleReturn) widg.pack(side=LEFT, fill=X, expand=1) frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) self.curEntWidgets[attribName] = text.set def addConstWidget(self, levelSpec, entSpec, entId, attribName, params): frame = Frame(self.pageOneFrame.interior()) label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) label.pack(side=LEFT, expand=0) text = str(entSpec[attribName]) valueLabel = Label(frame, text=text, anchor=W, justify=LEFT, relief=GROOVE) valueLabel.pack(side=LEFT, fill=X, expand=1) frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) def addPythonWidget(self, levelSpec, entSpec, entId, attribName, params): text = StringVar() text.set(repr(entSpec[attribName])) def handleReturn(event): self.handleAttributeChangeSubmit(attribName, text, entId, levelSpec) frame = Frame(self.pageOneFrame.interior()) label = Label(frame, text=attribName, width=15, anchor=W, justify=LEFT) label.pack(side=LEFT, expand=0) widg = Entry(frame, textvariable=text) widg.bind('', handleReturn) widg.pack(side=LEFT, fill=X, expand=1) frame.pack(fill=X, expand=1) self.attribWidgets.append(frame) self.curEntWidgets[attribName] = lambda x: text.set(repr(x)) def handleAttributeChangeSubmit(self, attribName, textObj, entId, levelSpec): newText = textObj.get() try: value = eval(newText) except: showwarning('ERROR', 'that is not a valid Python object', parent=self.parent) return self.level.setAttribEdit(entId, attribName, value) def handleAttribChange(self, entId, attribName, value, username): if username == self.level.editUsername: if self.entityCopy: self.entityCopy.removeNode() self.entityCopy = None if entId == self.curEntId: widgetSetter = self.curEntWidgets[attribName] if widgetSetter is not None: widgetSetter(value) return def createButtons(self): pass def toggleBalloon(self): if self.toggleBalloonVar.get(): self.balloon().configure(state='both') else: self.balloon().configure(state='none') def onDestroy(self, event): from direct.directtools import DirectSession direct.disable() bboard.remove(DirectSession.DirectSession.DIRECTdisablePost) self.ignore(self.level.getAttribChangeEventName()) self.ignore('DIRECT_selectedNodePath') self.ignore('DIRECT_manipulateObjectCleanup') self.ignore('DIRECT_undo') self.ignore('DIRECT_redo') print('InGameEditor.onDestroy()') if self.visZonesEditor: self.visZonesEditor.destroy() self.explorer._node.destroy() del self.level messenger.send(self.doneEvent) def handleRequestSave(self): messenger.send(self.requestSaveEvent) def handleSaveAs(self): filename = tkinter.filedialog.asksaveasfilename(parent=self.parent, defaultextension='.py', filetypes=[('Python Source Files', '.py'), ('All Files', '*')]) if len(filename) > 0: messenger.send(self.saveAsEvent, [filename]) def doUndo(self): messenger.send(self.undoEvent) def doRedo(self): messenger.send(self.redoEvent) def doWireframe(self): messenger.send(self.wireframeEvent) def doOobe(self): messenger.send(self.oobeEvent) def doCs(self): messenger.send(self.csEvent) def doRun(self): messenger.send(self.runEvent) def doTex(self): messenger.send(self.texEvent) def resetLevel(self): self.showTodo(what='resetLevel') def showTodo(self, what = ''): self.showWarning('%s\nThis is not yet implemented.' % what, 'TODO') def showWarning(self, msg, title = 'Warning'): showwarning(title, msg, parent=self.parent) def askYesNo(self, msg, title = 'Query'): return askyesno(title, msg, parent=self.parent) def popupLevelDialog(self): data = askstring('Input Level Data', 'Level Data:', parent=self) if data: self.messageBar().helpmessage(data) class LevelVisZonesEditor(Pmw.MegaToplevel): def __init__(self, editor, entId, visible, modelZones = [], updateCommand = None, parent = None, **kw): DGG.INITOPT = Pmw_INITOPT optiondefs = (('title', 'Level Vis-Zone Editor', None),) self.defineoptions(kw, optiondefs) Pmw.MegaToplevel.__init__(self, parent, title=self['title']) self.editor = editor self.entId = entId self.modelZones = modelZones self.modelZones.sort() self.updateCommand = updateCommand hull = self.component('hull') balloon = self.balloon = Pmw.Balloon(hull) self.balloon.configure(state='none') menuFrame = Frame(hull, relief=GROOVE, bd=2) menuFrame.pack(fill=X, expand=1) menuBar = Pmw.MenuBar(menuFrame, hotkeys=1, balloon=balloon) menuBar.pack(side=LEFT, expand=1, fill=X) menuBar.addmenu('Vis Zones Editor', 'Visability Zones Editor Operations') menuBar.addmenuitem('Vis Zones Editor', 'command', 'Exit Visability Zones Editor', label='Exit', command=self.destroy) menuBar.addmenu('Help', 'Visibility Zones Editor Help Operations') self.toggleBalloonVar = IntVar() self.toggleBalloonVar.set(0) menuBar.addmenuitem('Help', 'checkbutton', 'Toggle balloon help', label='Balloon Help', variable=self.toggleBalloonVar, command=self.toggleBalloon) sf = Pmw.ScrolledFrame(hull, horizflex='elastic', usehullsize=1, hull_width=200, hull_height=400) frame = sf.interior() sf.pack(padx=5, pady=3, fill=BOTH, expand=1) self.radioSelect = Pmw.RadioSelect(frame, selectmode=MULTIPLE, orient=DGG.VERTICAL, pady=0, command=self.toggleVisZone) self.buttons = [] for modelZoneNum in self.modelZones: buttonStr = '%d - %s' % (modelZoneNum, self.editor.getEntityName(modelZoneNum)) button = self.radioSelect.add(buttonStr, width=12) button.configure(anchor=W, justify=LEFT) self.buttons.append(button) self.radioSelect.pack(expand=1, fill=X) sf.reposition() buttonFrame = Frame(hull) self.showMode = IntVar() self.showMode.set(self.editor.level.level.getColorZones()) self.showActiveButton = Radiobutton(buttonFrame, text='Stash Invisible', value=0, indicatoron=1, variable=self.showMode, command=self.setVisRenderMode) self.showActiveButton.pack(side=LEFT, fill=X, expand=1) self.showAllButton = Radiobutton(buttonFrame, text='Color Invisible', value=1, indicatoron=1, variable=self.showMode, command=self.setVisRenderMode) self.showAllButton.pack(side=LEFT, fill=X, expand=1) buttonFrame.pack(fill=X, expand=1) self.initialiseoptions(LevelVisZonesEditor) self.setVisible(visible) return None def toggleVisZone(self, zoneStr, state): zoneNum = int(zoneStr.split('-')[0]) if state == 0: if zoneNum in self.visible: self.visible.remove(zoneNum) elif zoneNum not in self.visible: self.visible.append(zoneNum) self.visible.sort() if self.updateCommand: self.updateCommand(self.visible) def setVisible(self, visible): self.visible = visible self.refreshVisibility() def setVisRenderMode(self): self.editor.level.level.setColorZones(self.showMode.get()) def refreshVisibility(self): self.radioSelect.selection = [] for button in self.buttons: componentName = button['text'] modelZone = int(componentName.split('-')[0]) if modelZone in self.visible: button.configure(relief='sunken') self.radioSelect.selection.append(componentName) else: button.configure(relief='raised') def destroy(self): self.editor.visZonesEditor = None Pmw.MegaToplevel.destroy(self) return def toggleBalloon(self): if self.toggleBalloonVar.get(): self.balloon.configure(state='balloon') else: self.balloon.configure(state='none') DEFAULT_MENU_ITEMS = ['Update Explorer', 'Separator', 'Select', 'Deselect', 'Separator', 'Delete', 'Separator', 'Fit', 'Flash', 'Isolate', 'Toggle Vis', 'Show All', 'Separator', 'Set Reparent Target', 'Reparent', 'WRT Reparent', 'Separator', 'Place', 'Set Name', 'Set Color', 'Explore', 'Separator'] class LevelExplorer(Pmw.MegaWidget, DirectObject.DirectObject): def __init__(self, parent = None, editor = None, **kw): optiondefs = (('menuItems', [], Pmw.INITOPT),) self.defineoptions(kw, optiondefs) Pmw.MegaWidget.__init__(self, parent) self.editor = editor interior = self.interior() interior.configure(relief=GROOVE, borderwidth=2) self._scrolledCanvas = self.createcomponent('scrolledCanvas', (), None, Pmw.ScrolledCanvas, (interior,), hull_width=300, hull_height=80, usehullsize=1) self._canvas = self._scrolledCanvas.component('canvas') self._canvas['scrollregion'] = ('0i', '0i', '2i', '4i') self._scrolledCanvas.resizescrollregion() self._scrolledCanvas.pack(padx=3, pady=3, expand=1, fill=BOTH) self._canvas.bind('', self.mouse2Down) self._canvas.bind('', self.mouse2Motion) self._canvas.bind('', lambda e, sc = self._scrolledCanvas: sc.resizescrollregion()) self.interior().bind('', self.onDestroy) self._treeItem = LevelExplorerItem(self.editor.level, self.editor) self._node = TreeNode(self._canvas, None, self._treeItem, DEFAULT_MENU_ITEMS + self['menuItems']) self._node.expand() self._parentFrame = Frame(interior) self.accept('Level_Update Explorer', lambda f, s = self: s.update()) self.initialiseoptions(LevelExplorer) return def update(self, fUseCachedChildren = 1): self._node.update(fUseCachedChildren) self._node.expand() def mouse2Down(self, event): self._width = 1.0 * self._canvas.winfo_width() self._height = 1.0 * self._canvas.winfo_height() xview = self._canvas.xview() yview = self._canvas.yview() self._left = xview[0] self._top = yview[0] self._dxview = xview[1] - xview[0] self._dyview = yview[1] - yview[0] self._2lx = event.x self._2ly = event.y def mouse2Motion(self, event): newx = self._left - (event.x - self._2lx) / self._width * self._dxview self._canvas.xview_moveto(newx) newy = self._top - (event.y - self._2ly) / self._height * self._dyview self._canvas.yview_moveto(newy) self._2lx = event.x self._2ly = event.y self._left = self._canvas.xview()[0] self._top = self._canvas.yview()[0] def onDestroy(self, event): self.ignore('Level_Update Explorer') class LevelExplorerItem(TreeItem): def __init__(self, levelElement, editor): self.levelElement = levelElement self.editor = editor def GetText(self): return self.levelElement.getName() def GetKey(self): return self.levelElement.id() def IsEditable(self): return 1 def SetText(self, text): self.levelElement.setNewName(text) def GetIconName(self): return 'sphere2' def IsExpandable(self): return self.levelElement.getNumChildren() != 0 def GetSubList(self): sublist = [] for element in self.levelElement.getChildren(): item = LevelExplorerItem(element, self.editor) sublist.append(item) return sublist def OnSelect(self): messenger.send(self.editor.getEventMsgName('Select'), [self.levelElement]) def MenuCommand(self, command): messenger.send(self.editor.getEventMsgName(command), [self.levelElement])