class ScenePortalManager(SceneEditorModule): name = 'scene_portal_manager' dependency = ['mock'] def onLoad(self): #UI self.container = self.requestDockWindow('ScenePortalManager', title='Scene Portals', size=(120, 120), minSize=(120, 120), dock='left', toolWindow=False) self.window = window = self.container.addWidgetFromFile( _getModulePath('container.ui')) #Components leftLayout = QtWidgets.QVBoxLayout(window.containerLeft) leftLayout.setSpacing(0) leftLayout.setContentsMargins(0, 0, 0, 0) rightLayout = QtWidgets.QVBoxLayout(window.containerRight) rightLayout.setSpacing(0) rightLayout.setContentsMargins(0, 0, 0, 0) self.treeGraphsFilter = GenericTreeFilter(window.containerRight) self.treeGraphsFilter.setSizePolicy(QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed) self.treeGraphs = ScenePortalGraphListWidget(window.containerLeft) self.treeGraphsFilter.setTargetTree(self.treeGraphs) self.treeGraphs.setIndentation(0) self.toolbarGraph = QtWidgets.QToolBar(window.containerLeft) self.toolbarGraph.setOrientation(Qt.Horizontal) self.toolbarGraph.setMaximumHeight(32) self.graphView = ScenePortalGraphView(parent=window.containerRight, use_gl=False, antialias=True) leftLayout.addWidget(self.toolbarGraph) leftLayout.addWidget(self.treeGraphsFilter) leftLayout.addWidget(self.treeGraphs) rightLayout.addWidget(self.graphView) self.addToolBar('scene_portal_graph', self.toolbarGraph) self.addTool('scene_portal_graph/refresh', label='Refresh', icon='refresh') self.addTool('scene_portal_graph/----') self.addTool('scene_portal_graph/add_graph', label='Add', icon='add') self.addTool('scene_portal_graph/remove_graph', label='Remove', icon='remove') self.addMenuItem('main/scene/rebuild_portal_registry', dict(label='Rebuild Portal Data')) self.addMenuItem('main/scene/portal_jump', dict(label='Portal Jump', shortcut='Ctrl+Alt+J')) signals.connect('selection.changed', self.onSelectionChanged) signals.connect('scene.change', self.onSceneChange) self.portalRegistry = None self.showAllScenePortals = False self.updatingSelection = False def onStart(self): signals.connect('asset.register', self.onAssetRegister) signals.connect('asset.unregister', self.onAssetUnregister) signals.connect('asset.modified', self.onAssetModified) self.loadRegistry() def onAppReady(self): self.treeGraphs.rebuild() def onStop(self): self.saveRegistry() def clearRegistry(self): self.portalRegistry = {} def loadRegistry(self): self.portalRegistry = JSONHelper.tryLoadJSON( self.getProject().getGameConfigPath(_GII_PORTAL_DATA_NAME), 'portal data') if not self.portalRegistry: self.portalRegistry = {} self.scanPortals() def saveRegistry(self): JSONHelper.trySaveJSON( self.portalRegistry, self.getProject().getGameConfigPath(_GII_PORTAL_DATA_NAME), 'portal data') reg = _MOCK.getScenePortalRegistry() reg.markDirty(reg) def scanPortals(self): assetLib = self.getAssetLibrary() for assetNode in assetLib.iterAssets(): if assetNode.getType() == 'scene': self.processScenePortal(assetNode) self.saveRegistry() def clearScenePortal(self, scenePath): registry = self.portalRegistry toRemove = [] for key, entry in list(registry.items()): if entry['scene'] == scenePath: toRemove.append(key) for key in toRemove: del registry[key] def processScenePortal(self, sceneNode): if sceneNode.getType() != 'scene': return self.clearScenePortal(sceneNode.getPath()) if sceneNode.hasTag('deprecated'): return registry = self.portalRegistry sceneFilePath = sceneNode.getFilePath() if os.path.isfile(sceneFilePath): sceneData = JSONHelper.tryLoadJSON(sceneFilePath) elif os.path.isdir(sceneFilePath): sceneData = JSONHelper.tryLoadJSON(sceneFilePath + '/scene_index.json') configData = sceneData and sceneData.get('config', None) if not configData: return managerData = configData.get('managers', None) if not managerData: return scenePortalData = managerData.get('ScenePortalManager', None) if not scenePortalData: return scenePath = sceneNode.getPath() prior = sceneNode.getInheritedMetaData('scene_portal_priority', 0) for portalData in scenePortalData.get('portals', []): name = portalData['name'] fullname = portalData['fullname'] prevEntry = registry.get(fullname, None) if prevEntry: prevScene = prevEntry['scene'] if prevScene != scenePath: prior0 = prevEntry['priority'] if prior < prior0: logging.info( 'ignore duplicated portal(low priority): %s @ %s' % (fullname, scenePath)) continue elif prior == prior0: #TODO: warn about duplicated portal logging.warning( 'duplicated portal ID: %s, found in %s and %s' % (fullname, scenePath, prevScene)) continue newEntry = { 'fullname': fullname, 'name': name, 'scene': scenePath, 'data': portalData, 'priority': prior } registry[fullname] = newEntry logging.info('add portal: %s @ %s' % (fullname, scenePath)) def locatePortalGroup(self, groupName): registry = _MOCK.getScenePortalRegistry() scenePath = registry.getPortalGroupDefaultScene(registry, groupName) if not scenePath: alertMessage('No scene for portal group', 'No default scene found for group:' + groupName) return False node = self.getAssetLibrary().getAssetNode(scenePath) if not node: alertMessage('No scene found', 'Target scene not found:' + scenePath) return False node.edit() def locatePortal(self, portalInfo, select=False): if self.updatingSelection: return if isinstance(portalInfo, str): #ID? registry = _MOCK.getScenePortalRegistry() portalInfo = registry.getPortalInfo(registry, portalInfo) if not portalInfo: return False scenePath = portalInfo.scene sceneNode = self.getAssetLibrary().getAssetNode(scenePath) if sceneNode: guid = portalInfo.data.guid editor = self.getModule('scenegraph_editor') if editor.getActiveSceneNode() == sceneNode: editor.locateEntityByGUID(guid) else: # if editor.getActiveSceneNode() and ( not requestConfirm( 'Changing Scene', 'opening another scene, proceed?' ) ): # return callLocating = signals.callOnce( 'scene.change', lambda: editor.locateEntityByGUID(guid)) if not editor.openScene(sceneNode): signals.dropCall(callLocating) def setCurrentGraph(self, graphNode): self.currentGraphNode = graphNode if graphNode: parser = YEdGraphMLParser() assetPath = graphNode.path assetNode = self.getAssetLibrary().getAssetNode(assetPath) g = parser.parse(assetNode.getAbsFilePath()) if g: self.graphView.loadGraph(g) self.graphView.fitAll() else: self.graphView.clear() def locateGraph(self, graph): self.getModule('asset_browser').locateAsset(graph.path) def getPortalList(self): if self.showAllScenePortals: registry = _MOCK.getScenePortalRegistry() return [info for info in list(registry.portals.values())] else: return self.getCurrentScenePortals() def getCurrentScenePortals(self): registry = _MOCK.getScenePortalRegistry() editor = self.getModule('scenegraph_editor') sceneNode = editor.getActiveSceneNode() if not sceneNode: return [] result = [] for info in list(registry.portals.values()): if info.scene == sceneNode.getPath(): result.append(info) return result def onSelectionChanged(self, selection, key): #TODO pass def onSceneChange(self): pass def onAssetRegister(self, assetNode): if assetNode.getType() == 'scene': self.processScenePortal(assetNode) self.saveRegistry() def onAssetUnregister(self, assetNode): if assetNode.getType() == 'scene': self.clearScenePortal(assetNode.getPath()) self.saveRegistry() def onAssetModified(self, assetNode): if assetNode.getType() == 'scene': self.processScenePortal(assetNode) self.saveRegistry() def enumerateSelectableGraphs(self, typeId, context, option): result = [] registry = _MOCK.getScenePortalRegistry() for asset in self.getAssetLibrary().iterAssets(): if asset.getType() != 'scene_portal_graph': continue path = asset.getPath() if registry.hasGraph(registry, path): continue entry = (asset, path, 'portal_graph', 'portal_graph') result.append(entry) return result def promptProtalJump(self): entries = [(portal.id, portal.id, 'portal', 'portal') for portal in self.getCurrentScenePortals()] def _locateConnectedPortal(id): if not self.locateConnectedPortal(id): alertMessage('No connection', 'No connected portal found') def _locatePortal(selections): pass requestSearchView(info='select portal to jump', selections=entries, on_selection=_locateConnectedPortal, on_change=_locatePortal) def locateConnectedPortal(self, portalId): registry = _MOCK.getScenePortalRegistry() targetId = registry.findConnectedPortal(registry, portalId) if targetId: self.locatePortal(targetId) return True return False def addGraph(self, assetNode): registry = _MOCK.getScenePortalRegistry() graphNode = registry.addGraph(registry, assetNode.getPath()) self.treeGraphs.addNode(graphNode) def removeGraph(self, graphNode): registry = _MOCK.getScenePortalRegistry() registry.removeGraph(registry, graphNode.getPath(graphNode)) self.treeGraphs.removeNode(graphNode) def onTool(self, tool): name = tool.name if name == 'add_graph': requestSearchView(info='select portal graph to add', context='portal_graph', on_search=self.enumerateSelectableGraphs, on_selection=self.addGraph) elif name == 'remove_graph': for node in self.treeGraphs.getSelection(): self.removeGraph(node) elif name == 'refresh': self.treeGraphs.rebuild() def onMenu(self, menu): if menu.name == 'rebuild_portal_registry': self.clearRegistry() self.scanPortals() print('done') elif menu.name == 'portal_jump': self.promptProtalJump()
class QuestSessionManager( SceneEditorModule ): name = 'quest_session_manager' dependency = [ 'mock' ] def onLoad( self ): #UI self.container = self.requestDockWindow( 'QuestSessionManager', title = 'Quest', size = (120,120), minSize = (120,120), dock = 'left', toolWindow = False ) self.window = window = self.container.addWidgetFromFile( _getModulePath('container.ui') ) #Components leftLayout = QtWidgets.QVBoxLayout( window.containerLeft ) leftLayout.setSpacing( 0 ) leftLayout.setContentsMargins( 0 , 0 , 0 , 0 ) rightLayout = QtWidgets.QVBoxLayout( window.containerRight ) rightLayout.setSpacing( 0 ) rightLayout.setContentsMargins( 0 , 0 , 0 , 0 ) self.treeFilter = GenericTreeFilter( window.containerRight ) self.treeFilter.setSizePolicy( QtWidgets.QSizePolicy.Expanding, QtWidgets.QSizePolicy.Fixed ) self.tree = QuestSchemeTreeWidget( window.containerLeft ) self.treeFilter.setTargetTree( self.tree ) self.toolbar = QtWidgets.QToolBar( window.containerLeft ) self.toolbar.setOrientation( Qt.Horizontal ) self.toolbar.setMaximumHeight( 32 ) self.graphView = QuestSchemeGraphView( parent = window.containerRight, use_gl = False, antialias = True ) leftLayout.addWidget( self.toolbar ) leftLayout.addWidget( self.treeFilter ) leftLayout.addWidget( self.tree ) rightLayout.addWidget( self.graphView ) self.addToolBar( 'quest_session_manager', self.toolbar ) self.addTool( 'quest_session_manager/add_session', label = 'Add', icon = 'add_folder' ) self.addTool( 'quest_session_manager/----' ) self.addTool( 'quest_session_manager/add_scheme', label = 'Add', icon = 'add' ) self.addTool( 'quest_session_manager/remove_scheme', label = 'Remove', icon = 'remove' ) self.addTool( 'quest_session_manager/----' ) self.addTool( 'quest_session_manager/refresh', label = 'Refresh', icon = 'refresh' ) self.portalRegistry = None self.showAllScenePortals = False self.updatingSelection = False self.currentSchemeEntry = None self.currentSession = None def onAppReady( self ): self.tree.rebuild() def setCurrentSelection( self, session, schemeEntry ): self.currentSession = session self.currentSchemeEntry = schemeEntry self.graphView.clear() if schemeEntry: parser = YEdGraphMLParser() assetPath = schemeEntry.path assetNode = self.getAssetLibrary().getAssetNode( assetPath ) g = parser.parse( assetNode.getAbsFilePath() ) if g: self.graphView.loadGraph( g ) self.graphView.fitAll() def addSession( self ): mgr = _MOCK.getQuestManager() session = mgr.addSession( mgr ) self.tree.addNode( session ) self.tree.editNode( session ) return session def removeSession( self, session ): mgr = _MOCK.getQuestManager() mgr.removeSession( mgr, session ) self.tree.removeNode( session ) def renameSession( self, session, name ): mgr = _MOCK.getQuestManager() mgr.renameSession( mgr, session, name ) self.tree.refreshNodeContent( session ) def addSchemeEntry( self, assetNode ): if not self.currentSession: logging.warn( 'no quest session specified' ) return False session = self.currentSession schemeEntry = session.addSchemeEntry( session, assetNode.getPath() ) self.updateQuestNodeList() if schemeEntry: self.tree.addNode( schemeEntry ) return schemeEntry def updateQuestNodeList( self ): pass def removeSchemeEntry( self, schemeEntry ): if not self.currentSession: logging.warn( 'no quest session specified' ) return False self.tree.removeNode( schemeEntry ) session = self.currentSession session.removeSchemeEntry( session, schemeEntry ) def enumerateSelectableGraphs( self, typeId, context, option ): result = [] session = self.currentSession if not session: return [] for asset in self.getAssetLibrary().iterAssets(): if asset.getType() != 'quest_scheme': continue path = asset.getPath() if session.hasScheme( session, path ): continue entry = ( asset, path, 'quest_scheme', 'quest_scheme' ) result.append( entry ) return result def onTool( self, tool ): name = tool.name if name == 'add_session': self.addSession() elif name == 'add_scheme': if not self.currentSession: logging.warn( 'no quest session specified' ) return False requestSearchView( info = 'select quest scheme to add', context = 'quest_scheme', on_search = self.enumerateSelectableGraphs, on_selection = self.addSchemeEntry ) elif name == 'remove_scheme': if not self.currentSession: logging.warn( 'no quest session specified' ) return False for node in self.tree.getSelection(): if isMockInstance( node, 'QuestSessionSchemeEntry' ): self.removeSchemeEntry( node ) elif isMockInstance( node, 'QuestSession' ): self.removeSession( node ) elif name == 'refresh': self.tree.rebuild()