from ShowBaseGlobal import * from PandaObject import * from PieMenu import * from DirectGuiGlobals import * from Tkinter import * from DirectUtil import * from DirectGeometry import * from SceneGraphExplorer import * from tkMessageBox import showinfo from tkFileDialog import * from whrandom import * import Pmw import Floater import VectorWidgets import string import os import getopt import sys import whrandom import __builtin__ visualizeZones = base.config.GetBool("visualize-zones", 0) # Temporary try..except for old Pandas. try: dnaDirectory = Filename.expandFrom(base.config.GetString("dna-directory", "$TTMODELS/src/dna")) except: dnaDirectory = Filename.fromOsSpecific(base.config.GetString("dna-directory", "/")) # Colors used by all color menus DEFAULT_COLORS = [ Vec4(1,1,1,1), Vec4(0.75, 0.75, 0.75, 1.0 ), Vec4(0.5, 0.5, 0.5, 1.0), Vec4(0.25, 0.25, 0.25, 1.0) ] # The list of items with color attributes COLOR_TYPES = ['wall_color', 'window_color', 'window_awning_color', 'sign_color', 'door_color', 'door_awning_color', 'cornice_color', 'prop_color'] # The list of dna components maintained in the style attribute dictionary DNA_TYPES = ['wall', 'window', 'sign', 'door', 'cornice', 'toon_landmark', 'prop', 'street'] BUILDING_TYPES = ['10_10', '20', '10_20', '20_10', '10_10_10', '4_21', '3_22', '4_13_8', '3_13_9', '10', '12_8', '13_9_8' ] BUILDING_HEIGHTS = [10, 20, 25, 30] NUM_WALLS = [1,2,3] OBJECT_SNAP_POINTS = { 'street_5x20': [(Vec3(5.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_10x20': [(Vec3(10.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_20x20': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_30x20': [(Vec3(30.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_40x20': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_80x20': [(Vec3(80.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_5x40': [(Vec3(5.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_10x40': [(Vec3(10.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_20x40': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_20x40_15': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_30x40': [(Vec3(30.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_40x40': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_20x60': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_40x60': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_40x40_15': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_80x40': [(Vec3(80.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_angle_30': [(Vec3(0), Vec3(-30,0,0)), (Vec3(0), Vec3(0))], 'street_angle_45': [(Vec3(0), Vec3(-45,0,0)), (Vec3(0), Vec3(0))], 'street_angle_60': [(Vec3(0), Vec3(-60,0,0)), (Vec3(0), Vec3(0))], 'street_inner_corner': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_outer_corner': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_full_corner': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_tight_corner': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_tight_corner_mirror': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_double_corner': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_curved_corner': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_curved_corner_15': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_t_intersection': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_y_intersection': [(Vec3(30.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_street_20x20': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_street_40x40': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_sidewalk_20x20': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_sidewalk_40x40': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_divided_transition': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_divided_40x70': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_divided_transition_15': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_divided_40x70_15': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_stairs_40x10x5': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_4way_intersection': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_incline_40x40x5': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_square_courtyard': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_courtyard_70': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_courtyard_70_exit': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_courtyard_90': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_courtyard_90_exit': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_courtyard_70_15': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_courtyard_70_15_exit': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_courtyard_90_15': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_courtyard_90_15_exit': [(Vec3(0.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_50_transition': [(Vec3(10.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_20x50': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_40x50': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_keyboard_10x40': [(Vec3(10.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_keyboard_20x40': [(Vec3(20.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_keyboard_40x40': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], 'street_sunken_40x40': [(Vec3(40.0,0,0), Vec3(0)), (Vec3(0), Vec3(0))], } # NEIGHBORHOOD DATA # If you run this from the command line you can pass in the hood codes # you want to load. For example: # ppython LevelEditor.py DD TT BR # if sys.argv[1:]: try: opts, pargs = getopt.getopt(sys.argv[1:], '') hoods = pargs except Exception, e: print e # If you do not run from the command line, we just load all of them # or you can hack this up for your own purposes. else: hoodString = base.config.GetString('level-editor-hoods', 'TT DD BR DG DL MM') hoods = string.split(hoodString) # The list of neighborhoods to edit hoodIds = {'TT' : 'toontown_central', 'DD' : 'donalds_dock', 'MM' : 'minnies_melody_land', 'BR' : 'the_burrrgh', 'DG' : 'daisys_garden', 'DL' : 'donalds_dreamland', } # Init neighborhood arrays NEIGHBORHOODS = [] NEIGHBORHOOD_CODES = {} for hoodId in hoods: if hoodIds.has_key(hoodId): hoodName = hoodIds[hoodId] NEIGHBORHOOD_CODES[hoodName] = hoodId NEIGHBORHOODS.append(hoodName) else: print 'Error: no hood defined for: ', hoodId # Load DNA try: if dnaLoaded: pass except NameError: print "Loading LevelEditor for hoods: ", hoods # DNAStorage instance for storing level DNA info __builtin__.DNASTORE = DNASTORE = DNAStorage() # Load the generic storage files loadDNAFile(DNASTORE, 'phase_4/dna/storage.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_5/dna/storage_town.dna', CSDefault, 1) # Load all the neighborhood specific storage files if 'TT' in hoods: loadDNAFile(DNASTORE, 'phase_4/dna/storage_TT.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_4/dna/storage_TT_sz.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_5/dna/storage_TT_town.dna', CSDefault, 1) if 'DD' in hoods: loadDNAFile(DNASTORE, 'phase_6/dna/storage_DD.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_6/dna/storage_DD_sz.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_6/dna/storage_DD_town.dna', CSDefault, 1) if 'MM' in hoods: loadDNAFile(DNASTORE, 'phase_6/dna/storage_MM.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_6/dna/storage_MM_sz.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_6/dna/storage_MM_town.dna', CSDefault, 1) if 'BR' in hoods: loadDNAFile(DNASTORE, 'phase_8/dna/storage_BR.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_8/dna/storage_BR_sz.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_8/dna/storage_BR_town.dna', CSDefault, 1) if 'DG' in hoods: loadDNAFile(DNASTORE, 'phase_8/dna/storage_DG.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_8/dna/storage_DG_sz.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_8/dna/storage_DG_town.dna', CSDefault, 1) if 'DL' in hoods: loadDNAFile(DNASTORE, 'phase_8/dna/storage_DL.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_8/dna/storage_DL_sz.dna', CSDefault, 1) loadDNAFile(DNASTORE, 'phase_8/dna/storage_DL_town.dna', CSDefault, 1) __builtin__.dnaLoaded = 1 # Precompute class types for type comparisons DNA_CORNICE = DNACornice.getClassType() DNA_DOOR = DNADoor.getClassType() DNA_FLAT_DOOR = DNAFlatDoor.getClassType() DNA_FLAT_BUILDING = DNAFlatBuilding.getClassType() DNA_NODE = DNANode.getClassType() DNA_GROUP = DNAGroup.getClassType() DNA_VIS_GROUP = DNAVisGroup.getClassType() DNA_LANDMARK_BUILDING = DNALandmarkBuilding.getClassType() DNA_NODE = DNANode.getClassType() DNA_PROP = DNAProp.getClassType() DNA_SIGN = DNASign.getClassType() DNA_SIGN_BASELINE = DNASignBaseline.getClassType() DNA_SIGN_TEXT = DNASignText.getClassType() DNA_SIGN_GRAPHIC = DNASignGraphic.getClassType() DNA_STREET = DNAStreet.getClassType() DNA_WALL = DNAWall.getClassType() DNA_WINDOWS = DNAWindows.getClassType() # DNA Utility functions (possible class extensions?) def DNARemoveChildren(dnaObject): """ Utility function to delete all the children of a DNANode """ children = [] for i in range(dnaObject.getNumChildren()): children.append(dnaObject.at(i)) for child in children: dnaObject.remove(child) DNASTORE.removeDNAGroup(child) def DNARemoveChildOfClass(dnaNode, classType, childNum = 0): """ Remove the nth object of that type you come across """ childCount = 0 for i in range(dnaNode.getNumChildren()): child = dnaNode.at(i) if DNAClassEqual(child, classType): if childCount == childNum: dnaNode.remove(child) DNASTORE.removeDNAGroup(child) return 1 childCount = childCount + 1 # None found return 0 def DNARemoveAllChildrenOfClass(dnaNode, classType): """ Remove the objects of that type """ children = [] for i in range(dnaNode.getNumChildren()): child=dnaNode.at(i) if DNAClassEqual(child, classType): children.append(child) for child in children: dnaNode.remove(child) DNASTORE.removeDNAGroup(child) def DNAGetChildren(dnaNode, classType=None): """ Return the objects of that type """ children = [] for i in range(dnaNode.getNumChildren()): child=dnaNode.at(i) if ((not classType) or DNAClassEqual(child, classType)): children.append(child) return children def DNAGetChild(dnaObject, type = DNA_NODE, childNum = 0): childCount = 0 for i in range(dnaObject.getNumChildren()): child = dnaObject.at(i) if DNAClassEqual(child, type): if childCount == childNum: return child childCount = childCount + 1 # Not found return None def DNAGetChildRecursive(dnaObject, type = DNA_NODE, childNum = 0): childCount = 0 for i in range(dnaObject.getNumChildren()): child = dnaObject.at(i) if DNAClassEqual(child, type): if childCount == childNum: return child childCount = childCount + 1 else: child = DNAGetChildRecursive(child, type, childNum-childCount) if child: return child # Not found return None def DNAGetChildOfClass(dnaNode, classType): for i in range(dnaNode.getNumChildren()): child = dnaNode.at(i) if DNAClassEqual(child, classType): return child # Not found return None def DNAGetClassType(dnaObject): return dnaObject.__class__.getClassType() def DNAClassEqual(dnaObject, classType): return DNAGetClassType(dnaObject).eq(classType) def DNAIsDerivedFrom(dnaObject, classType): return DNAGetClassType(dnaObject).isDerivedFrom(classType) def DNAGetWallHeights(aDNAFlatBuilding): """ Get a list of wall heights for a given flat building """ # Init variables heightList = [] offsetList = [] offset = 0.0 # Compute wall heights for i in range(aDNAFlatBuilding.getNumChildren()): child = aDNAFlatBuilding.at(i) if DNAClassEqual(child, DNA_WALL): height = child.getHeight() heightList.append(height) offsetList.append(offset) offset = offset + height return heightList, offsetList def DNAGetBaselineString(baseline): s="" for i in range(baseline.getNumChildren()): child = baseline.at(i) if DNAClassEqual(child, DNA_SIGN_TEXT): s=s+child.getLetters() elif DNAClassEqual(child, DNA_SIGN_GRAPHIC): s=s+'['+child.getCode()+']' return s def DNASetBaselineString(baseline, text): # TODO: Instead of removing all the text and replacing it, # replace each text item and then add or remove at the end. # This should allow inlined graphics to stay in place. # end of todo. DNARemoveAllChildrenOfClass(baseline, DNA_SIGN_TEXT); for i in text: text=DNASignText("text") text.setLetters(i) baseline.add(text) class LevelEditor(NodePath, PandaObject): """Class used to create a Toontown LevelEditor object""" # Init the list of callbacks: selectedNodePathHookHooks=[] deselectedNodePathHookHooks=[] # Primary variables: # DNAData: DNA object holding DNA info about level # DNAToplevel: Top level DNA Node, all DNA objects descend from this node # NPToplevel: Corresponding Node Path # DNAParent: Current DNA Node that new objects get added to # NPParent: Corresponding Node Path # DNAVisGroup: Current DNAVisGroup that new objects get added to # NPVisGroup: Corresponding Node Path # selectedDNARoot: DNA Node of currently selected object # selectedNPRoot: Corresponding Node Path # DNATarget: Subcomponent being modified by Pie Menu def __init__(self, hoods = hoods): # Make the level editor a node path so that you can show/hide # The level editor separately from loading/saving the top level node # Initialize superclass NodePath.__init__(self) # Become the new node path self.assign(hidden.attachNewNode('LevelEditor')) # Enable replaceSelected by default: self.replaceSelectedEnabled=1 self.removeHookList=[self.landmarkBlockRemove] # Start block ID at 0 (it will be incremented before use (to 1)): self.landmarkBlock=0 # Create ancillary objects # Style manager for keeping track of styles/colors self.styleManager = LevelStyleManager() # Load neighborhood maps self.createLevelMaps() # Marker for showing next insertion point self.createInsertionMarker() # Create level Editor Panel self.panel = LevelEditorPanel(self) # Used to store whatever edges and points are loaded in the level self.edgeDict = {} self.pointDict = {} self.point2edgeDict = {} self.cellDict = {} self.visitedPoints = [] self.visitedEdges = [] # Initialize LevelEditor variables DNAData, DNAToplevel, NPToplevel # DNAParent, NPParent, groupNum, lastAngle # Pass in the new toplevel group and don't clear out the old # toplevel group (since it doesn't exist yet) self.reset(fDeleteToplevel = 0, fCreateToplevel = 1) # The list of events the level editor responds to self.actionEvents = [ # Node path events ('preRemoveNodePath', self.removeNodePathHook), # Actions in response to DIRECT operations ('DIRECT_selectedNodePath', self.selectedNodePathHook), ('DIRECT_deselectedNodePath', self.deselectedNodePathHook), ('DIRECT_manipulateObjectCleanup', self.updateSelectedPose), ('DIRECT_nodePathSetName', self.setName), ('DIRECT_activeParent', self.setActiveParent), ('DIRECT_reparent', self.reparent), ('RGBPanel_setColor', self.setColor), # Actions in response to Level Editor Panel operations ('SGE_Add Group', self.addGroup), ('SGE_Add Vis Group', self.addVisGroup), # Actions in response to Pie Menu interaction ('select_building_style_all', self.setBuildingStyle), ('select_building_type', self.setBuildingType), ('select_building_width', self.setBuildingWidth), ('select_cornice_color', self.setDNATargetColor), ('select_cornice_orientation', self.setDNATargetOrientation), ('select_cornice_texture', self.setDNATargetCode, ['cornice']), ('select_sign_color', self.setDNATargetColor), ('select_sign_orientation', self.setDNATargetOrientation), ('select_sign_texture', self.setDNATargetCode, ['sign']), ('select_baseline_style', self.panel.setSignBaselineStyle), ('select_door_color', self.setDNATargetColor), ('select_door_orientation', self.setDNATargetOrientation), ('select_door_texture', self.setDNATargetCode, ['door']), ('select_door_awning_texture', self.setDNATargetCode, ['door_awning']), ('select_door_awning_color', self.setDNATargetColor), ('select_window_color', self.setDNATargetColor), ('select_window_count', self.setWindowCount), ('select_window_orientation', self.setDNATargetOrientation), ('select_window_texture', self.setDNATargetCode, ['windows']), ('select_window_awning_texture', self.setDNATargetCode, ['window_awning']), ('select_window_awning_color', self.setDNATargetColor), ('select_wall_style', self.setWallStyle), ('select_wall_color', self.setDNATargetColor), ('select_wall_orientation', self.setDNATargetOrientation), ('select_wall_texture', self.setDNATargetCode, ['wall']), ('select_toon_landmark_texture', self.setDNATargetCode, ['landmark']), ('select_toon_landmark_door_color', self.setDNATargetColor), ('select_toon_landmark_door_orientation', self.setDNATargetOrientation), ('select_landmark_door_texture', self.setDNATargetCode, ['landmark_door']), ('select_street_texture', self.setDNATargetCode, ['street']), ('select_prop_texture', self.setDNATargetCode, ['prop']), ('select_prop_color', self.setDNATargetColor), # Hot key actions ('a', self.autoPositionGrid), ('j', self.jumpToInsertionPoint), ('arrow_left', self.keyboardXformSelected, ['left', 'xlate']), ('arrow_right', self.keyboardXformSelected, ['right', 'xlate']), ('arrow_up', self.keyboardXformSelected, ['up','xlate']), ('arrow_down', self.keyboardXformSelected, ['down','xlate']), ('control-arrow_left', self.keyboardXformSelected, ['left', 'rotate']), ('control-arrow_right', self.keyboardXformSelected, ['right', 'rotate']), ('control-arrow_up', self.keyboardXformSelected, ['up', 'rotate']), ('control-arrow_down', self.keyboardXformSelected, ['down', 'rotate']), ('shift-s', self.placeSuitPoint), ('shift-c', self.placeBattleCell), ('o', self.addToLandmarkBlock), ('shift-o', self.toggleShowLandmarkBlock), ('%', self.pdbBreak), ] # Initialize state # Make sure direct is running direct.enable() # And only the appropriate handles are showing direct.widget.disableHandles(['x-ring', 'x-disc', 'y-ring', 'y-disc', 'z-post']) # Initialize camera base.camLens.setNear(1.0) base.camLens.setFar(3000) direct.camera.setPos(0,-10,10) # Initialize drive mode self.configureDriveModeCollisionData() # Init visibility variables self.__zoneId = None # Hide (disable) grid initially self.showGrid(0) # Create variable for vis groups panel self.vgpanel = None # Start off enabled self.enable() # SUIT POINTS # Create a sphere model to show suit points self.suitPointMarker = loader.loadModel('models/misc/sphere') self.suitPointMarker.setScale(0.25) # Initialize the suit points self.startSuitPoint = None self.endSuitPoint = None self.currentSuitPointType = DNASuitPoint.STREETPOINT # BATTLE CELLS self.battleCellMarker = loader.loadModel('models/misc/sphere') self.battleCellMarker.setName('battleCellMarker') self.battleCellMarker.setScale(1) self.currentBattleCellType = "20w 20l" # Update panel # Editing the first hood id on the list self.setEditMode(NEIGHBORHOODS[0]) # Start of with first item in lists self.panel.streetSelector.selectitem(0) self.panel.streetSelector.invoke() self.panel.toonBuildingSelector.selectitem(0) self.panel.toonBuildingSelector.invoke() self.panel.landmarkBuildingSelector.selectitem(0) self.panel.landmarkBuildingSelector.invoke() self.panel.propSelector.selectitem(0) self.panel.propSelector.invoke() # Start off with 20 foot buildings self.panel.twentyFootButton.invoke() # Update scene graph explorer self.panel.sceneGraphExplorer.update() # ENABLE/DISABLE def enable(self): """ Enable level editing and show level """ # Make sure level is visible self.reparentTo(direct.group) self.show() # Add all the action events for event in self.actionEvents: if len(event) == 3: self.accept(event[0], event[1], event[2]) else: self.accept(event[0], event[1]) # Enable mouse interaction (pie menus) self.enableMouse() # Spawn task to keep insertion marker aligned with grid self.spawnInsertionMarkerTask() def disable(self): """ Disable level editing and hide level """ # Deselect everything as a precaution direct.deselectAll() # Hide the level self.reparentTo(hidden) # Ignore the hooks for eventPair in self.actionEvents: self.ignore(eventPair[0]) # These are added outside of actionEvents list self.ignore('insert') self.ignore('space') # Disable Pie Menu mouse interaction self.disableMouse() # Remove insertion marker task taskMgr.remove('insertionMarkerTask') def reset(self, fDeleteToplevel = 1, fCreateToplevel = 1, fUpdateExplorer = 1): """ Reset level and re-initialize main class variables Pass in the new top level group """ # Reset path markers self.resetPathMarkers() # Reset battle cell markers self.resetBattleCellMarkers() if fDeleteToplevel: # First destroy existing scene-graph/DNA hierarchy self.deleteToplevel() # Clear DNASTORE DNASTORE.resetDNAGroups() # Reset DNA VIS Groups DNASTORE.resetDNAVisGroups() DNASTORE.resetSuitPoints() DNASTORE.resetBattleCells() # Create fresh DNA DATA self.DNAData = DNAData('level_data') # Create new toplevel node path and DNA if fCreateToplevel: self.createToplevel(DNANode('level')) # Initialize variables # Reset grid direct.grid.setPosHprScale(0,0,0,0,0,0,1,1,1) # The selected DNA Object/NodePath self.selectedDNARoot = None self.selectedNPRoot = None self.selectedSuitPoint = None self.lastLandmarkBuildingDNA = None self.showLandmarkBlockToggleGroup = None # Set active target (the subcomponent being modified) self.DNATarget = None self.DNATargetParent = None # Set count of groups added to level self.setGroupNum(0) # Heading angle of last object added to level self.setLastAngle(0.0) # Last wall and building modified using pie menus self.lastSign = None self.lastWall = None self.lastBuilding = None # Code of last selected object (for autopositionGrid) self.snapList = [] # Last menu used self.activeMenu = None # For highlighting suit paths self.visitedPoints = [] self.visitedEdges = [] # Update scene graph explorer if fUpdateExplorer: self.panel.sceneGraphExplorer.update() def deleteToplevel(self): # Destory old toplevel node path and DNA # First the toplevel DNA self.DNAData.remove(self.DNAToplevel) # Then the toplevel Node Path self.NPToplevel.removeNode() def createToplevel(self, dnaNode, nodePath = None): # When you create a new level, data is added to this node # When you load a DNA file, you replace this node with the new data self.DNAToplevel = dnaNode self.DNAData.add(self.DNAToplevel) if nodePath: # Node path given, use it self.NPToplevel = nodePath self.NPToplevel.reparentTo(self) else: # No node path given, traverse self.NPToplevel = self.DNAToplevel.traverse(self, DNASTORE, 1) # Update parent pointers self.DNAParent = self.DNAToplevel self.NPParent = self.NPToplevel self.VGParent = None # Add toplevel node path for suit points self.suitPointToplevel = self.NPToplevel.attachNewNode('suitPoints') def destroy(self): """ Disable level editor and destroy node path """ self.disable() self.removeNode() self.panel.destroy() if self.vgpanel: self.vgpanel.destroy() def useDirectFly(self): """ Disable player camera controls/enable direct camera control """ # Turn off collision traversal self.traversalOff() # Turn on collisions self.collisionsOff() # Turn on visiblity self.visibilityOff() # Reset cam base.camera.iPos(base.cam) base.cam.iPosHpr() # Renable mouse self.enableMouse() direct.enable() def useDriveMode(self): """ Lerp down to eye level then switch to Drive mode """ pos = direct.camera.getPos() pos.setZ(4.0) hpr = direct.camera.getHpr() hpr.set(hpr[0], 0.0, 0.0) t = direct.camera.lerpPosHpr(pos, hpr, 1.0, blendType = 'easeInOut', task = 'manipulateCamera') # Note, if this dies an unatural death, this could screw things up t.uponDeath = self.switchToDriveMode def switchToDriveMode(self,state): """ Disable direct camera manipulation and enable player drive mode """ direct.minimumConfiguration() direct.manipulationControl.disableManipulation() # Update vis data self.initVisibilityData() # Switch to drive mode base.useDrive() # Move cam up and back base.cam.setPos(0,-5,4) # And move down and forward to compensate base.camera.setPos(base.camera, 0, 5, -4) # Make sure we're where we want to be pos = direct.camera.getPos() pos.setZ(0.0) hpr = direct.camera.getHpr() hpr.set(hpr[0], 0.0, 0.0) # Fine tune the drive mode base.mouseInterface.node().setPos(pos) base.mouseInterface.node().setHpr(hpr) base.mouseInterface.node().setForwardSpeed(20.0) base.mouseInterface.node().setReverseSpeed(20.0) # Turn on collisions if self.panel.fColl.get(): self.collisionsOn() # Turn on visiblity if self.panel.fVis.get(): self.visibilityOn() # Turn on collision traversal if self.panel.fColl.get() or self.panel.fVis.get(): self.traversalOn() def configureDriveModeCollisionData(self): """initializeCollisions(self) Set up the local avatar for collisions """ # Set up the collision sphere # This is a sphere on the ground to detect barrier collisions self.cSphere = CollisionSphere(0.0, 0.0, 0.0, 1.5) self.cSphereNode = CollisionNode('cSphereNode') self.cSphereNode.addSolid(self.cSphere) self.cSphereNodePath = camera.attachNewNode(self.cSphereNode) self.cSphereNodePath.hide() self.cSphereBitMask = BitMask32.bit(0) self.cSphereNode.setFromCollideMask(self.cSphereBitMask) self.cSphereNode.setIntoCollideMask(BitMask32.allOff()) # Set up the collison ray # This is a ray cast from your head down to detect floor polygons self.cRay = CollisionRay(0.0, 0.0, 6.0, 0.0, 0.0, -1.0) self.cRayNode = CollisionNode('cRayNode') self.cRayNode.addSolid(self.cRay) self.cRayNodePath = camera.attachNewNode(self.cRayNode) self.cRayNodePath.hide() self.cRayBitMask = BitMask32.bit(1) self.cRayNode.setFromCollideMask(self.cRayBitMask) self.cRayNode.setIntoCollideMask(BitMask32.allOff()) # set up wall collision mechanism self.pusher = CollisionHandlerPusher() self.pusher.setInPattern("enter%in") self.pusher.setOutPattern("exit%in") # set up floor collision mechanism self.lifter = CollisionHandlerFloor() self.lifter.setInPattern("on-floor") self.lifter.setOutPattern("off-floor") self.floorOffset = 0.1 self.lifter.setOffset(self.floorOffset) # Limit our rate-of-fall with the lifter. # If this is too low, we actually "fall" off steep stairs # and float above them as we go down. I increased this # from 8.0 to 16.0 to prevent this self.lifter.setMaxVelocity(16.0) # set up the collision traverser self.cTrav = CollisionTraverser() # activate the collider with the traverser and pusher self.pusher.addColliderNode(self.cSphereNode, base.drive.node()) self.lifter.addColliderNode(self.cRayNode, base.drive.node()) # A map of zone ID's to a list of nodes that are visible from # that zone. self.nodeDict = {} # A map of zone ID's to the particular node that corresponds # to that zone. self.zoneDict = {} # A list of all visible nodes self.nodeList = [] # Flag for bootstrapping visibility self.fVisInit = 0 def traversalOn(self): base.cTrav = self.cTrav def traversalOff(self): base.cTrav = 0 def collisionsOff(self): self.cTrav.removeCollider(self.cSphereNode) def collisionsOn(self): self.collisionsOff() self.cTrav.addCollider(self.cSphereNode, self.pusher) def toggleCollisions(self): if self.panel.fColl.get(): self.collisionsOn() self.traversalOn() else: self.collisionsOff() if (not (self.panel.fColl.get() or self.panel.fVis.get())): self.traversalOff() def initVisibilityData(self): # First make sure everything is shown self.showAllVisibles() # A map of zone ID's to a list of nodes that are visible from # that zone. self.nodeDict = {} # A map of zone ID's to the particular node that corresponds # to that zone. self.zoneDict = {} # A list of all visible nodes self.nodeList = [] # NOTE: this should change to find the groupnodes in # the dna storage instead of searching through the tree for i in range(DNASTORE.getNumDNAVisGroups()): groupFullName = DNASTORE.getDNAVisGroupName(i) groupName = self.extractGroupName(groupFullName) zoneId = int(groupName) self.nodeDict[zoneId] = [] self.zoneDict[zoneId] = self.NPToplevel.find("**/" + groupName) # TODO: we only need to look from the top of the hood # down one level to find the vis groups as an optimization groupNode = self.NPToplevel.find("**/" + groupFullName) if groupNode.isEmpty(): print "Could not find visgroup" self.nodeList.append(groupNode) for j in range(DNASTORE.getNumVisiblesInDNAVisGroup(i)): visName = DNASTORE.getVisibleName(i, j) visNode = self.NPToplevel.find("**/" + visName) self.nodeDict[zoneId].append(visNode) # Rename the floor polys to have the same name as the # visgroup they are in... This makes visibility possible. self.renameFloorPolys(self.nodeList) # Init vis flag self.fVisInit = 1 def extractGroupName(self, groupFullName): # The Idea here is that group names may have extra flags associated # with them that tell more information about what is special about # the particular vis zone. A normal vis zone might just be "13001", # but a special one might be "14356:safe_zone" or # "345:safe_zone:exit_zone"... These are hypotheticals. The main # idea is that there are colon separated flags after the initial # zone name. return(string.split(groupFullName, ":", 1)[0]) def renameFloorPolys(self, nodeList): for i in nodeList: # Get all the collision nodes in the vis group collNodePaths = i.findAllMatches("**/+CollisionNode") numCollNodePaths = collNodePaths.getNumPaths() visGroupName = i.node().getName() for j in range(numCollNodePaths): collNodePath = collNodePaths.getPath(j) bitMask = collNodePath.node().getIntoCollideMask() if bitMask.getBit(1): # Bit 1 is the floor collision bit. This renames # all floor collision polys to the same name as their # visgroup. collNodePath.node().setName(visGroupName) def hideAllVisibles(self): for i in self.nodeList: i.hide() def showAllVisibles(self): for i in self.nodeList: i.show() i.clearColor() def visibilityOn(self): self.visibilityOff() # Accept event self.accept("on-floor", self.enterZone) # Add collider self.cTrav.addCollider(self.cRayNode, self.lifter) # Reset lifter self.lifter.clear() # Reset flag self.fVisInit = 1 def visibilityOff(self): self.ignore("on-floor") self.cTrav.removeCollider(self.cRayNode) self.showAllVisibles() def toggleVisibility(self): if self.panel.fVis.get(): self.visibilityOn() self.traversalOn() else: self.visibilityOff() if (not (self.panel.fColl.get() or self.panel.fVis.get())): self.traversalOff() def enterZone(self, newZone): """ Puts the toon in the indicated zone. newZone may either be a CollisionEntry object as determined by a floor polygon, or an integer zone id. It may also be None, to indicate no zone. """ # First entry into a zone, hide everything if self.fVisInit: self.hideAllVisibles() self.fVisInit = 0 # Get zone id if isinstance(newZone, CollisionEntry): # Get the name of the collide node newZoneId = int(newZone.getIntoNode().getName()) else: newZoneId = newZone # Ensure we have vis data assert(self.nodeDict) # Hide the old zone (if there is one) if self.__zoneId != None: for i in self.nodeDict[self.__zoneId]: i.hide() # Show the new zone if newZoneId != None: for i in self.nodeDict[newZoneId]: i.show() # Make sure we changed zones if newZoneId != self.__zoneId: if self.panel.fVisZones.get(): # Set a color override on our zone to make it obvious what # zone we're in. if self.__zoneId != None: self.zoneDict[self.__zoneId].clearColor() if newZoneId != None: self.zoneDict[newZoneId].setColor(0, 0, 1, 1, 100) # The new zone is now old self.__zoneId = newZoneId def enableMouse(self): """ Enable Pie Menu interaction (and disable player camera control) """ # Turn off player camera control base.disableMouse() # Handle mouse events for pie menus self.accept('DIRECT-mouse3',self.levelHandleMouse3) self.accept('DIRECT-mouse3Up',self.levelHandleMouse3Up) def disableMouse(self): """ Disable Pie Menu interaction """ # Disable handling of mouse events self.ignore('DIRECT-mouse3') self.ignore('DIRECT-mouse3Up') # LEVEL OBJECT MANAGEMENT FUNCTIONS def findDNANode(self, nodePath): """ Find node path's DNA Object in DNAStorage (if any) """ if nodePath: return DNASTORE.findDNAGroup(nodePath.node()) else: return None def replaceSelected(self): if self.replaceSelectedEnabled: # Update visible geometry using new DNA newRoot = self.replace(self.selectedNPRoot, self.selectedDNARoot) # Reselect node path and respawn followSelectedNodePathTask direct.select(newRoot) def replace(self, nodePath, dnaNode): """ Replace a node path with the results of a DNANode traversal """ # Find node path's parent parent = nodePath.getParent() dnaParent = dnaNode.getParent() # Get rid of the old node path and remove its DNA and # node relations from the DNA Store self.remove(dnaNode, nodePath) # Traverse the old (modified) dna to create the new node path newNodePath = dnaNode.traverse(parent, DNASTORE, 1) # Add it back to the dna parent dnaParent.add(dnaNode) # Update scene graph explorer # self.panel.sceneGraphExplorer.update() # Return new node path return newNodePath def remove(self, dnaNode, nodePath): """ Delete DNA and Node relation from DNA Store and remove the node path from the scene graph. """ # First delete DNA and node relation from the DNA Store if dnaNode: # Get DNANode's parent parentDNANode = dnaNode.getParent() if parentDNANode: # Remove DNANode from its parent parentDNANode.remove(dnaNode) # Delete DNA and associated Node Relations from DNA Store DNASTORE.removeDNAGroup(dnaNode) if nodePath: # Next deselect nodePath to avoid having bad node paths in the dict direct.deselect(nodePath) # Now you can get rid of the node path nodePath.removeNode() def removeNodePathHook(self, nodePath): dnaNode = self.findDNANode(nodePath) # Does the node path correspond to a DNA Object if dnaNode: for hook in self.removeHookList: hook(dnaNode, nodePath) # Get DNANode's parent parentDNANode = dnaNode.getParent() if parentDNANode: # Remove DNANode from its parent parentDNANode.remove(dnaNode) # Delete DNA and associated Node Relations from DNA Store DNASTORE.removeDNAGroup(dnaNode) else: pointOrCell, type = self.findPointOrCell(nodePath) if pointOrCell and type: if (type == 'suitPointMarker'): print 'Suit Point:', pointOrCell if DNASTORE.removeSuitPoint(pointOrCell): print "Removed from DNASTORE" else: print "Not found in DNASTORE" # Remove point from pointDict del(self.pointDict[pointOrCell]) # Remove point from visitedPoints list if pointOrCell in self.visitedPoints: self.visitedPoints.remove(pointOrCell) # Update edge related dictionaries for edge in self.point2edgeDict[pointOrCell]: # Is it still in edge dict? oldEdgeLine = self.edgeDict.get(edge, None) if oldEdgeLine: del self.edgeDict[edge] oldEdgeLine.reset() del oldEdgeLine # Find other endpoints of edge and clear out # corresponding point2edgeDict entry startPoint = edge.getStartPoint() endPoint = edge.getEndPoint() if pointOrCell == startPoint: self.point2edgeDict[endPoint].remove(edge) elif pointOrCell == endPoint: self.point2edgeDict[startPoint].remove(edge) # Is it in the visited edges list? if edge in self.visitedEdges: self.visitedEdges.remove(edge) # Now delete point2edgeDict entry for this point del(self.point2edgeDict[pointOrCell]) elif (type == 'battleCellMarker'): # Get parent vis group visGroupNP, visGroupDNA = self.findParentVisGroup(nodePath) print 'Battle Cell:', pointOrCell # Remove cell from vis group if visGroupNP and visGroupDNA: if visGroupDNA.removeBattleCell(pointOrCell): print "Removed from Vis Group" else: print "Not found in Vis Group" else: print "Parent Vis Group not found" # Remove cell from DNASTORE if DNASTORE.removeBattleCell(pointOrCell): print "Removed from DNASTORE" else: print "Not found in DNASTORE" # Remove cell from cellDict del(self.cellDict[pointOrCell]) def reparent(self, nodePath, oldParent, newParent): """ Move node path (and its DNA) to active parent """ # Does the node path correspond to a DNA Object dnaNode = self.findDNANode(nodePath) if dnaNode: # Find old parent DNA oldParentDNANode = self.findDNANode(oldParent) # Remove DNA from old parent if oldParentDNANode: oldParentDNANode.remove(dnaNode) if newParent: # Update active parent just to be safe self.setActiveParent(newParent) # Move DNA to new parent (if active parent set) if self.DNAParent != None: self.DNAParent.add(dnaNode) # It is, is it a DNA_NODE (i.e. it has pos/hpr/scale)? # Update pose to reflect new relationship if DNAIsDerivedFrom(dnaNode, DNA_NODE): # Update DNA self.updatePose(dnaNode, nodePath) def setActiveParent(self, nodePath = None): """ Set NPParent and DNAParent to node path and its DNA """ # If we've got a valid node path if nodePath: # See if this is in the DNA database newDNAParent = self.findDNANode(nodePath) if newDNAParent: self.DNAParent = newDNAParent self.NPParent = nodePath else: print 'LevelEditor.setActiveParent: nodePath not found' else: print 'LevelEditor.setActiveParent: nodePath == None' def setName(self, nodePath, newName): """ Set name of nodePath's DNA (if it exists) """ # Find the DNA that corresponds to this node path dnaNode = self.findDNANode(nodePath) if dnaNode: # If it exists, set the name of the DNA Node dnaNode.setName(newName) def updateSelectedPose(self): """ Update the DNA database to reflect selected objects current positions """ for selectedNode in direct.selected: # Is this a DNA Object in the DNASTORE database? dnaObject = self.findDNANode(selectedNode) if dnaObject: # It is, is it a DNA_NODE (i.e. does it have pos/hpr/scale)? if DNAIsDerivedFrom(dnaObject, DNA_NODE): # First snap selected node path to grid pos = selectedNode.getPos(direct.grid) snapPos = direct.grid.computeSnapPoint(pos) if self.panel.fPlaneSnap.get(): zheight = 0 else: zheight = snapPos[2] selectedNode.setPos(direct.grid, snapPos[0], snapPos[1], zheight) # Angle snap h = direct.grid.computeSnapAngle(selectedNode.getH()) selectedNode.setH(h) if selectedNode == direct.selected.last: self.setLastAngle(h) # Update DNA self.updatePose(dnaObject, selectedNode) else: pointOrCell, type = self.findPointOrCell(selectedNode) if pointOrCell and type: # First snap selected node path to grid pos = selectedNode.getPos(direct.grid) snapPos = direct.grid.computeSnapPoint(pos) if self.panel.fPlaneSnap.get(): zheight = 0 else: zheight = snapPos[2] selectedNode.setPos( direct.grid, snapPos[0], snapPos[1], zheight) newPos = selectedNode.getPos(self.NPToplevel) # Update DNA pointOrCell.setPos(newPos) if (type == 'suitPointMarker'): print "Found suit point!", pointOrCell # Ok, now update all the lines into that node for edge in self.point2edgeDict[pointOrCell]: # Is it still in edge dict? oldEdgeLine = self.edgeDict.get(edge, None) if oldEdgeLine: del self.edgeDict[edge] oldEdgeLine.reset() del oldEdgeLine newEdgeLine = self.drawSuitEdge( edge, self.NPParent) self.edgeDict[edge] = newEdgeLine elif (type == 'battleCellMarker'): print "Found battle cell!", pointOrCell def updatePose(self, dnaObject, nodePath): """ Update a DNA Object's pos, hpr, and scale based upon node path's current pose """ # Set DNA's pos, hpr, and scale dnaObject.setPos(nodePath.getPos()) dnaObject.setHpr(nodePath.getHpr()) dnaObject.setScale(nodePath.getScale()) # LEVEL OBJECT CREATION FUNCTIONS def initDNANode(self, dnaNode): """ This method adds a new DNA object to the scene and adds hooks that allow duplicate copies of this DNA node to be added using the space bar. For DNAFlatBuildings, a new copy with random style is generated by hitting the insert key. """ # First create the visible geometry for this DNA Node self.initNodePath(dnaNode) # And add hooks to insert copies of dnaNode self.addReplicationHooks(dnaNode) def addReplicationHooks(self, dnaNode): # Now add hook to allow placement of a new dna Node of this type # by simply hitting the space bar or insert key. Note, extra paramter # indicates new dnaNode should be a copy self.accept('space', self.initNodePath, [dnaNode, 'space']) self.accept('insert', self.initNodePath, [dnaNode, 'insert']) def setRandomBuildingStyle(self, dnaNode, name = 'building'): """ Initialize a new DNA Flat building to a random building style """ # What is the current building type? buildingType = self.getCurrent('building_type') # If random if buildingType == 'random': # Generate height list based on current building height buildingHeight = self.getCurrent('building_height') heightList = self.getRandomHeightList(buildingHeight) # Convert height list to building type buildingType = createHeightCode(heightList) else: # Use specified height list heightList = map(string.atof, string.split(buildingType, '_')) height = calcHeight(heightList) # Is this a never before seen height list? If so, record it. try: attr = self.getAttribute(`height` + '_ft_wall_heights') if heightList not in attr.getList(): print 'Adding new height list entry' attr.add(heightList) except KeyError: print 'Non standard height building' # See if this building type corresponds to existing style dict try: dict = self.getDict(buildingType + '_styles') except KeyError: # Nope dict = {} # No specific dict or empty dict, try to pick a dict # based on number of walls if not dict: # How many walls? numWalls = len(heightList) # Get the building_style attribute dictionary for # this number of walls dict = self.getDict(`numWalls` + '_wall_styles') if not dict: # Still no dict, create new random style using height list styleList = [] # Build up wall styles for height in heightList: wallStyle = self.getRandomDictionaryEntry( self.getDict('wall_style')) styleList.append((wallStyle, height)) # Create new random flat building style style = DNAFlatBuildingStyle(styleList = styleList) else: # Pick a style style = self.getRandomDictionaryEntry(dict) # Set style....finally self.styleManager.setDNAFlatBuildingStyle( dnaNode, style, width = self.getRandomWallWidth(), heightList = heightList, name = name) def getRandomHeightList(self, buildingHeight): # Select a list of wall heights heightLists = self.getList(`buildingHeight` + '_ft_wall_heights') l = len(heightLists) if l > 0: # If a list exists for this building height, pick randomly return heightLists[randint(0,l - 1)] else: # No height lists exists for this building height, generate chance = randint(0,100) if buildingHeight <= 10: return [buildingHeight] elif buildingHeight <= 20: if chance <= 30: return [20] elif chance <= 80: return [10, 10] else: return [12, 8] elif buildingHeight <= 25: if chance <= 25: return [3, 22] elif chance <= 50: return [4, 21] elif chance <= 75: return [3, 13, 9] else: return [4, 13, 8] else: if chance <= 20: return [10, 20] elif chance <= 35: return [20, 10] elif chance <= 75: return [10, 10, 10] else: return [13, 9, 8] def getRandomWallWidth(self): chance = randint(0,100) if chance <= 15: return 5.0 elif chance <= 30: return 10.0 elif chance <= 65: return 15.0 elif chance <= 85: return 20.0 else: return 25.0 def initNodePath(self, dnaNode, hotKey = None): """ Update DNA to reflect latest style choices and then generate new node path and add it to the scene graph """ # Determine dnaNode Class Type nodeClass = DNAGetClassType(dnaNode) # Did the user hit insert or space? if hotKey: # Yes, make a new copy of the dnaNode dnaNode = dnaNode.__class__(dnaNode) # And determine dnaNode type and perform any type specific updates if nodeClass.eq(DNA_PROP): dnaNode.setCode(self.getCurrent('prop_texture')) elif nodeClass.eq(DNA_STREET): dnaNode.setCode(self.getCurrent('street_texture')) elif nodeClass.eq(DNA_FLAT_BUILDING): # If insert, pick a new random style if hotKey == 'insert': self.setRandomBuildingStyle(dnaNode, dnaNode.getName()) # Get a new building width self.setCurrent('building_width', self.getRandomWallWidth()) dnaNode.setWidth(self.getCurrent('building_width')) # Position it # First kill autoposition task so grid can jump to its final # destination (part of cleanup taskMgr.remove('autoPositionGrid') # Now find where to put node path if (hotKey is not None) and nodeClass.eq(DNA_PROP): # If its a prop and a copy, place it based upon current # mouse position hitPt = self.getGridIntersectionPoint() # Attach a node, so we can set pos with respect to: tempNode = hidden.attachNewNode('tempNode') # Place it: tempNode.setPos(direct.grid, hitPt) # Copy the pos to where we really want it: dnaNode.setPos(tempNode.getPos()) # Clean up: tempNode.removeNode() else: # Place the new node path at the current grid origin dnaNode.setPos(direct.grid.getPos()) # Initialize angle to match last object dnaNode.setHpr(Vec3(self.getLastAngle(), 0, 0)) # Add the DNA to the active parent self.DNAParent.add(dnaNode) # And create the geometry newNodePath = dnaNode.traverse(self.NPParent, DNASTORE, 1) # Update scene graph explorer # self.panel.sceneGraphExplorer.update() # Reset last Code (for autoPositionGrid) if DNAClassEqual(dnaNode, DNA_STREET): self.snapList = OBJECT_SNAP_POINTS[dnaNode.getCode()] # Select the instance direct.select(newNodePath) # Update grid to get ready for the next object self.autoPositionGrid() def addGroup(self, nodePath): """ Add a new DNA Node Group to the specified Node Path """ # Set the node path as the current parent direct.setActiveParent(nodePath) # Add a new group to the selected parent self.createNewGroup() def addVisGroup(self, nodePath): """ Add a new DNA Group to the specified Node Path """ # Set the node path as the current parent direct.setActiveParent(nodePath) # Add a new group to the selected parent self.createNewGroup(type = 'vis') def createNewGroup(self, type = 'dna'): """ Create a new DNA Node group under the active parent """ # Create a new DNA Node group if type == 'dna': newDNANode = DNANode('group_' + `self.getGroupNum()`) else: newDNANode = DNAVisGroup('VisGroup_' + `self.getGroupNum()`) # Increment group counter self.setGroupNum(self.getGroupNum() + 1) # Add new DNA Node group to the current parent DNA Object self.DNAParent.add(newDNANode) # The new Node group becomes the active parent self.DNAParent = newDNANode # Traverse it to generate the new node path as a child of NPParent newNodePath = self.DNAParent.traverse(self.NPParent, DNASTORE, 1) # Update NPParent to point to the new node path self.NPParent = newNodePath # Update scene graph explorer # self.panel.sceneGraphExplorer.update() def addFlatBuilding(self, buildingType): # Create new building newDNAFlatBuilding = DNAFlatBuilding() self.setRandomBuildingStyle(newDNAFlatBuilding, name = 'tb0:'+ buildingType + '_DNARoot') # Now place new building in the world self.initDNANode(newDNAFlatBuilding) def getNextLandmarkBlock(self): self.landmarkBlock=self.landmarkBlock+1 return str(self.landmarkBlock) def addLandmark(self, landmarkType, hq): # Record new landmark type self.setCurrent('toon_landmark_texture', landmarkType) # And create new landmark building block=self.getNextLandmarkBlock() newDNALandmarkBuilding = DNALandmarkBuilding( 'tb'+block+':'+landmarkType + '_DNARoot') newDNALandmarkBuilding.setCode(landmarkType) newDNALandmarkBuilding.setHq(hq) newDNALandmarkBuilding.setPos(VBase3(0)) newDNALandmarkBuilding.setHpr(VBase3(0)) # Headquarters do not have doors if not hq: newDNADoor = self.createDoor('landmark_door') newDNALandmarkBuilding.add(newDNADoor) # Now place new landmark building in the world self.initDNANode(newDNALandmarkBuilding) def addProp(self, propType): # Record new prop type self.setCurrent('prop_texture', propType) # And create new prop newDNAProp = DNAProp(propType + '_DNARoot') newDNAProp.setCode(propType) newDNAProp.setPos(VBase3(0)) newDNAProp.setHpr(VBase3(0)) # Now place new prop in the world self.initDNANode(newDNAProp) def addStreet(self, streetType): # Record new street type self.setCurrent('street_texture', streetType) # And create new street newDNAStreet = DNAStreet(streetType + '_DNARoot') newDNAStreet.setCode(streetType) newDNAStreet.setPos(VBase3(0)) newDNAStreet.setHpr(VBase3(0)) # Set street texture to neighborhood dependant texture newDNAStreet.setStreetTexture( 'street_street_' + self.neighborhoodCode + '_tex') newDNAStreet.setSidewalkTexture( 'street_sidewalk_' + self.neighborhoodCode + '_tex') # Now place new street in the world self.initDNANode(newDNAStreet) def createCornice(self): newDNACornice = DNACornice('cornice') newDNACornice.setCode(self.getCurrent('cornice_texture')) newDNACornice.setColor(self.getCurrent('cornice_color')) return newDNACornice def createDoor(self, type): if not (self.getCurrent('door_texture')): doorStyles = self.styleManager.attributeDictionary['door_texture'].getList()[:-1] defaultDoorStyle = doorStyles[randint(0, len(doorStyles) - 1)] self.setCurrent('door_texture', defaultDoorStyle) if (type == 'landmark_door'): newDNADoor = DNADoor('door') elif (type == 'door'): newDNADoor = DNAFlatDoor('door') newDNADoor.setCode(self.getCurrent('door_texture')) newDNADoor.setColor(self.getCurrent('door_color')) return newDNADoor def createSign(self): if not (self.getCurrent('sign_texture')): defaultSignStyle = self.styleManager.attributeDictionary['sign_texture'].getList()[0] self.setCurrent('sign_texture', defaultSignStyle) newDNASign = DNASign('sign') newDNASign.setCode(self.getCurrent('sign_texture')) newDNASign.setColor(self.getCurrent('sign_color')) #newDNASign.setColor(VBase4(0.0, 1.0, 0.0, 1.0)) #newDNASign.setScale(VBase3(2.0, 1.0, 2.0)) baseline = DNASignBaseline('baseline') baseline.setCode("comic") baseline.setColor(VBase4(0.0, 0.0, 0.0, 1.0)) #baseline.setKern(1.0); #baseline.setWiggle(30.0); #baseline.setStumble(1.0); #baseline.setStomp(10.0); #baseline.setWidth(16.0) #baseline.setHeight(16.0) baseline.setScale(VBase3(0.7, 1.0, 0.7)) newDNASign.add(baseline) #graphic = DNASignGraphic('graphic') #graphic.setCode("sign_general1") #graphic.setColor(VBase4(1.0, 1.0, 0.0, 0.5)) #graphic.setScale(VBase3(.2, 1.0, .2)) #graphic.setHpr(VBase3(0.0, 0.0, 90.0)) #baseline.add(graphic) DNASetBaselineString(baseline, "Toon Shop") return newDNASign def createWindows(self): newDNAWindows = DNAWindows() newDNAWindows.setCode(self.getCurrent('window_texture')) newDNAWindows.setWindowCount(self.getCurrent('window_count')) newDNAWindows.setColor(self.getCurrent('window_color')) return newDNAWindows def removeCornice(self, cornice, parent): self.setCurrent('cornice_color', cornice.getColor()) DNARemoveChildOfClass(parent, DNA_CORNICE) def removeLandmarkDoor(self, door, parent): self.setCurrent('door_color', door.getColor()) DNARemoveChildOfClass(parent, DNA_DOOR) def removeSign(self, sign, parent): self.setCurrent('sign_color', sign.getColor()) DNARemoveChildOfClass(parent, DNA_SIGN) def removeDoor(self, door, parent): self.setCurrent('door_color', door.getColor()) DNARemoveChildOfClass(parent, DNA_FLAT_DOOR) def removeWindows(self, windows, parent): # And record number of windows self.setCurrent('window_color', windows.getColor()) self.setCurrent('window_count', windows.getWindowCount()) DNARemoveChildOfClass(parent, DNA_WINDOWS) # LEVEL-OBJECT MODIFICATION FUNCTIONS def levelHandleMouse3(self, modifiers): # Initialize dna target self.DNATarget = None # If nothing selected, just return if not self.selectedNPRoot: return # Next check to see if the selected object is a DNA object dnaObject = self.findDNANode(self.selectedNPRoot) # Nope, not a DNA object, just return if not dnaObject: return # Pick a menu based upon object type if DNAClassEqual(dnaObject, DNA_FLAT_BUILDING): # FLAT BUILDING OPERATIONS menuMode, wallNum = self.getFlatBuildingMode(dnaObject, modifiers) # Check menuMode if menuMode == None: return # Find appropriate target wall = self.getWall(dnaObject, wallNum) # Record bldg/wall self.lastBuilding = dnaObject self.lastWall = wall if string.find(menuMode,'wall') >= 0: self.DNATarget = wall self.DNATargetParent = dnaObject elif string.find(menuMode,'door') >= 0: self.DNATarget = DNAGetChildOfClass(wall, DNA_FLAT_DOOR) self.DNATargetParent = wall elif string.find(menuMode, 'window') >= 0: self.DNATarget = DNAGetChildOfClass(wall, DNA_WINDOWS) self.DNATargetParent = wall elif string.find(menuMode,'cornice') >= 0: self.DNATarget = DNAGetChildOfClass(wall, DNA_CORNICE) self.DNATargetParent = wall else: self.DNATarget = dnaObject elif DNAClassEqual(dnaObject, DNA_PROP): # PROP OPERATIONS self.DNATarget = dnaObject if direct.gotControl(modifiers): menuMode = 'prop_color' elif direct.gotAlt(modifiers) and self.panel.currentBaselineDNA: menuMode = 'baseline_style' elif direct.gotShift(modifiers): menuMode = 'sign_texture' self.DNATarget = DNAGetChildOfClass(dnaObject, DNA_SIGN) self.DNATargetParent = dnaObject else: menuMode = 'prop_texture' elif DNAClassEqual(dnaObject, DNA_LANDMARK_BUILDING): # INSERT HERE # LANDMARK BUILDING OPERATIONS menuMode = self.getLandmarkBuildingMode(dnaObject, modifiers) if string.find(menuMode, 'door') >= 0: self.DNATarget = DNAGetChildOfClass(dnaObject, DNA_DOOR) self.DNATargetParent = dnaObject elif string.find(menuMode, 'sign') >= 0: self.DNATarget = DNAGetChildOfClass(dnaObject, DNA_SIGN) self.DNATargetParent = dnaObject else: self.DNATarget = dnaObject elif DNAClassEqual(dnaObject, DNA_STREET): # STREET OPERATIONS self.DNATarget = dnaObject menuMode = 'street_texture' # No valid menu mode, get the hell out of here! if menuMode == None: return # Now spawn apropriate menu task if menu selected self.activeMenu = self.getMenu(menuMode) # Set initial state state = None if self.DNATarget: if string.find(menuMode,'texture') >= 0: state = self.DNATarget.getCode() elif string.find(menuMode, 'color') >= 0: state = self.DNATarget.getColor() self.panel.setCurrentColor(state) self.panel.setResetColor(state) elif string.find(menuMode, 'orientation') >= 0: state = self.DNATarget.getCode()[-2:] elif menuMode == 'building_width': state = self.DNATarget.getWidth() elif menuMode == 'window_count': state = self.DNATarget.getWindowCount() elif menuMode == 'building_style_all': # Extract the building style from the current building state = DNAFlatBuildingStyle(building = self.DNATarget) elif menuMode == 'baseline_style': # Extract the baseline style state = DNABaselineStyle( baseline = self.panel.currentBaselineDNA) elif menuMode == 'wall_style': # Extract the wall style from the current wall state = DNAWallStyle(wall = self.DNATarget) self.activeMenu.setInitialState(state) # Spawn active menu's task self.activeMenu.spawnPieMenuTask() def getLandmarkBuildingMode(self, dnaObject, modifiers): # Where are we hitting the building? hitPt = self.getWallIntersectionPoint(self.selectedNPRoot) if hitPt[2] < 10.0: # Do door operations if direct.gotControl(modifiers): menuMode = 'door_color' elif direct.gotAlt(modifiers): menuMode = 'door_orientation' else: menuMode = 'door_texture' else: # Do sign operations if direct.gotControl(modifiers): menuMode = 'sign_color' elif direct.gotAlt(modifiers) and self.panel.currentBaselineDNA: menuMode = 'baseline_style' elif direct.gotAlt(modifiers): menuMode = 'sign_orientation' else: menuMode = 'sign_texture' return menuMode def getFlatBuildingMode(self, dnaObject, modifiers): # Where are we hitting the building? hitPt = self.getWallIntersectionPoint(self.selectedNPRoot) wallNum = self.computeWallNum(dnaObject, hitPt) if wallNum < 0: # Do building related operations if direct.gotAlt(modifiers): menuMode = 'building_width' else: menuMode = 'building_style_all' else: # Otherwise, do wall specific operations # Figure out where you are hitting on the wall wallHeights, offsetList = DNAGetWallHeights(dnaObject) # Find a normalized X and Z coordinate xPt = hitPt[0]/dnaObject.getWidth() # Adjust zPt depending on what wall you are pointing at wallHeight = wallHeights[wallNum] zPt = (hitPt[2] - offsetList[wallNum])/wallHeight # Record current wall height self.setCurrent('wall_height', wallHeight) # Determine which zone you are pointing at if (zPt > 0.8): # Do cornice operations if direct.gotControl(modifiers): menuMode = 'cornice_color' elif direct.gotAlt(modifiers): menuMode = 'cornice_orientation' else: menuMode = 'cornice_texture' elif ((xPt < 0.3) or (xPt > 0.7)): # Do wall operations if direct.gotControl(modifiers): menuMode = 'wall_color' elif direct.gotAlt(modifiers): menuMode = 'wall_orientation' elif direct.gotShift(modifiers): menuMode = 'wall_texture' else: menuMode = 'wall_style' elif (zPt < 0.4): # Do door operations if direct.gotControl(modifiers): menuMode = 'door_color' elif direct.gotAlt(modifiers): menuMode = 'door_orientation' else: menuMode = 'door_texture' else: # Do window operations if direct.gotControl(modifiers): menuMode = 'window_color' elif direct.gotAlt(modifiers): menuMode = 'window_orientation' elif direct.gotShift(modifiers): menuMode = 'window_count' else: menuMode = 'window_texture' return menuMode, wallNum def levelHandleMouse3Up(self): if self.activeMenu: self.activeMenu.removePieMenuTask() # Update panel color if appropriate if self.DNATarget: objClass = DNAGetClassType(self.DNATarget) if ((objClass.eq(DNA_WALL)) or (objClass.eq(DNA_WINDOWS)) or (objClass.eq(DNA_DOOR)) or (objClass.eq(DNA_FLAT_DOOR)) or (objClass.eq(DNA_CORNICE)) or (objClass.eq(DNA_PROP)) ): self.panel.setCurrentColor(self.DNATarget.getColor()) def setDNATargetColor(self, color): if self.DNATarget: self.DNATarget.setColor(color) self.replaceSelected() def setDNATargetCode(self, type, code): if (self.DNATarget != None) and (code != None): # Update code self.DNATarget.setCode(code) elif (self.DNATarget != None) and (code == None): # Delete object, record pertinant properties before # you delete the object so you can restore them later # Remove object if (type == 'cornice'): self.removeCornice(self.DNATarget, self.DNATargetParent) elif (type == 'sign'): self.removeSign(self.DNATarget, self.DNATargetParent) elif (type == 'landmark_door'): self.removeLandmarkDoor(self.DNATarget, self.DNATargetParent) elif (type == 'door'): self.removeDoor(self.DNATarget, self.DNATargetParent) elif (type == 'windows'): self.removeWindows(self.DNATarget, self.DNATargetParent) # Clear out DNATarget self.DNATarget = None elif (self.DNATarget == None) and (code != None): # Add new object if (type == 'cornice'): self.DNATarget = self.createCornice() elif (type == 'sign'): self.DNATarget = self.createSign() elif (type == 'landmark_door'): self.DNATarget = self.createDoor('landmark_door') elif (type == 'door'): self.DNATarget = self.createDoor('door') elif (type == 'windows'): # Make sure window_count n.e. 0 if self.getCurrent('window_count') == 0: self.setCurrent( 'window_count', self.getRandomWindowCount()) # Now create the windows self.DNATarget = self.createWindows() if self.DNATarget: self.DNATargetParent.add(self.DNATarget) # Update visible representation self.replaceSelected() def setDNATargetOrientation(self, orientation): if (self.DNATarget != None) and (orientation != None): oldCode = self.DNATarget.getCode()[:-2] self.DNATarget.setCode(oldCode+orientation) self.replaceSelected() def setBuildingStyle(self, style): if (self.DNATarget != None) and (style != None): self.styleManager.setDNAFlatBuildingStyle( self.DNATarget, style, width = self.DNATarget.getWidth(), name = self.DNATarget.getName()) # MRM: Need to disable dna store warning self.replaceSelected() # Re-add replication hooks so we get right kind of copy self.addReplicationHooks(self.DNATarget) def setBuildingType(self, type): print 'setBuildingType: ', `type` def setBuildingWidth(self, width): if self.DNATarget: self.DNATarget.setWidth(width) self.replaceSelected() def setWindowCount(self, count): if (self.DNATarget != None) and (count != 0): self.DNATarget.setWindowCount(count) elif (self.DNATarget != None) and (count == 0): # Remove windows and clear out DNATarget self.removeWindows(self.DNATarget, self.DNATargetParent) self.DNATarget = None elif (self.DNATarget == None) and (count != 0): self.DNATarget = self.createWindows() self.DNATargetParent.add(self.DNATarget) self.replaceSelected() def setWallStyle(self, style): if (self.DNATarget != None) and (style != None): self.styleManager.setDNAWallStyle( self.DNATarget, style, self.DNATarget.getHeight()) self.replaceSelected() def setColor(self, nodePath, r,g,b,a): """ This is used to set color of dnaNode subparts """ dnaNode = self.findDNANode(nodePath) if dnaNode: objClass = DNAGetClassType(dnaNode) if ((objClass.eq(DNA_WALL)) or (objClass.eq(DNA_WINDOWS)) or (objClass.eq(DNA_DOOR)) or (objClass.eq(DNA_FLAT_DOOR)) or (objClass.eq(DNA_CORNICE)) or (objClass.eq(DNA_PROP)) or (objClass.eq(DNA_SIGN)) or (objClass.eq(DNA_SIGN_BASELINE)) or (objClass.eq(DNA_SIGN_TEXT)) or (objClass.eq(DNA_SIGN_GRAPHIC)) ): # Update dna information dnaNode.setColor(VBase4(r/255.0, g/255.0, b/255.0, a/255.0)) # SELECTION FUNCTIONS def selectedNodePathHook(self, nodePath): """ Hook called upon selection of a node path used to restrict selection to DNA Objects. Press control to select any type of DNA Object, with no control key pressed, hook selects only DNA Root objects """ # Clear out old root variables self.selectedDNARoot = None self.selectedNPRoot = None self.selectedSuitPoint = None # Now process newly selected node path dnaParent = None dnaNode = self.findDNANode(nodePath) if direct.fControl: # Is the current node a DNA Object? if not dnaNode: # No it isn't, look for a parent DNA object dnaParent = self.findDNAParent(nodePath.getParent()) else: # Is the current node a DNA Root object? if nodePath.getName()[-8:] != '_DNARoot': # No it isn't, look for a parent DNA Root object dnaParent = self.findDNARoot(nodePath.getParent()) # Do we need to switch selection to a parent object? if dnaParent: # Yes, deselect currently selected node path direct.deselect(nodePath) # And select parent direct.select(dnaParent, direct.fShift) elif dnaNode: # We got a valid node path/DNA object, continue self.selectedNPRoot = nodePath self.selectedDNARoot = dnaNode # Reset last landmark if DNAClassEqual(dnaNode, DNA_LANDMARK_BUILDING): self.lastLandmarkBuildingDNA=dnaNode if self.showLandmarkBlockToggleGroup: # Toggle old highlighting off: self.toggleShowLandmarkBlock() # Toggle on the the new highlighting: self.toggleShowLandmarkBlock() # Reset last Code (for autoPositionGrid) if DNAClassEqual(dnaNode, DNA_STREET): self.snapList = OBJECT_SNAP_POINTS[dnaNode.getCode()] else: pointOrCell, type = self.findPointOrCell(nodePath) if pointOrCell and (type == 'suitPointMarker'): print "Found suit point!", pointOrCell self.selectedSuitPoint = pointOrCell if pointOrCell and (type == 'battleCellMarker'): print "Found battle cell!", pointOrCell # Let others know that something new may be selected: for i in self.selectedNodePathHookHooks: i() def deselectedNodePathHook(self, nodePath): # Clear out old root variables self.selectedDNARoot = None self.selectedNPRoot = None self.selectedSuitPoint = None # Let others know: for i in self.deselectedNodePathHookHooks: i() def findDNAParent(self, nodePath): """ Walk up a node path's ancestry looking for its DNA Root """ # Check to see if current node is a dna object if self.findDNANode(nodePath): # Its a root! return nodePath else: # If reached the top: fail if not nodePath.hasParent(): return 0 else: # Try parent return self.findDNAParent(nodePath.getParent()) def findDNARoot(self, nodePath): """ Walk up a node path's ancestry looking for its DNA Root """ # Check current node's name for root marker if (nodePath.getName()[-8:] == '_DNARoot'): # Its a root! return nodePath else: # If reached the top: fail if not nodePath.hasParent(): return None else: # Try parent return self.findDNARoot(nodePath.getParent()) def findPointOrCell(self, nodePath): """ Walk up a node path's ancestry to see if its a suit point marker or a battle cell marker """ # Check current node's name for root marker if (nodePath.getName() == 'suitPointMarker'): # Its a suit point marker! # See if point is in pointDict point = self.getSuitPointFromNodePath(nodePath) return point, 'suitPointMarker' elif (nodePath.getName() == 'battleCellMarker'): # Its a battle cell marker # See if cell is in cell Dict cell = self.getBattleCellFromNodePath(nodePath) return cell, 'battleCellMarker' else: # If reached the top: fail if not nodePath.hasParent(): return None, None else: # Try parent return self.findPointOrCell(nodePath.getParent()) # MANIPULATION FUNCTIONS def keyboardRotateSelected(self, arrowDirection): """ Rotate selected objects using arrow keys """ # Get snap angle if direct.fShift: oldSnapAngle = direct.grid.snapAngle direct.grid.setSnapAngle(1.0) snapAngle = direct.grid.snapAngle # Compute new angle if ((arrowDirection == 'left') or (arrowDirection == 'up')): self.setLastAngle(self.getLastAngle() + snapAngle) else: self.setLastAngle(self.getLastAngle() - snapAngle) if (self.getLastAngle() < -180.0): self.setLastAngle(self.getLastAngle() + 360.0) elif (self.getLastAngle() > 180.0): self.setLastAngle(self.getLastAngle() - 360.0) # Move selected objects for selectedNode in direct.selected: selectedNode.setHpr(self.getLastAngle(), 0, 0) # Snap objects to grid and update DNA if necessary self.updateSelectedPose() if direct.fShift: direct.grid.setSnapAngle(oldSnapAngle) def keyboardTranslateSelected(self, arrowDirection): gridToCamera = direct.grid.getMat(direct.camera) camXAxis = gridToCamera.xformVec(X_AXIS) xxDot = camXAxis.dot(X_AXIS) xzDot = camXAxis.dot(Z_AXIS) # what is the current grid spacing? if direct.fShift: # If shift, divide grid spacing by 10.0 oldGridSpacing = direct.grid.gridSpacing # Use back door to set grid spacing to avoid grid update direct.grid.gridSpacing = direct.grid.gridSpacing/10.0 deltaMove = direct.grid.gridSpacing # Compute the specified delta deltaPos = Vec3(0) if (abs(xxDot) > abs(xzDot)): if (xxDot < 0.0): deltaMove = -deltaMove # Compute delta if (arrowDirection == 'right'): deltaPos.setX(deltaPos[0] + deltaMove) elif (arrowDirection == 'left'): deltaPos.setX(deltaPos[0] - deltaMove) elif (arrowDirection == 'up'): deltaPos.setY(deltaPos[1] + deltaMove) elif (arrowDirection == 'down'): deltaPos.setY(deltaPos[1] - deltaMove) else: if (xzDot < 0.0): deltaMove = -deltaMove # Compute delta if (arrowDirection == 'right'): deltaPos.setY(deltaPos[1] - deltaMove) elif (arrowDirection == 'left'): deltaPos.setY(deltaPos[1] + deltaMove) elif (arrowDirection == 'up'): deltaPos.setX(deltaPos[0] + deltaMove) elif (arrowDirection == 'down'): deltaPos.setX(deltaPos[0] - deltaMove) # Move selected objects for selectedNode in direct.selected: # Move it selectedNode.setPos(direct.grid, selectedNode.getPos(direct.grid) + deltaPos) # Snap objects to grid and update DNA if necessary self.updateSelectedPose() # Restore grid spacing if direct.fShift: # Use back door to set grid spacing to avoid grid update direct.grid.gridSpacing = oldGridSpacing def keyboardXformSelected(self, arrowDirection, mode): if mode == 'rotate': self.keyboardRotateSelected(arrowDirection) else: self.keyboardTranslateSelected(arrowDirection) # VISIBILITY FUNCTIONS def editDNAVisGroups(self): visGroups = self.getDNAVisGroups(self.NPToplevel) if visGroups: self.vgpanel = VisGroupsEditor(self, visGroups) else: showinfo('Vis Groups Editor','No DNA Vis Groups Found!') def getDNAVisGroups(self, nodePath): """ Find the highest level vis groups in the scene graph """ dnaNode = self.findDNANode(nodePath) if dnaNode: if DNAClassEqual(dnaNode, DNA_VIS_GROUP): return [[nodePath, dnaNode]] childVisGroups = [] children = nodePath.getChildrenAsList() for child in children: childVisGroups = (childVisGroups + self.getDNAVisGroups(child)) return childVisGroups def findParentVisGroup(self, nodePath): """ Find the containing vis group """ dnaNode = self.findDNANode(nodePath) if dnaNode: if DNAClassEqual(dnaNode, DNA_VIS_GROUP): return nodePath, dnaNode elif nodePath.hasParent(): return self.findParentVisGroup(nodePath.getParent()) else: return None def showGrid(self,flag): """ toggle direct grid """ if flag: direct.grid.enable() else: direct.grid.disable() # LEVEL MAP/MARKER FUNCTIONS def createLevelMaps(self): """ Load up the various neighborhood maps """ # For neighborhood maps self.levelMap = hidden.attachNewNode('level-map') self.activeMap = None self.mapDictionary = {} """ for neighborhood in NEIGHBORHOODS: self.createMap(neighborhood) """ def createMap(self, neighborhood): map = loader.loadModel('models/level_editor/' + neighborhood + '_layout') if map: map.setTransparency(1) map.setColor(Vec4(1,1,1,.4)) self.mapDictionary[neighborhood] = map # Make sure this item isn't pickable direct.addUnpickable(neighborhood + '_layout') def selectMap(self, neighborhood): if self.activeMap: self.activeMap.reparentTo(hidden) if self.mapDictionary.has_key(neighborhood): self.activeMap = self.mapDictionary[neighborhood] self.activeMap.reparentTo(self.levelMap) def toggleMapVis(self, flag): if flag: self.levelMap.reparentTo(direct.group) else: self.levelMap.reparentTo(hidden) def createInsertionMarker(self): self.insertionMarker = LineNodePath(self) self.insertionMarker.lineNode.setName('insertionMarker') self.insertionMarker.setColor(VBase4(0.785, 0.785, 0.5,1)) self.insertionMarker.setThickness(1) self.insertionMarker.reset() self.insertionMarker.moveTo(-75,0,0) self.insertionMarker.drawTo(75,0,0) self.insertionMarker.moveTo(0,-75,0) self.insertionMarker.drawTo(0,75,0) self.insertionMarker.moveTo(0,0,-75) self.insertionMarker.drawTo(0,0,75) self.insertionMarker.create() def spawnInsertionMarkerTask(self): taskMgr.add(self.insertionMarkerTask, 'insertionMarkerTask') def insertionMarkerTask(self, state): self.insertionMarker.setPosHpr(direct.grid, 0,0,0, 0,0,0) # MRM: Why is this necessary? self.insertionMarker.setScale(1,1,1) return Task.cont # UTILITY FUNCTIONS def getRandomDictionaryEntry(self,dict): numKeys = len(dict) if numKeys > 0: keys = dict.keys() key = keys[randint(0,numKeys - 1)] return dict[key] else: return None def getRandomWindowCount(self): if ((self.lastWall != None) and (self.lastBuilding != None)): h = ROUND_INT(self.lastWall.getHeight()) w = ROUND_INT(self.lastBuilding.getWidth()) # Otherwise.... if w == 5: # 5 ft walls can have 1 window return 1 elif h == 10: # All other 10 ft high bldgs can have 1 or 2 return randint(1,2) else: # All others can have 1 - 4 return randint(1,4) else: return 1 def autoPositionGrid(self): taskMgr.remove('autoPositionGrid') # Move grid to prepare for placement of next object selectedNode = direct.selected.last if selectedNode: dnaNode = self.findDNANode(selectedNode) if dnaNode == None: return nodeClass = DNAGetClassType(dnaNode) deltaPos = Point3(20,0,0) deltaHpr = VBase3(0) if nodeClass.eq(DNA_FLAT_BUILDING): deltaPos.setX(dnaNode.getWidth()) elif nodeClass.eq(DNA_STREET): objectCode = dnaNode.getCode() deltas = self.getNextSnapPoint() deltaPos.assign(deltas[0]) deltaHpr.assign(deltas[1]) elif nodeClass.eq(DNA_LANDMARK_BUILDING): objectCode = dnaNode.getCode() if objectCode[-2:-1] == 'A': deltaPos.setX(25.0) elif objectCode[-2:-1] == 'B': deltaPos.setX(15.0) elif objectCode[-2:-1] == 'C': deltaPos.setX(20.0) # Position grid for placing next object # Eventually we need to setHpr too t = direct.grid.lerpPosHpr( deltaPos, deltaHpr, 0.25, other = selectedNode, blendType = 'easeInOut', task = 'autoPositionGrid') t.deltaPos = deltaPos t.deltaHpr = deltaHpr t.selectedNode = selectedNode t.uponDeath = self.autoPositionCleanup # Also move the camera taskMgr.remove('autoMoveDelay') handlesToCam = direct.widget.getPos(direct.camera) handlesToCam = handlesToCam * ( direct.dr.near/handlesToCam[1]) if ((abs(handlesToCam[0]) > (direct.dr.nearWidth * 0.4)) or (abs(handlesToCam[2]) > (direct.dr.nearHeight * 0.4))): taskMgr.remove('manipulateCamera') direct.cameraControl.centerCamIn(0.5) def autoPositionCleanup(self,state): direct.grid.setPosHpr(state.selectedNode, state.deltaPos, state.deltaHpr) if direct.grid.getHprSnap(): # Clean up grid angle direct.grid.setH(ROUND_TO(direct.grid.getH(), direct.grid.snapAngle)) def getNextSnapPoint(self): """ Pull next pos hpr deltas off of snap list then rotate list """ if self.snapList: deltas = self.snapList[0] # Rotate list by one self.snapList = self.snapList[1:] + self.snapList[:1] return deltas else: return (ZERO_VEC, ZERO_VEC) def getWallIntersectionPoint(self, selectedNode): """ Return point of intersection between building's wall and line from cam through mouse. """ # Find mouse point on near plane mouseX = direct.dr.mouseX mouseY = direct.dr.mouseY nearX = (math.tan(deg2Rad(direct.dr.fovH)/2.0) * mouseX * direct.dr.near) nearZ = (math.tan(deg2Rad(direct.dr.fovV)/2.0) * mouseY * direct.dr.near) # Initialize points mCam2Wall = direct.camera.getMat(selectedNode) mouseOrigin = Point3(0) mouseOrigin.assign(mCam2Wall.getRow3(3)) mouseDir = Vec3(0) mouseDir.set(nearX, direct.dr.near, nearZ) mouseDir.assign(mCam2Wall.xformVec(mouseDir)) # Calc intersection point return planeIntersect(mouseOrigin, mouseDir, ZERO_POINT, NEG_Y_AXIS) def getGridSnapIntersectionPoint(self): """ Return point of intersection between ground plane and line from cam through mouse. Return false, if nothing selected. Snap to grid. """ return direct.grid.computeSnapPoint(self.getGridIntersectionPoint()) def getGridIntersectionPoint(self): """ Return point of intersection between ground plane and line from cam through mouse. Return false, if nothing selected """ # Find mouse point on near plane mouseX = direct.dr.mouseX mouseY = direct.dr.mouseY nearX = (math.tan(deg2Rad(direct.dr.fovH)/2.0) * mouseX * direct.dr.near) nearZ = (math.tan(deg2Rad(direct.dr.fovV)/2.0) * mouseY * direct.dr.near) # Initialize points mCam2Grid = direct.camera.getMat(direct.grid) mouseOrigin = Point3(0) mouseOrigin.assign(mCam2Grid.getRow3(3)) mouseDir = Vec3(0) mouseDir.set(nearX, direct.dr.near, nearZ) mouseDir.assign(mCam2Grid.xformVec(mouseDir)) # Calc intersection point return planeIntersect(mouseOrigin, mouseDir, ZERO_POINT, Z_AXIS) def jumpToInsertionPoint(self): """ Move selected object to insertion point """ selectedNode = direct.selected.last if selectedNode: # Check if its a dna node dnaNode = self.findDNANode(selectedNode) if dnaNode: # Place the new node path at the current grid origin selectedNode.setPos(direct.grid,0,0,0) # Initialize angle to match last object selectedNode.setHpr(self.getLastAngle(), 0, 0) # Now update DNA pos and hpr to reflect final pose dnaNode.setPos(selectedNode.getPos()) dnaNode.setHpr(selectedNode.getHpr()) # Update grid to get ready for the next object self.autoPositionGrid() # STYLE/DNA FILE FUNCTIONS def loadSpecifiedDNAFile(self): path = dnaDirectory.toOsSpecific() if not os.path.isdir(path): print 'LevelEditor Warning: Invalid default DNA directory!' print 'Using current directory' path = '.' dnaFilename = askopenfilename( defaultextension = '.dna', filetypes = (('DNA Files', '*.dna'),('All files', '*')), initialdir = path, title = 'Load DNA File', parent = self.panel.component('hull')) if dnaFilename: self.loadDNAFromFile(dnaFilename) def saveToSpecifiedDNAFile(self): path = dnaDirectory.toOsSpecific() if not os.path.isdir(path): print 'LevelEditor Warning: Invalid DNA save directory!' print 'Using current directory' path = '.' dnaFilename = asksaveasfilename( defaultextension = '.dna', filetypes = (('DNA Files', '*.dna'),('All files', '*')), initialdir = path, title = 'Save DNA File as', parent = self.panel.component('hull')) if dnaFilename: self.outputDNA(dnaFilename) def loadDNAFromFile(self, filename): print filename # Reset level, destroying existing scene/DNA hierarcy self.reset(fDeleteToplevel = 1, fCreateToplevel = 0, fUpdateExplorer = 0) # Now load in new file node = loadDNAFile(DNASTORE, filename, CSDefault, 1) if node.getNumParents() == 1: # If the node already has a parent arc when it's loaded, we must # be using the level editor and we want to preserve that arc. newNPToplevel = NodePath(node) newNPToplevel.reparentTo(hidden) else: # Otherwise, we should create a new arc for the node. newNPToplevel = hidden.attachNewNode(node) # Make sure the topmost file DNA object gets put under DNARoot newDNAToplevel = self.findDNANode(newNPToplevel) # reset the landmark block number: (self.landmarkBlock, needTraverse)=self.findHighestLandmarkBlock( newDNAToplevel, newNPToplevel) # Update toplevel variables if needTraverse: self.createToplevel(newDNAToplevel) else: self.createToplevel(newDNAToplevel, newNPToplevel) # Create visible representations of all the paths and battle cells self.createSuitPaths() self.hideSuitPaths() self.createBattleCells() self.hideBattleCells() # Set the title bar to have the filename to make it easier # to remember what file you are working on self.panel["title"] = 'Level Editor: ' + os.path.basename(filename) self.panel.sceneGraphExplorer.update() def outputDNADefaultFile(self): file = os.path.join(dnaDirectory.toOsSpecific(), self.outputFile) self.outputDNA(file) def outputDNA(self,filename): print 'Saving DNA to: ', filename binaryFilename = Filename(filename) binaryFilename.setBinary() self.DNAData.writeDna(binaryFilename, Notify.out(),DNASTORE) def saveColor(self): self.appendColorToColorPaletteFile(self.panel.colorEntry.get()) def appendColorToColorPaletteFile(self, color): obj = self.DNATarget if obj: classType = DNAGetClassType(obj) if classType.eq(DNA_WALL): tag = 'wall_color:' elif classType.eq(DNA_WINDOWS): tag = 'window_color:' elif classType.eq(DNA_DOOR): tag = 'door_color:' elif classType.eq(DNA_FLAT_DOOR): tag = 'door_color:' elif classType.eq(DNA_CORNICE): tag = 'cornice_color:' elif classType.eq(DNA_PROP): tag = 'prop_color:' else: return # Valid type, add color to file filename = self.neighborhood + '_colors.txt' fname = Filename(dnaDirectory.getFullpath() + '/stylefiles/' + filename) f = open(fname.toOsSpecific(), 'ab') f.write('%s Vec4(%.2f, %.2f, %.2f, 1.0)\n' % (tag, color[0]/255.0, color[1]/255.0, color[2]/255.0)) f.close() def saveStyle(self, filename, style): # A generic routine to append a new style definition to one of # the style files. fname = Filename(dnaDirectory.getFullpath() + '/stylefiles/' + filename) # We use binary mode to avoid Windows' end-of-line convention f = open(fname.toOsSpecific(), 'ab') # Add a blank line f.write('\n') # Now output style details to file style.output(f) # Close the file f.close() def saveBaselineStyle(self): if self.panel.currentBaselineDNA: # Valid baseline, add style to file filename = self.neighborhood + '_baseline_styles.txt' style = DNABaselineStyle(self.panel.currentBaselineDNA) self.saveStyle(filename, style) def saveWallStyle(self): if self.lastWall: # Valid wall, add style to file filename = self.neighborhood + '_wall_styles.txt' style = DNAWallStyle(self.lastWall) self.saveStyle(filename, style) def saveBuildingStyle(self): dnaObject = self.selectedDNARoot if dnaObject: if DNAClassEqual(dnaObject, DNA_FLAT_BUILDING): # Valid wall, add style to file filename = self.neighborhood + '_building_styles.txt' style = DNAFlatBuildingStyle(dnaObject) self.saveStyle(filename, style) return print 'Must select building before saving building style' # GET/SET # DNA Object elements def getWall(self, dnaFlatBuilding, wallNum): wallCount = 0 for i in range(dnaFlatBuilding.getNumChildren()): child = dnaFlatBuilding.at(i) if DNAClassEqual(child, DNA_WALL): if wallCount == wallNum: return child wallCount = wallCount + 1 # Not found return None def computeWallNum(self, aDNAFlatBuilding, hitPt): """ Given a hitPt, return wall number if cursor is over building Return -1 if cursor is outside of building """ xPt = hitPt[0] zPt = hitPt[2] # Left or right of building if ((xPt < 0) or (xPt > aDNAFlatBuilding.getWidth())): return -1 # Below the building if zPt < 0: return -1 # Above z = 0 and within wall width, check height of walls heightList, offsetList = DNAGetWallHeights(aDNAFlatBuilding) wallNum = 0 for i in range(len(heightList)): # Compute top of wall segment top = offsetList[i] + heightList[i] if zPt < top: return wallNum wallNum = wallNum + 1 return -1 def getWindowCount(self, dnaWall): windowCount = 0 for i in range(dnaWall.getNumChildren()): child = dnaWall.at(i) if DNAClassEqual(child, DNA_WINDOWS): windowCount = windowCount + 1 # Not found return windowCount # Style manager edit mode def setEditMode(self, neighborhood): self.neighborhood = neighborhood self.neighborhoodCode = NEIGHBORHOOD_CODES[self.neighborhood] self.outputFile = neighborhood + '_working.dna' if neighborhood == 'toontown_central': self.outputDir = 'ToontownCentral' elif neighborhood == 'donalds_dock': self.outputDir = 'DonaldsDock' elif neighborhood == 'minnies_melody_land': self.outputDir = 'MinniesMelodyLand' elif neighborhood == 'the_burrrgh': self.outputDir = 'TheBurrrgh' elif neighborhood == 'daisys_garden': self.outputDir = 'DaisysGarden' elif neighborhood == 'donalds_dreamland': self.outputDir = 'DonaldsDreamland' self.panel.editMenu.selectitem(neighborhood) self.styleManager.setEditMode(neighborhood) self.panel.updateHeightList(self.getCurrent('building_height')) self.selectMap(neighborhood) def getEditMode(self): return self.styleManager.getEditMode() # Level Style Attributes def __getitem__(self,attribute): """ Return top level entry in attribute dictionary """ return self.styleManager.attributeDictionary[attribute] def getAttribute(self, attribute): """ Return specified attribute for current neighborhood """ return self.styleManager.getAttribute(attribute) def hasAttribute(self, attribute): """ Return specified attribute for current neighborhood """ return self.styleManager.hasAttribute(attribute) def getCurrent(self, attribute): """ Return neighborhood's current selection for specified attribute """ return self.getAttribute(attribute).getCurrent() def setCurrent(self, attribute, newCurrent): """ Set neighborhood's current selection for specified attribute """ self.getAttribute(attribute).setCurrent(newCurrent, fEvent = 0) def getMenu(self, attribute): """ Return neighborhood's Pie Menu object for specified attribute """ return self.getAttribute(attribute).getMenu() def getDict(self, attribute): """ Return neighborhood's Dictionary for specified attribute """ return self.getAttribute(attribute).getDict() def getList(self, attribute): """ Return neighborhood's List for specified attribute """ return self.getAttribute(attribute).getList() # DNA variables def getDNAData(self): return self.DNAData def getDNAToplevel(self): return self.DNAToplevel def getDNAParent(self): return self.DNAParent def getDNATarget(self): return self.DNATarget # Node Path variables def getNPToplevel(self): return self.NPToplevel def getNPParent(self): return self.NPParent # Count of groups added to level def setGroupNum(self,num): self.groupNum = num def getGroupNum(self): return self.groupNum # Angle of last object added to level def setLastAngle(self, angle): self.lastAngle = angle def getLastAngle(self): return self.lastAngle def drawSuitEdge(self, edge, parent): # Draw a line from start to end edgeLine = LineNodePath(parent) edgeLine.lineNode.setName('suitEdge') edgeLine.setColor(VBase4(0.0, 0.0, 0.5 ,1)) edgeLine.setThickness(1) edgeLine.reset() # We need to draw the arrow relative to the parent, but the # point positions are relative to the NPToplevel. So get the # start and end positions relative to the parent, then draw # the arrow using those points tempNode = self.NPToplevel.attachNewNode('tempNode') mat = self.NPToplevel.getMat(parent) relStartPos = Point3(mat.xformPoint(edge.getStartPoint().getPos())) relEndPos = Point3(mat.xformPoint(edge.getEndPoint().getPos())) # Compute offset: a vector rotated 90 degrees clockwise offset = Vec3(relEndPos - relStartPos) offset.normalize() offset *= 0.1 a = offset[0] offset.setX(offset[1]) offset.setY(-1 * a) # Just to get it above the street offset.setZ(0.05) # Add offset to start and end to help differentiate lines relStartPos += offset relEndPos += offset # Draw arrow edgeLine.drawArrow(relStartPos, relEndPos, 15, # arrow angle 1) # arrow length edgeLine.create() # Adjust color of highlighted lines if edge in self.visitedEdges: NodePath.setColor(edgeLine,1,0,0,1) # Clean up: tempNode.removeNode() return edgeLine def drawSuitPoint(self, suitPoint, pos, type, parent): marker = self.suitPointMarker.copyTo(parent) marker.setName("suitPointMarker") marker.setPos(pos) if (type == DNASuitPoint.STREETPOINT): marker.setColor(0,0,0.6) marker.setScale(0.4) elif (type == DNASuitPoint.FRONTDOORPOINT): marker.setColor(0,0,1) marker.setScale(0.5) elif (type == DNASuitPoint.SIDEDOORPOINT): marker.setColor(0,0.6,0.2) marker.setScale(0.5) # Highlight if necessary if suitPoint in self.visitedPoints: marker.setColor(1,0,0,1) return marker def placeSuitPoint(self): v = self.getGridSnapIntersectionPoint() # get the absolute pos relative to the top level. # That is what gets stored in the point mat = direct.grid.getMat(self.NPToplevel) absPos = Point3(mat.xformPoint(v)) print 'Suit point: ' + `absPos` # Store the point in the DNA. If this point is already in there, # it returns the existing point suitPoint = DNASTORE.storeSuitPoint(self.currentSuitPointType, absPos) if not self.pointDict.has_key(suitPoint): marker = self.drawSuitPoint(suitPoint, absPos, self.currentSuitPointType, self.suitPointToplevel) self.pointDict[suitPoint] = marker self.currentSuitPointIndex = suitPoint.getIndex() if self.startSuitPoint: self.endSuitPoint = suitPoint # Make a new dna edge if DNAClassEqual(self.DNAParent, DNA_VIS_GROUP): zoneId = self.DNAParent.getName() suitEdge = DNASuitEdge( self.startSuitPoint, self.endSuitPoint, zoneId) DNASTORE.storeSuitEdge(suitEdge) # Add edge to the current vis group so it can be written out self.DNAParent.addSuitEdge(suitEdge) # Draw a line to represent the edge edgeLine = self.drawSuitEdge(suitEdge, self.NPParent) # Store the line in a dict so we can hide/show them self.edgeDict[suitEdge] = edgeLine # Store the edge on each point in case we move the point # we can update the edge for point in [self.startSuitPoint, self.endSuitPoint]: if self.point2edgeDict.has_key(point): self.point2edgeDict[point].append(suitEdge) else: self.point2edgeDict[point] = [suitEdge] # If this is a building point, you need edges in both directions # so just make the other edge automatically if ((self.startSuitPoint.getPointType() == DNASuitPoint.FRONTDOORPOINT) or (self.startSuitPoint.getPointType() == DNASuitPoint.SIDEDOORPOINT)): suitEdge = DNASuitEdge( self.endSuitPoint, self.startSuitPoint, zoneId) DNASTORE.storeSuitEdge(suitEdge) # Add edge to the current vis group so it can be written out self.DNAParent.addSuitEdge(suitEdge) # Draw a line to represent the edge edgeLine = self.drawSuitEdge(suitEdge, self.NPParent) # Store the line in a dict so we can hide/show them self.edgeDict[suitEdge] = edgeLine for point in [self.startSuitPoint, self.endSuitPoint]: if self.point2edgeDict.has_key(point): self.point2edgeDict[point].append(suitEdge) else: self.point2edgeDict[point] = [suitEdge] print 'Added dnaSuitEdge to zone: ' + zoneId else: print 'Error: DNAParent is not a dnaVisGroup. Did not add edge' # Reset self.startSuitPoint = None self.endSuitPoint = None else: # First point, store it self.startSuitPoint = suitPoint def highlightConnected(self, nodePath = None, fReversePath = 0): if nodePath == None: nodePath = direct.selected.last if nodePath: suitPoint = self.findPointOrCell(nodePath)[0] if suitPoint: self.clearPathHighlights() self.highlightConnectedRec(suitPoint, fReversePath) def highlightConnectedRec(self, suitPoint, fReversePath): nodePath = self.pointDict.get(suitPoint, None) if nodePath: # highlight marker nodePath.setColor(1,0,0,1) # Add point to visited points self.visitedPoints.append(suitPoint) # highlight connected edges for edge in self.point2edgeDict[suitPoint]: if ((fReversePath or (suitPoint == edge.getStartPoint())) and (edge not in self.visitedEdges)): edgeLine = self.edgeDict[edge] # Call node path not LineNodePath setColor NodePath.setColor(edgeLine,1,0,0,1) # Add edge to visited edges self.visitedEdges.append(edge) # Color components connected to the edge if fReversePath: startPoint = edge.getStartPoint() if startPoint not in self.visitedPoints: self.highlightConnectedRec(startPoint, fReversePath) endPoint = edge.getEndPoint() type = endPoint.getPointType() if ((endPoint not in self.visitedPoints) and (fReversePath or (type == DNASuitPoint.STREETPOINT))): self.highlightConnectedRec(endPoint, fReversePath) def clearPathHighlights(self): for point in self.pointDict.keys(): type = point.getPointType() marker = self.pointDict[point] if (type == DNASuitPoint.STREETPOINT): marker.setColor(0,0,0.6) elif (type == DNASuitPoint.FRONTDOORPOINT): marker.setColor(0,0,1) elif (type == DNASuitPoint.SIDEDOORPOINT): marker.setColor(0,0.6,0.2) for edge in self.edgeDict.values(): edge.clearColor() self.visitedPoints = [] self.visitedEdges = [] def drawBattleCell(self, cell, parent): marker = self.battleCellMarker.copyTo(parent) # Greenish marker.setColor(0.25,0.75,0.25,0.5) marker.setTransparency(1) marker.setPos(cell.getPos()) # scale to radius which is width/2 marker.setScale(cell.getWidth()/2.0, cell.getHeight()/2.0, 1) return marker def placeBattleCell(self): # Store the battle cell in the current vis group if not DNAClassEqual(self.DNAParent, DNA_VIS_GROUP): print 'Error: DNAParent is not a dnaVisGroup. Did not add battle cell' return v = self.getGridSnapIntersectionPoint() mat = direct.grid.getMat(self.NPParent) absPos = Point3(mat.xformPoint(v)) if (self.currentBattleCellType == '20w 20l'): cell = DNABattleCell(20, 20, absPos) elif (self.currentBattleCellType == '20w 30l'): cell = DNABattleCell(20, 30, absPos) elif (self.currentBattleCellType == '30w 20l'): cell = DNABattleCell(30, 20, absPos) elif (self.currentBattleCellType == '30w 30l'): cell = DNABattleCell(30, 30, absPos) # Store the battle cell in the storage DNASTORE.storeBattleCell(cell) # Draw the battle cell marker = self.drawBattleCell(cell, self.NPParent) # Keep a handy dict of the visible markers self.cellDict[cell] = marker # Store the battle cell in the current vis group self.DNAParent.addBattleCell(cell) def createSuitPaths(self): # Points numPoints = DNASTORE.getNumSuitPoints() for i in range(numPoints): point = DNASTORE.getSuitPointAtIndex(i) marker = self.drawSuitPoint(point, point.getPos(), point.getPointType(), self.suitPointToplevel) self.pointDict[point] = marker # Edges visGroups = self.getDNAVisGroups(self.NPToplevel) for visGroup in visGroups: np = visGroup[0] dnaVisGroup = visGroup[1] numSuitEdges = dnaVisGroup.getNumSuitEdges() for i in range(numSuitEdges): edge = dnaVisGroup.getSuitEdge(i) edgeLine = self.drawSuitEdge(edge, np) self.edgeDict[edge] = edgeLine # Store the edge on each point in case we move the point # we can update the edge for point in [edge.getStartPoint(), edge.getEndPoint()]: if self.point2edgeDict.has_key(point): self.point2edgeDict[point].append(edge) else: self.point2edgeDict[point] = [edge] def getSuitPointFromNodePath(self, nodePath): """ Given a node path, attempt to find the point,nodePath pair in the pointDict. If it is there, return the point. If we cannot find it, return None. TODO: a reverse lookup pointDict would speed this up quite a bit """ for point, marker in self.pointDict.items(): if marker.eq(nodePath): return point return None def resetPathMarkers(self): for edge, edgeLine in self.edgeDict.items(): if not edgeLine.isEmpty(): edgeLine.reset() edgeLine.removeNode() self.edgeDict = {} for point, marker in self.pointDict.items(): if not marker.isEmpty(): marker.removeNode() self.pointDict = {} def hideSuitPaths(self): for edge, edgeLine in self.edgeDict.items(): edgeLine.hide() for point, marker in self.pointDict.items(): marker.hide() def showSuitPaths(self): for edge, edgeLine in self.edgeDict.items(): edgeLine.show() for point, marker in self.pointDict.items(): marker.show() def createBattleCells(self): # Edges visGroups = self.getDNAVisGroups(self.NPToplevel) for visGroup in visGroups: np = visGroup[0] dnaVisGroup = visGroup[1] numCells = dnaVisGroup.getNumBattleCells() for i in range(numCells): cell = dnaVisGroup.getBattleCell(i) marker = self.drawBattleCell(cell, np) self.cellDict[cell] = marker def resetBattleCellMarkers(self): for cell, marker in self.cellDict.items(): if not marker.isEmpty(): marker.remove() self.cellDict = {} def hideBattleCells(self): for cell, marker in self.cellDict.items(): marker.hide() def showBattleCells(self): for cell, marker in self.cellDict.items(): marker.show() def getBattleCellFromNodePath(self, nodePath): """ Given a node path, attempt to find the battle cell, nodePath pair in the cellDict. If it is there, return the cell. If we cannot find it, return None. TODO: a reverse lookup cellDict would speed this up quite a bit """ for cell, marker in self.cellDict.items(): if marker.eq(nodePath): return cell return None def toggleZoneColors(self): if self.panel.zoneColor.get(): self.colorZones() else: self.clearZoneColors() def colorZones(self): # Give each zone a random color to see them better visGroups = self.getDNAVisGroups(self.NPToplevel) for visGroup in visGroups: np = visGroup[0] np.setColor(0.5 + random()/2.0, 0.5 + random()/2.0, 0.5 + random()/2.0) def clearZoneColors(self): # Clear random colors visGroups = self.getDNAVisGroups(self.NPToplevel) for visGroup in visGroups: np = visGroup[0] np.clearColor() def getBlockFromName(self, name): block=name[2:name.find(':')] return block def addToLandmarkBlock(self): dnaRoot=self.selectedDNARoot if dnaRoot and self.lastLandmarkBuildingDNA: if DNAClassEqual(dnaRoot, DNA_FLAT_BUILDING): n=dnaRoot.getName() n=n[n.find(':'):] block=self.lastLandmarkBuildingDNA.getName() block=block[2:block.find(':')] dnaRoot.setName('tb'+block+n) self.replaceSelected() # If we're highlighting the landmark blocks: if self.showLandmarkBlockToggleGroup: # then highlight this one: np=self.selectedNPRoot self.showLandmarkBlockToggleGroup.append(np) np.setColor(1,0,0,1) elif self.selectedSuitPoint and self.lastLandmarkBuildingDNA: block=self.lastLandmarkBuildingDNA.getName() block=block[2:block.find(':')] print ("associate point with building: " + str(block)) self.selectedSuitPoint.setLandmarkBuildingIndex(int(block)) marker = self.pointDict[self.selectedSuitPoint] marker.setColor(1,0,0,1) marker.setScale(1.0) def findHighestLandmarkBlock(self, dnaRoot, npRoot): npc=npRoot.findAllMatches("**/*:toon_landmark_*") highest=0 for i in range(npc.getNumPaths()): path=npc.getPath(i) block=path.getName() block=int(block[2:block.find(':')]) if (block > highest): highest=block # Make a list of flat building names, outside of the # recursive function: self.flatNames=['random'] + BUILDING_TYPES self.flatNames=map(lambda n: n+'_DNARoot', self.flatNames) # Search/recurse the dna: newHighest=self.convertToLandmarkBlocks(highest, dnaRoot) # Get rid of the list of flat building names: del self.flatNames needToTraverse = (highest!=newHighest) return (newHighest, needToTraverse) def convertToLandmarkBlocks(self, block, dnaRoot): """ Find all the buildings without landmark blocks and assign them one. """ for i in range(dnaRoot.getNumChildren()): child = dnaRoot.at(i) if DNAClassEqual(child, DNA_LANDMARK_BUILDING): # Landmark buildings: name=child.getName() if name.find('toon_landmark_')==0: block=block+1 child.setName('tb'+str(block)+':'+name) elif DNAClassEqual(child, DNA_FLAT_BUILDING): # Flat buildings: name=child.getName() if (name in self.flatNames): child.setName('tb0:'+name) else: block = self.convertToLandmarkBlocks(block, child) return block def revertLandmarkBlock(self, block): """ un-block flat buildings (set them to block zero). """ npc=self.NPToplevel.findAllMatches("**/tb"+block+":*_DNARoot") for i in range(npc.getNumPaths()): nodePath=npc.getPath(i) name=nodePath.getName() if name[name.find(':'):][:15] != ':toon_landmark_': name='tb0'+name[name.find(':'):] dna=self.findDNANode(nodePath) dna.setName(name) nodePath=self.replace(nodePath, dna) # If we're highlighting the landmark blocks: if self.showLandmarkBlockToggleGroup: # then highlight this one: self.showLandmarkBlockToggleGroup.append(nodePath) nodePath.setColor(0,1,0,1) def landmarkBlockRemove(self, dna, nodePath): if dna: name=dna.getName() # Get the underscore index within the name: usIndex=name.find(':') if name[usIndex:][:15] == ':toon_landmark_': block=name[2:usIndex] self.lastLandmarkBuildingDNA=None self.revertLandmarkBlock(block) def toggleShowLandmarkBlock(self): dna=self.lastLandmarkBuildingDNA if dna: if not self.showLandmarkBlockToggleGroup: group=[] block=dna.getName() block=block[2:block.find(':')] # Get current landmark buildings: npc=self.NPToplevel.findAllMatches("**/tb"+block+":*_DNARoot") for i in range(npc.getNumPaths()): nodePath=npc.getPath(i) group.append(nodePath) nodePath.setColor(1,0,0,1) # Get block zero buildings (i.e. non-blocked): npc=self.NPToplevel.findAllMatches("**/tb0:*_DNARoot") for i in range(npc.getNumPaths()): nodePath=npc.getPath(i) group.append(nodePath) nodePath.setColor(0,1,0,1) # Get the suit point for this lb for point, marker in self.pointDict.items(): if ((point.getPointType() == DNASuitPoint.FRONTDOORPOINT) or (point.getPointType() == DNASuitPoint.SIDEDOORPOINT)): lbIndex = point.getLandmarkBuildingIndex() if (lbIndex == int(block)): marker.setColor(1,0,0,1) marker.setScale(1.0) # There should only be one, so break now elif (lbIndex == -1): # This point belongs to no block marker.setColor(0,1,0,1) self.showLandmarkBlockToggleGroup=group else: for i in self.showLandmarkBlockToggleGroup: i.clearColor() for point, marker in self.pointDict.items(): if (point.getPointType() == DNASuitPoint.FRONTDOORPOINT): marker.setColor(0,0,1,1) marker.setScale(0.5) elif (point.getPointType() == DNASuitPoint.SIDEDOORPOINT): marker.setColor(0,0.6,0.2,1) marker.setScale(0.5) self.showLandmarkBlockToggleGroup=None def pdbBreak(self): pdb.set_trace() class LevelStyleManager: """Class which reads in style files and manages class variables""" def __init__(self): # The main dictionary holding all attribute objects self.attributeDictionary = {} # Create the style samples self.createBaselineStyleAttributes() self.createWallStyleAttributes() self.createBuildingStyleAttributes() self.createColorAttributes() self.createDNAAttributes() self.createMiscAttributes() # BASELINE STYLE FUNCTIONS def createBaselineStyleAttributes(self): """ Create a baselineStyle entry in the attribute dictionary This will be a dictionary of style attributes, one per neighborhood """ # First create an empty dictionary dict = self.attributeDictionary['baseline_style'] = {} # Create a attribute object for each neighborhood for neighborhood in NEIGHBORHOODS: attribute = LevelAttribute('baseline_style') attribute.setDict( # Create a baseline style dictionary for each neighborhood self.createBaselineStyleDictionary(neighborhood)) # Using this dictionary, create style pie menus attribute.setMenu( self.createBaselineStyleMenu(neighborhood, attribute.getDict())) dict[neighborhood] = attribute def createBaselineStyleDictionary(self, neighborhood): """ Create a dictionary of baseline styles for a neighborhood """ filename = neighborhood + '_baseline_styles.txt' print 'Loading baseline styles from: ' + filename styleData = self.getStyleFileData(filename) return self.initializeBaselineStyleDictionary(styleData, neighborhood) def initializeBaselineStyleDictionary(self, styleData, neighborhood): """ Fill in the baseline style dictionary with data from the style file """ styleDictionary = {} styleCount = 0 code = NEIGHBORHOOD_CODES[neighborhood] while styleData: l = styleData[0] if l == 'baselineStyle': # Start of new style, strip off first line then extract style style, styleData = self.extractBaselineStyle(styleData) style.name = code + '_baseline_style_' + `styleCount` # Store style in dictionary styleDictionary[style.name] = style styleCount = styleCount + 1 # Move to next line styleData = styleData[1:] return styleDictionary def extractBaselineStyle(self, styleData): """ Pull out one style from a list of style data. Will keep processing data until endBaselineStyle of end of data is reached. Returns a baseline style and remaining styleData. """ # Create default style style = DNABaselineStyle() # Strip off first line styleData = styleData[1:] while styleData: l = styleData[0] if l == 'endBaselineStyle': # End of style found, break out of while loop # Note, endBaselineStyle line is *not* stripped off return style, styleData else: pair = map(string.strip, l.split(':')) if style.__dict__.has_key(pair[0]): pair_0 = pair[0] # Convert some numerical values if (pair_0 == 'color' or pair_0 == 'kern' or pair_0 == 'wiggle' or pair_0 == 'stumble' or pair_0 == 'stomp' or pair_0 == 'curve' or pair_0 == 'x' or pair_0 == 'z' or pair_0 == 'scaleX' or pair_0 == 'scaleZ' or pair_0 == 'roll'): style[pair_0] = eval(pair[1]) else: style[pair_0] = pair[1] else: print 'extractBaselineStyle: Invalid Key' print pair[0] styleData = styleData[1:] # No end of style found, return style data as is return style, None def createBaselineStyleMenu(self, neighborhood, dictionary): """ Create a baseline style pie menu """ numItems = len(dictionary) newStyleMenu = hidden.attachNewNode(neighborhood + '_style_menu') radius = 0.7 angle = deg2Rad(360.0/numItems) keys = dictionary.keys() keys.sort() styles = map(lambda x, d = dictionary: d[x], keys) sf = 0.1 aspectRatio = (direct.dr.width/float(direct.dr.height)) for i in range(numItems): # Get the node node = self.createBaselineStyleSample(styles[i]) bounds = node.getBounds() center = bounds.getCenter() center = center * sf # Reposition it node.setPos((radius * math.cos(i * angle)) - center[0], 0.0, ((radius * aspectRatio * math.sin(i * angle)) - center[2])) # Scale it node.setScale(sf) # Add it to the styleMenu node.reparentTo(newStyleMenu) # Scale the whole shebang down by 0.5 newStyleMenu.setScale(0.5) # Create and return a pie menu return PieMenu(newStyleMenu, styles) def createBaselineStyleSample(self, baselineStyle): """ Create a style sample using the DNA info in the style """ baseline = DNASignBaseline() # Set some example text: DNASetBaselineString(baseline, "Example Text") baselineStyle.copyTo(baseline) return baseline.traverse(hidden, DNASTORE, 1) # WALL STYLE FUNCTIONS def createWallStyleAttributes(self): """ Create a wallStyle entry in the attribute dictionary This will be a dictionary of style attributes, one per neighborhood """ # First create an empty dictionary dict = self.attributeDictionary['wall_style'] = {} # Create a attribute object for each neighborhood for neighborhood in NEIGHBORHOODS: attribute = LevelAttribute('wall_style') attribute.setDict( # Create a wall style dictionary for each neighborhood self.createWallStyleDictionary(neighborhood)) # Using this dictionary, create color pie menus attribute.setMenu( self.createWallStyleMenu(neighborhood, attribute.getDict())) dict[neighborhood] = attribute def createWallStyleDictionary(self, neighborhood): """ Create a dictionary of wall styles for a neighborhood """ filename = neighborhood + '_wall_styles.txt' print 'Loading wall styles from: ' + filename styleData = self.getStyleFileData(filename) return self.initializeWallStyleDictionary(styleData, neighborhood) def initializeWallStyleDictionary(self, styleData, neighborhood): """ Fill in the wall style dictionary with data from the style file """ styleDictionary = {} styleCount = 0 code = NEIGHBORHOOD_CODES[neighborhood] while styleData: l = styleData[0] if l == 'wallStyle': # Start of new style, strip off first line then extract style style, styleData = self.extractWallStyle(styleData) style.name = code + '_wall_style_' + `styleCount` # Store style in dictionary styleDictionary[style.name] = style styleCount = styleCount + 1 # Move to next line styleData = styleData[1:] return styleDictionary def extractWallStyle(self, styleData): """ Pull out one style from a list of style data. Will keep processing data until endWallStyle of end of data is reached. Returns a wall style and remaining styleData. """ # Create default style style = DNAWallStyle() # Strip off first line styleData = styleData[1:] while styleData: l = styleData[0] if l == 'endWallStyle': # End of style found, break out of while loop # Note, endWallStyle line is *not* stripped off return style, styleData else: pair = map(string.strip, l.split(':')) if style.__dict__.has_key(pair[0]): # Convert colors and count strings to numerical values if ((string.find(pair[0],'_color') >= 0) or (string.find(pair[0],'_count') >= 0)): style[pair[0]] = eval(pair[1]) else: style[pair[0]] = pair[1] else: print 'getStyleDictionaryFromStyleData: Invalid Key' print pair[0] styleData = styleData[1:] # No end of style found, return style data as is return style, None def createWallStyleMenu(self, neighborhood, dictionary): """ Create a wall style pie menu """ numItems = len(dictionary) newStyleMenu = hidden.attachNewNode(neighborhood + '_style_menu') radius = 0.7 angle = deg2Rad(360.0/numItems) keys = dictionary.keys() keys.sort() styles = map(lambda x, d = dictionary: d[x], keys) sf = 0.03 aspectRatio = (direct.dr.width/float(direct.dr.height)) for i in range(numItems): # Get the node node = self.createWallStyleSample(styles[i]) bounds = node.getBounds() center = bounds.getCenter() center = center * sf # Reposition it node.setPos((radius * math.cos(i * angle)) - center[0], 0.0, ((radius * aspectRatio * math.sin(i * angle)) - center[2])) # Scale it node.setScale(sf) # Add it to the styleMenu node.reparentTo(newStyleMenu) # Scale the whole shebang down by 0.5 newStyleMenu.setScale(0.5) # Create and return a pie menu return PieMenu(newStyleMenu, styles) def createWallStyleSample(self, wallStyle): """ Create a style sample using the DNA info in the style """ bldg = DNAFlatBuilding() bldgStyle = DNAFlatBuildingStyle(styleList = [(wallStyle, 10.0)]) self.setDNAFlatBuildingStyle(bldg, bldgStyle, width = 12.0, name = 'wall_style_sample') return bldg.traverse(hidden, DNASTORE, 1) # BUILDING STYLE FUNCTIONS def createBuildingStyleAttributes(self): """ Create a buildingStyle entry in the attribute dictionary This will be a dictionary of style attributes, one per neighborhood This information is sorted and stored by num walls, building height, and building type (e.g. 10_20, or 10_10_10). """ # First create an attribute which holds one dictionary per # neighborhood which stores all of the styles for each neighborhood. styleDict = self.attributeDictionary['building_style_all'] = {} # Keep track of all building types typeDict = {} masterTypeList = [] for neighborhood in NEIGHBORHOODS: attribute = LevelAttribute('building_style_all') # Create a building style dictionary for each neighborhood attribute.setDict( self.createBuildingStyleDictionary(neighborhood)) # Using this dictionary, create building style pie menus attribute.setMenu( self.createBuildingStyleMenu(neighborhood, attribute.getDict())) # Store attribute in dictionary keyed by neighborhood styleDict[neighborhood] = attribute # Generate a list of building types for this neighborhood # and a list of building types for all neighborhoods, # to be used in creating attribute dicts below typeList = typeDict[neighborhood] = [] for style in attribute.getList(): heightType = string.strip(string.split(style.name, ':')[1]) if heightType not in typeList: typeList.append(heightType) if heightType not in masterTypeList: masterTypeList.append(heightType) # Now sort styles according to the building type and number of walls # in the building style. Each of these attributes is also sorted by # neighborhood for i in masterTypeList: typeKey = i + '_styles' self.attributeDictionary[typeKey] = {} for i in NUM_WALLS: numWallKey = `i` + '_wall_styles' self.attributeDictionary[numWallKey] = {} # Also sort height lists according to total height of the building for i in BUILDING_HEIGHTS: heightKey = `i` + '_ft_wall_heights' self.attributeDictionary[heightKey] = {} # Now distribute data for each neighborhood for neighborhood in NEIGHBORHOODS: # Create temp dicts to accumulate styles and heights for that # neighborhood typeAttributes = {} numWallsAttributes = {} heightAttributes = {} # Store atributes for the different categories # Building type for i in typeDict[neighborhood]: typeAttrName = neighborhood + '_' + i + '_styles' typeAttributes[i] = LevelAttribute(typeAttrName) # Number of walls for i in NUM_WALLS: styleAttrName = neighborhood + '_' + `i` + '_wall_styles' numWallsAttributes[i] = LevelAttribute(styleAttrName) # Building height for i in BUILDING_HEIGHTS: heightAttrName = neighborhood + '_' + `i` + '_ft_wall_heights' heightAttributes[i] = LevelAttribute(heightAttrName) # Sort through the styles and store in separate lists for style in styleDict[neighborhood].getList(): # Put in code for number of walls into building styles heightType = string.strip(string.split(style.name, ':')[1]) heightList = map(string.atof, string.split(heightType, '_')) numWalls = len(heightList) # This one stores styles sorted by type typeAttributes[heightType].add(style) # This one stores styles sorted by number of walls numWallsAttributes[numWalls].add(style) # A record of all the unique height lists height = calcHeight(heightList) if heightList not in heightAttributes[height].getList(): heightAttributes[height].add(heightList) # Now store these sorted style and height attributes # in the appropriate top-level attribute dictionary for i in typeDict[neighborhood]: # Styles typeKey = i + '_styles' self.attributeDictionary[typeKey][neighborhood] = ( typeAttributes[i]) for i in NUM_WALLS: # Styles numWallKey = `i` + '_wall_styles' self.attributeDictionary[numWallKey][neighborhood] = ( numWallsAttributes[i]) for i in BUILDING_HEIGHTS: # Heights heightKey = `i` + '_ft_wall_heights' self.attributeDictionary[heightKey][neighborhood] = ( heightAttributes[i]) def createBuildingStyleDictionary(self, neighborhood): """ Create a dictionary of wall styles for a neighborhood """ filename = neighborhood + '_building_styles.txt' print 'Loading building styles from: ' + filename styleData = self.getStyleFileData(filename) return self.initializeBuildingStyleDictionary(styleData, neighborhood) def initializeBuildingStyleDictionary(self, styleData, neighborhood): """ Fill in the building style dictionary with data from the style file """ # Create a dictionary of all building styles, this will later be # split out into a separate dictionary for each building type # e.g. 20, 10-10, 10-20.... bldgStyleDictionary = {} styleCount = 0 code = NEIGHBORHOOD_CODES[neighborhood] while styleData: # Pull out first line l = styleData[0] if l[:13] == 'buildingStyle': # Start with empty style list bldgStyle = DNAFlatBuildingStyle(styleList = []) # Extract height information found at end of line heightCode = string.strip(string.split(l, ':')[1]) heightList = map(string.atof, string.split(heightCode, '_')) # Construct name for building style. Tack on height code # to be used later to split styles by heightCode bldgStyle.name = ( code + '_building_style_' + `styleCount` + ':' + heightCode) # Increment counter styleCount = styleCount + 1 # Reset wall counter to zero wallCount = 0 elif l == 'endBuildingStyle': # Done, add new style to dictionary bldgStyleDictionary[bldgStyle.name] = bldgStyle elif l[:9] == 'wallStyle': # Beginning of next wall style wallStyle, styleData = self.extractWallStyle(styleData) wallStyle.name = bldgStyle.name + '_wall_' + `wallCount` try: height = heightList[wallCount] except IndexError: height = 10.0 # Add wall style to building style bldgStyle.add(wallStyle, height) # Increment wall counter wallCount = wallCount + 1 # Move to next line styleData = styleData[1:] return bldgStyleDictionary def createBuildingStyleMenu(self, neighborhood, dictionary): """ Create a wall style pie menu """ numItems = len(dictionary) newStyleMenu = hidden.attachNewNode(neighborhood + '_style_menu') radius = 0.7 angle = deg2Rad(360.0/numItems) keys = dictionary.keys() keys.sort() styles = map(lambda x, d = dictionary: d[x], keys) sf = 0.02 aspectRatio = (direct.dr.width/float(direct.dr.height)) for i in range(numItems): # Get the node node = self.createBuildingStyleSample(styles[i]) bounds = node.getBounds() center = bounds.getCenter() center = center * sf # Reposition it node.setPos((radius * math.cos(i * angle)) - center[0], 0.0, ((radius * aspectRatio * math.sin(i * angle)) - center[2])) # Scale it node.setScale(sf) # Add it to the styleMenu node.reparentTo(newStyleMenu) # Scale the whole shebang down by 0.5 newStyleMenu.setScale(0.5) # Create and return a pie menu return PieMenu(newStyleMenu, styles) def createBuildingStyleSample(self, bldgStyle): """ Create a style sample using the DNA info in the style """ bldg = DNAFlatBuilding() self.setDNAFlatBuildingStyle(bldg, bldgStyle, width = 10.0, name = 'building_style_sample') return bldg.traverse(hidden, DNASTORE, 1) def setDNAFlatBuildingStyle(self, fb, bldgStyle, width = 10.0, heightList = None, name = 'building'): """ Set DNAFlatBuilding style. """ # Remove flat building's children DNARemoveChildren(fb) # Update the name fb.setName(name) # Create the walls styleList = bldgStyle.styleList # Height list not specified, use styles default heights if not heightList: heightList = bldgStyle.heightList for i in range(len(styleList)): wallStyle = styleList[i] # Get Style if not wallStyle: # If set to None use default style wallStyle = DNAWallStyle() # Try to get height try: wallHeight = heightList[i] except IndexError: wallHeight = 10.0 # Create wall accordingly wall = DNAWall() self.setDNAWallStyle(wall, wallStyle, wallHeight, width = width) # Add it to building DNA fb.add(wall) # Set the buildings width fb.setWidth(width) def setDNAWallStyle(self, wall, style, height = 10.0, width = None): """ Set DNAWall to input style. """ # Remove wall's children DNARemoveChildren(wall) # Update wall attributes wall.setCode(style['wall_texture']) wall.setColor(style['wall_color']) wall.setHeight(height) # Add windows if necessary if style['window_texture']: windows = DNAWindows() windowCount = style['window_count'] if width: if (width < 15.0): windowCount = min(1, windowCount) windows.setWindowCount(windowCount) # Set window's attributes windows.setCode(style['window_texture']) windows.setColor(style['window_color']) # Add windows to the wall wall.add(windows) # Add a window awning if necessary if style['window_awning_texture']: awning = DNAProp() # Update awning's attributes awning.setCode(style['window_awning_texture']) awning.setColor(style['window_awning_color']) # Add awning to window windows.add(awning) # Add a door if necessary if style['door_texture']: door = DNAFlatDoor() # Set the door's attributes door.setCode(style['door_texture']) door.setColor(style['door_color']) # Add door to wall wall.add(door) # Add a door awning if necessary if style['door_awning_texture']: awning = DNAProp() awning.setCode(style['door_awning_texture']) awning.setColor(style['door_awning_color']) door.add(awning) # And a cornice if necessary if style['cornice_texture']: cornice = DNACornice() # Set the cornice's attributes cornice.setCode(style['cornice_texture']) cornice.setColor(style['cornice_color']) # Add cornice to wall wall.add(cornice) def printFlatBuildingStyle(self, building): for i in range(building.getNumChildren()): child = building.at(i) if DNAClassEqual(child, DNA_WALL): self.printWallStyle(child) def printWallStyle(self, wall): print 'wall_texture: ' + wall.getCode() color = wall.getColor() print ('wall_color: Vec4(%.3f, %.3f, %.3f, 1.0)' % (color[0], color[1], color[2])) for i in range(wall.getNumChildren()): child = wall.at(i) if DNAClassEqual(child, DNA_WINDOWS): print 'window_texture: ' + child.getCode() color = child.getColor() print ('window_color: Vec4(%.3f, %.3f, %.3f, 1.0)' % (color[0], color[1], color[2])) # MRM: Check for awnings here elif DNAClassEqual(child, DNA_DOOR): print 'door_texture: ' + child.getCode() color = child.getColor() print ('door_color: Vec4(%.3f, %.3f, %.3f, 1.0)' % (color[0], color[1], color[2])) elif DNAClassEqual(child, DNA_FLAT_DOOR): print 'door_texture: ' + child.getCode() color = child.getColor() print ('door_color: Vec4(%.3f, %.3f, %.3f, 1.0)' % (color[0], color[1], color[2])) elif DNAClassEqual(child, DNA_CORNICE): print 'cornice_texture: ' + child.getCode() color = child.getColor() print ('cornice_color: Vec4(%.3f, %.3f, %.3f, 1.0)' % (color[0], color[1], color[2])) # COLOR PALETTE FUNCTIONS def createColorAttributes(self): # First compile color information for each neighborhood colorDictionary = {} colorMenuDictionary = {} for neighborhood in NEIGHBORHOODS: colorDictionary[neighborhood] = ( # Create a wall style dictionary for each neighborhood self.createColorDictionary(neighborhood)) # Using this dictionary, create color pie menus colorMenuDictionary[neighborhood] = ( self.createColorMenus( neighborhood, colorDictionary[neighborhood])) # Now store this info in the appropriate place in the attribute dict for colorType in COLOR_TYPES: neighborhoodDict = self.attributeDictionary[colorType] = {} for neighborhood in NEIGHBORHOODS: attribute = LevelAttribute(colorType) dict = {} # Add colors to attribute dictionary colorList = colorDictionary[neighborhood][colorType] for i in range(len(colorList)): dict[i] = colorList[i] attribute.setDict(dict) attribute.setMenu( colorMenuDictionary[neighborhood][colorType]) neighborhoodDict[neighborhood] = attribute def createColorDictionary(self, neighborhood): filename = neighborhood + '_colors.txt' print 'Loading Color Palettes from: ' + filename colorData = self.getStyleFileData(filename) return self.getColorDictionary(colorData) def getColorDictionary(self, colorData): # Initialze neighborhod color dictionary dict = {} for colorType in COLOR_TYPES: dict[colorType] = DEFAULT_COLORS[:] # Add color information to appropriate sub-list for line in colorData: pair = map(string.strip, line.split(':')) key = pair[0] if dict.has_key(key): dict[key].append(eval(pair[1])) else: print 'LevelStyleManager.getColorDictionary key not found' return dict def createColorMenus(self, neighborhood, dictionary): menuDict = {} keys = dictionary.keys() for key in keys: menuDict[key] = ( self.createColorMenu(neighborhood + key, dictionary[key])) return menuDict def createColorMenu(self, menuName, colorList, radius = 0.7, sf = 2.0): # Create color chips for each color numItems = len(colorList) # Attach it to hidden for now newColorMenu = hidden.attachNewNode(menuName + 'Menu') # Compute the angle per item angle = deg2Rad(360.0/float(numItems)) aspectRatio = (direct.dr.width / float(direct.dr.height)) # Attach the color chips to the new menu and adjust sizes for i in range (numItems): # Create a text node--just a card, really--of the right color. tn = TextNode('colorChip') tn.freeze() tn.setFont(getDefaultFont()) tn.setTransform(Mat4.scaleMat(0.07, 0.07, 0.07 * aspectRatio)) tn.setCardColor(colorList[i]) tn.setCardActual(0, 1.1111, 0, 0.8333) tn.setText(' ') tn.thaw() # Reposition it card = tn.getCardTransformed() center = (card[1] - card[0], card[3] - card[2]) node = newColorMenu.attachNewNode(tn) node.setScale(sf) node.setPos(radius * math.cos(i * angle) - center[0], 0.0, ((radius * aspectRatio * math.sin(i * angle)) - center[1])) # Scale the whole shebang down by 0.5 newColorMenu.setScale(0.5) # Create and return resulting pie menu return PieMenu(newColorMenu, colorList) # DNA ATTRIBUTES def createDNAAttributes(self): # Create the DNA Attribute entries # Most objects are oriented with graphical menu items # Street and props aren't oiented and use text menus for dnaType in DNA_TYPES: # Create a dictionary of dna types dict = {} if ((dnaType == 'street') or (dnaType == 'prop') or (dnaType == 'toon_landmark')): dnaList = self.getCatalogCodes(dnaType) elif (dnaType == 'sign'): dnaList = [''] + self.getCatalogCodes(dnaType) else: dnaList = [None] + self.getCatalogCodesSuffix(dnaType, '_ur') # Add dnaCodes to attribute dictionary for i in range(len(dnaList)): dict[i] = dnaList[i] # Create a LevelAttribute attribute = LevelAttribute(dnaType + '_texture') attribute.setDict(dict) # Prepend None to allow option of no item if ((dnaType == 'street') or (dnaType == 'prop') or (dnaType == 'toon_landmark')): attribute.setMenu(self.createTextPieMenu(dnaType, dnaList)) elif (dnaType == 'wall'): attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList, sf = 0.25)) elif (dnaType == 'sign'): attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList, sf = 0.05)) elif (dnaType == 'door'): attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList, sf = 0.035)) elif (dnaType == 'cornice'): attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList, sf = 0.5)) elif (dnaType == 'window'): attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList, sf = 0.125)) else: print 'unknown attribute' # Add it to the attributeDictionary self.attributeDictionary[dnaType + '_texture'] = attribute def createDNAPieMenu(self, dnaType, dnaList, radius = 0.7, sf = 1.0): # Get the currently available window options numItems = len(dnaList) # Create a top level node to hold the menu newMenu = hidden.attachNewNode(dnaType + 'Menu') # Compute angle increment per item angle = deg2Rad(360.0/numItems) aspectRatio = direct.dr.width /float(direct.dr.height) # Add items for i in range(0, numItems): if dnaList[i]: # Get the node node = DNASTORE.findNode(dnaList[i]) else: node = None if node: # Add it to the window menu # This instance was causing problems for dna nodes that were # loaded as singletons, so I changed it to copyTo. # path = node.instanceTo(newMenu) path = node.copyTo(newMenu) # Place menu nodes in a circle, offset each in X and Z # by half node width/height bounds = path.getBounds() center = bounds.getCenter() center = center * sf path.setPos((radius * math.cos(i * angle)) - center[0], 0.0, ((radius * aspectRatio * math.sin(i * angle)) - center[2])) path.setScale(sf) # Scale the whole shebang down by 0.5 newMenu.setScale(0.5) # Create and return a pie menu return PieMenu(newMenu, dnaList) def createTextPieMenu(self, dnaType, textList, radius = 0.7, sf = 1.0): numItems = len(textList) # Create top level node for new menu newMenu = hidden.attachNewNode(dnaType + 'Menu') # Compute angle per item angle = deg2Rad(360.0/numItems) aspectRatio = direct.dr.width/float(direct.dr.height) # Add items for i in range (numItems): # Create text node for each item if (textList[i] != None): tn = TextNode('TextItem') tn.freeze() tn.setFont(getDefaultFont()) tn.setTransform(Mat4.scaleMat(0.07, 0.07, 0.07 * aspectRatio)) tn.setTextColor(0, 0, 0, 1) tn.setCardColor(1, 1, 1, 1) tn.setCardAsMargin(0.1, 0.1, 0.1, 0.1) tn.setText(str(textList[i])) tn.thaw() # Reposition it card = tn.getCardTransformed() center = (card[1] - card[0], card[3] - card[2]) node = newMenu.attachNewNode(tn) node.setScale(sf) node.setPos(radius * math.cos(i * angle) - center[0], 0.0, ((radius * aspectRatio * math.sin(i * angle)) - center[1])) # Scale the whole shebang down by 0.5 newMenu.setScale(0.5) # Create and return a pie menu return PieMenu(newMenu, textList) # MISCELLANEOUS MENUS def createMiscAttributes(self): # Num windows menu self.createMiscAttribute('window_count',[0,1,2,3,4]) # Building width menu self.createMiscAttribute('building_width',[5,10,15,15.6,20,20.7,25]) # Building types self.createMiscAttribute('building_type', BUILDING_TYPES) # Building heights self.createMiscAttribute('building_height', [10,20,25,30]) # MRM: Need offset on these menus # Wall orientation menu self.createMiscAttribute('wall_orientation', ['ur','ul','dl','dr']) # Wall height menu self.createMiscAttribute('wall_height', [10, 20]) # Window orientation menu self.createMiscAttribute('window_orientation', ['ur','ul',None,None]) # Sign orientation menu self.createMiscAttribute('sign_orientation', ['ur','ul',None,None]) # Door orientation menu self.createMiscAttribute('door_orientation', ['ur','ul',None,None]) # Cornice orientation menu self.createMiscAttribute('cornice_orientation', ['ur','ul',None,None]) def createMiscAttribute(self, miscType, miscList, sf = 3.0): # Create a dictionary from miscList dict = {} # Add items to attribute dictionary for i in range(len(miscList)): dict[i] = miscList[i] # Create the miscellaneous Attribute entries attribute = LevelAttribute(miscType) attribute.setDict(dict) # Now create a pie menu attribute.setMenu(self.createTextPieMenu(miscType, miscList, sf = sf)) # Add it to the attributeDictionary self.attributeDictionary[miscType] = attribute # GENERAL FUNCTIONS def getStyleFileData(self, filename): """ Open the specified file and strip out unwanted whitespace and empty lines. Return file as list, one file line per element. """ fname = Filename(dnaDirectory.getFullpath() + '/stylefiles/' + filename) # We use binary mode to avoid Windows' end-of-line convention f = open(fname.toOsSpecific(), 'rb') rawData = f.readlines() f.close() styleData = [] for line in rawData: l = string.strip(line) if l: styleData.append(l) return styleData # UTILITY FUNCTIONS def getAttribute(self, attribute): """ Return specified attribute for current neighborhood """ levelAttribute = self.attributeDictionary[attribute] # Get attribute for current neighborhood if (type(levelAttribute) == types.DictionaryType): levelAttribute = levelAttribute[self.getEditMode()] return levelAttribute # UTILITY FUNCTIONS def hasAttribute(self, attribute): """ Return specified attribute for current neighborhood """ if not self.attributeDictionary.has_key(attribute): return 0 else: levelAttribute = self.attributeDictionary[attribute] # Get attribute for current neighborhood if (type(levelAttribute) == types.DictionaryType): editMode = self.getEditMode() return levelAttribute.has_key(editMode) else: return 1 def getCatalogCode(self, category, i): return DNASTORE.getCatalogCode(category, i) def getCatalogCodes(self, category): numCodes = DNASTORE.getNumCatalogCodes(category) codes = [] for i in range(numCodes): codes.append(DNASTORE.getCatalogCode(category, i)) return codes def getCatalogCodesSuffix(self, category, suffix): codes = self.getCatalogCodes(category) orientedCodes = [] for code in codes: if code[-3:] == suffix: orientedCodes.append(code) return orientedCodes def setEditMode(self, mode): """Set current neighborhood editing mode""" self._editMode = mode def getEditMode(self): """Get current neighborhood editing mode""" return self._editMode class LevelAttribute: """Class which encapsulates a pie menu and a set of items""" def __init__(self, name): # Record name self.name = name # Pie menu used to pick an option self._menu = None # Dictionary of available options self._dict = {} self._list = [] self.count = 0 # Currently selected option self._current = None def setCurrent(self, newValue, fEvent = 1): self._current = newValue # Send event if specified if fEvent: messenger.send('select_' + self.name, [self._current]) def setMenu(self,menu): self._menu = menu self._menu.action = self.setCurrent def setDict(self,dict): self._dict = dict # Create a list from the dictionary self._list = dict.values() # Update count self.count = len(self._list) # Initialize current to first item if (len(self._list) > 0): self._current = self._list[0] def setList(self,list): self._list = list # Create a dictionary from the list self._dict = {} self.count = 0 for item in list: self._dict[self.count] = item self.count += 1 # Initialize current to first item if (self.count > 0): self._current = self._list[0] def add(self,item): self._dict[self.count] = item self._list.append(item) self.count += 1 def getCurrent(self): return self._current def getMenu(self): return self._menu def getDict(self): return self._dict def getList(self): return self._list def getCount(self): return self.count class DNAFlatBuildingStyle: """Class to hold attributes of a building style""" def __init__(self, building = None, styleList = None, name = 'bldg_style'): self.name = name if building: # Passed in a building self.copy(building) elif styleList != None: # Passed in a list of style-height pairs self.styleList = [] self.heightList = [] self.numWalls = 0 for pair in styleList: self.add(pair[0], pair[1]) else: # Use default style/height self.styleList = [DNAWallStyle()] self.heightList = [10] self.numWalls = 1 def add(self, style, height): self.styleList.append(style) self.heightList.append(height) self.numWalls += 1 def copy(self, building): self.styleList = [] self.heightList = DNAGetWallHeights(building)[0] self.numWalls = 0 for i in range(building.getNumChildren()): child = building.at(i) if DNAClassEqual(child, DNA_WALL): wallStyle = DNAWallStyle(wall = child) self.styleList.append(wallStyle) self.numWalls += 1 def output(self, file = sys.stdout): file.write('buildingStyle: %s\n' % createHeightCode(self.heightList)) for style in self.styleList: style.output(file) file.write('endBuildingStyle\n') def createHeightCode(heightList): def joinHeights(h1,h2): return '%s_%s' % (h1, h2) hl = map(ROUND_INT, heightList) if len(hl) == 1: return `hl[0]` return reduce(joinHeights, hl) def calcHeight(heightList): height = 0 for h in heightList: height = height + h return int(height) class DNABaselineStyle: """Class to hold attributes of a baseline style (wiggle, colors, etc)""" def __init__(self, baseline = None, name = 'baseline_style'): # First initialize everything self.name = name self.code = None # i.e. font self.kern = None self.wiggle = None self.stumble = None self.stomp = None self.curve = None self.x = None self.z = None self.scaleX = 1.0 self.scaleZ = 1.0 self.roll = None self.flags = None self.color = Vec4(1.0) # Then copy the specifics for the baseline if input if baseline: self.copy(baseline) def copy(self, baseline): self.code = baseline.getCode() self.kern = baseline.getKern() self.wiggle = baseline.getWiggle() self.stumble = baseline.getStumble() self.stomp = baseline.getStomp() width=baseline.getWidth() if width: self.curve = 1.0/width else: self.curve = 0.0 pos = baseline.getPos() self.x = pos[0] self.z = pos[2] scale = baseline.getScale() self.scaleX = scale[0] self.scaleZ = scale[2] hpr = baseline.getHpr() self.roll = hpr[2] self.flags = baseline.getFlags() self.color = baseline.getColor() def copyTo(self, baseline): if self.code != None: baseline.setCode(self.code) if self.kern != None: baseline.setKern(self.kern) if self.wiggle != None: baseline.setWiggle(self.wiggle) if self.stumble != None: baseline.setStumble(self.stumble) if self.stomp != None: baseline.setStomp(self.stomp) if self.curve != None: diameter=0.0 if self.curve: diameter=1.0/self.curve baseline.setWidth(diameter) baseline.setHeight(diameter) if (self.x != None) or (self.z != None): pos=baseline.getPos() if self.x != None: pos=Vec3(self.x, pos[1], pos[2]) if self.z != None: pos=Vec3(pos[0], pos[1], self.z) baseline.setPos(pos) if self.roll != None: baseline.setHpr(Vec3(0.0, 0.0, self.roll)) if (self.scaleX != None) or (self.scaleZ != None): scale=baseline.getScale() if self.scaleX != None: scale=Vec3(self.scaleX, scale[1], scale[2]) if self.scaleZ != None: scale=Vec3(scale[0], scale[1], self.scaleZ) baseline.setScale(scale) if self.flags != None: baseline.setFlags(self.flags) if self.color != None: baseline.setColor(self.color) def output(self, file = sys.stdout): """ Output style description to a file """ file.write('baselineStyle\n') if self.name != None: file.write('name: %s\n' % self.name) if self.code != None: file.write('code: %s\n' % self.code) if self.kern != None: file.write('kern: %s\n' % self.kern) if self.wiggle != None: file.write('wiggle: %s\n' % self.wiggle) if self.stumble != None: file.write('stumble: %s\n' % self.stumble) if self.stomp != None: file.write('stomp: %s\n' % self.stomp) if self.curve != None: file.write('curve: %s\n' % self.curve) if self.x != None: file.write('x: %s\n' % self.x) if self.z != None: file.write('z: %s\n' % self.z) if self.scaleX != None: file.write('scaleX: %s\n' % self.scaleX) if self.scaleZ != None: file.write('scaleZ: %s\n' % self.scaleZ) if self.roll != None: file.write('roll: %s\n' % self.roll) if self.flags != None: file.write('flags: %s\n' % self.flags) if self.color != None: file.write('color: Vec4(%.2f, %.2f, %.2f, 1.0)\n' % (self.color[0], self.color[1], self.color[2])) file.write('endBaselineStyle\n') # Convience functions to facilitate class use def __setitem__(self, index, item): self.__dict__[index] = item def __getitem__(self, index): return self.__dict__[index] def __repr__(self): return( 'name: %s\n' % self.name + 'code: %s\n' % self.code + 'kern: %s\n' % self.kern + 'wiggle: %s\n' % self.wiggle + 'stumble: %s\n' % self.stumble + 'stomp: %s\n' % self.stomp + 'curve: %s\n' % self.curve + 'x: %s\n' % self.x + 'z: %s\n' % self.z + 'scaleX: %s\n' % self.scaleX + 'scaleZ: %s\n' % self.scaleZ + 'roll: %s\n' % self.roll + 'flags: %s\n' % self.flags + 'color: %s\n' % self.color ) class DNAWallStyle: """Class to hold attributes of a wall style (textures, colors, etc)""" def __init__(self, wall = None, name = 'wall_style'): # First initialize everything self.name = name self.wall_texture = 'wall_md_blank_ur' self.wall_color = Vec4(1.0) self.window_count = 2 self.window_texture = None self.window_color = Vec4(1.0) self.window_awning_texture = None self.window_awning_color = Vec4(1.0) self.door_texture = None self.door_color = Vec4(1.0) self.door_awning_texture = None self.door_awning_color = Vec4(1.0) self.cornice_texture = None self.cornice_color = Vec4(1.0) # Then copy the specifics for the wall if input if wall: self.copy(wall) def copy(self, wall): self.wall_texture = wall.getCode() self.wall_color = wall.getColor() for i in range(wall.getNumChildren()): child = wall.at(i) if DNAClassEqual(child, DNA_WINDOWS): self.window_count = child.getWindowCount() self.window_texture = child.getCode() self.window_color = child.getColor() # MRM: Check for awnings here elif DNAClassEqual(child, DNA_DOOR): self.door_texture = child.getCode() self.door_color = child.getColor() elif DNAClassEqual(child, DNA_FLAT_DOOR): self.door_texture = child.getCode() self.door_color = child.getColor() # MRM: Check for awnings here elif DNAClassEqual(child, DNA_CORNICE): self.cornice_texture = child.getCode() self.cornice_color = child.getColor() def output(self, file = sys.stdout): """ Output style description to a file """ def writeAttributes(f, type, s = self): color = s[type + '_color'] f.write('%s_texture: %s\n' % (type, s[type + '_texture'])) f.write('%s_color: Vec4(%.2f, %.2f, %.2f, 1.0)\n' % (type, color[0], color[1], color[2])) file.write('wallStyle\n') writeAttributes(file, 'wall') if self['window_texture']: writeAttributes(file, 'window') file.write('window_count: %s\n' % self['window_count']) if self['window_awning_texture']: writeAttributes(file, 'window_awning') if self['door_texture']: writeAttributes(file, 'door') if self['door_awning_texture']: writeAttributes(file, 'door_awning') if self['cornice_texture']: writeAttributes(file, 'cornice') file.write('endWallStyle\n') # Convience functions to facilitate class use def __setitem__(self, index, item): self.__dict__[index] = item def __getitem__(self, index): return self.__dict__[index] def __repr__(self): return( 'Name: %s\n' % self.name + 'Wall Texture: %s\n' % self.wall_texture + 'Wall Color: %s\n' % self.wall_color + 'Window Texture: %s\n' % self.window_texture + 'Window Color: %s\n' % self.window_color + 'Window Awning Texture: %s\n' % self.window_awning_texture + 'Window Awning Color: %s\n' % self.window_awning_color + 'Door Texture: %s\n' % self.door_texture + 'Door Color: %s\n' % self.door_color + 'Door Awning Texture: %s\n' % self.door_awning_texture + 'Door Awning Color: %s\n' % self.door_awning_color + 'Cornice Texture: %s\n' % self.cornice_texture + 'Cornice Color: %s\n' % self.cornice_color ) class OldLevelEditor(NodePath, PandaObject): pass class LevelEditorPanel(Pmw.MegaToplevel): def __init__(self, levelEditor, parent = None, **kw): INITOPT = Pmw.INITOPT optiondefs = ( ('title', 'Toontown Level Editor', None), ) self.defineoptions(kw, optiondefs) Pmw.MegaToplevel.__init__(self, parent, title = self['title']) self.levelEditor = levelEditor self.styleManager = self.levelEditor.styleManager self.fUpdateSelected = 1 # Handle to the toplevels hull hull = self.component('hull') hull.geometry('400x515') balloon = self.balloon = Pmw.Balloon(hull) # Start with balloon help disabled self.balloon.configure(state = 'none') menuFrame = Frame(hull, relief = GROOVE, bd = 2) menuFrame.pack(fill = X) menuBar = Pmw.MenuBar(menuFrame, hotkeys = 1, balloon = balloon) menuBar.pack(side = LEFT, expand = 1, fill = X) menuBar.addmenu('Level Editor', 'Level Editor Operations') menuBar.addmenuitem('Level Editor', 'command', 'Load DNA from specified file', label = 'Load DNA...', command = self.levelEditor.loadSpecifiedDNAFile) menuBar.addmenuitem('Level Editor', 'command', 'Save DNA data to specified file', label = 'Save DNA As...', command = self.levelEditor.saveToSpecifiedDNAFile) menuBar.addmenuitem('Level Editor', 'command', 'Save DNA File', label = 'Save DNA', command = self.levelEditor.outputDNADefaultFile) menuBar.addmenuitem('Level Editor', 'command', 'Edit Visibility Groups', label = 'Edit Vis Groups', command = self.levelEditor.editDNAVisGroups) menuBar.addmenuitem('Level Editor', 'command', 'Reset level', label = 'Reset level', command = self.levelEditor.reset) menuBar.addmenuitem('Level Editor', 'command', 'Exit Level Editor Panel', label = 'Exit', command = self.levelEditor.destroy) menuBar.addmenu('Style', 'Style Operations') menuBar.addmenuitem('Style', 'command', "Save Selected Object's Color", label = 'Save Color', command = self.levelEditor.saveColor) menuBar.addmenuitem('Style', 'command', "Save Selected Baseline's Style", label = 'Save Baseline Style', command = self.levelEditor.saveBaselineStyle) menuBar.addmenuitem('Style', 'command', "Save Selected Wall's Style", label = 'Save Wall Style', command = self.levelEditor.saveWallStyle) menuBar.addmenuitem('Style', 'command', "Save Selected Buildings's Style", label = 'Save Bldg Style', command = self.levelEditor.saveBuildingStyle) menuBar.addmenuitem('Style', 'command', 'Reload Color Palettes', label = 'Reload Colors', command = self.styleManager.createColorAttributes) menuBar.addmenuitem('Style', 'command', 'Reload Baseline Style Palettes', label = 'Reload Baseline Styles', command = self.styleManager.createBaselineStyleAttributes) menuBar.addmenuitem('Style', 'command', 'Reload Wall Style Palettes', label = 'Reload Wall Styles', command = self.styleManager.createWallStyleAttributes) menuBar.addmenuitem('Style', 'command', 'Reload Building Style Palettes', label = 'Reload Bldg Styles', command = self.styleManager.createBuildingStyleAttributes) menuBar.addmenu('Help', 'Level 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) self.editMenu = Pmw.ComboBox( menuFrame, labelpos = W, label_text = 'Edit Mode:', entry_width = 18, selectioncommand = self.levelEditor.setEditMode, history = 0, scrolledlist_items = NEIGHBORHOODS) self.editMenu.selectitem(NEIGHBORHOODS[0]) self.editMenu.pack(side = LEFT, expand = 0) # Create the notebook pages self.notebook = Pmw.NoteBook(hull) self.notebook.pack(fill = BOTH, expand = 1) streetsPage = self.notebook.add('Streets') toonBuildingsPage = self.notebook.add('Toon Bldgs') landmarkBuildingsPage = self.notebook.add('Landmark Bldgs') # suitBuildingsPage = self.notebook.add('Suit Buildings') propsPage = self.notebook.add('Props') signPage = self.notebook.add('Signs') suitPathPage = self.notebook.add('Paths') battleCellPage = self.notebook.add('Cells') sceneGraphPage = self.notebook.add('SceneGraph') self.notebook['raisecommand'] = self.updateInfo # STREETS Label(streetsPage, text = 'Streets', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) self.addStreetButton = Button( streetsPage, text = 'ADD STREET', command = self.addStreet) self.addStreetButton.pack(fill = X, padx = 20, pady = 10) self.streetSelector = Pmw.ComboBox( streetsPage, dropdown = 0, listheight = 200, labelpos = W, label_text = 'Street type:', label_width = 12, label_anchor = W, entry_width = 30, selectioncommand = self.setStreetModuleType, scrolledlist_items = map(lambda s: s[7:], self.styleManager.getCatalogCodes( 'street')) ) self.streetModuleType = self.styleManager.getCatalogCode('street',0) self.streetSelector.selectitem(self.streetModuleType[7:]) self.streetSelector.pack(expand = 1, fill = BOTH) # TOON BUILDINGS Label(toonBuildingsPage, text = 'Toon Buildings', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) self.addToonBuildingButton = Button( toonBuildingsPage, text = 'ADD TOON BUILDING', command = self.addFlatBuilding) self.addToonBuildingButton.pack(fill = X, padx = 20, pady = 10) self.toonBuildingSelector = Pmw.ComboBox( toonBuildingsPage, dropdown = 0, listheight = 200, labelpos = W, label_width = 12, label_anchor = W, label_text = 'Bldg type:', entry_width = 30, selectioncommand = self.setFlatBuildingType, scrolledlist_items = (['random'] + BUILDING_TYPES) ) bf = Frame(toonBuildingsPage) Label(bf, text = 'Building Height:').pack(side = LEFT, expand = 0) self.heightMode = IntVar() self.heightMode.set(20) self.tenFootButton = Radiobutton( bf, text = '10 ft', value = 10, variable = self.heightMode, command = self.setFlatBuildingHeight) self.tenFootButton.pack(side = LEFT, expand = 1, fill = X) self.twentyFootButton = Radiobutton( bf, text = '20 ft', value = 20, variable = self.heightMode, command = self.setFlatBuildingHeight) self.twentyFootButton.pack(side = LEFT, expand = 1, fill = X) self.twentyFiveFootButton = Radiobutton( bf, text = '25 ft', value = 25, variable = self.heightMode, command = self.setFlatBuildingHeight) self.twentyFiveFootButton.pack(side = LEFT, expand = 1, fill = X) self.thirtyFootButton = Radiobutton( bf, text = '30 ft', value = 30, variable = self.heightMode, command = self.setFlatBuildingHeight) self.thirtyFootButton.pack(side = LEFT, expand = 1, fill = X) bf.pack(fill = X) self.toonBuildingType = 'random' self.toonBuildingSelector.selectitem(self.toonBuildingType) self.toonBuildingSelector.pack(expand = 1, fill = BOTH) # LANDMARK BUILDINGS Label(landmarkBuildingsPage, text = 'Landmark Buildings', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) self.landmarkHQIntVar = IntVar() self.landmarkHQIntVar.set(0) self.landmarkHQButton = Checkbutton( landmarkBuildingsPage, text = 'HQ', variable=self.landmarkHQIntVar, command=self.setLandmarkHQ) self.landmarkHQButton.pack(side = LEFT, expand = 1, fill = X) self.addLandmarkBuildingButton = Button( landmarkBuildingsPage, text = 'ADD LANDMARK BUILDING', command = self.addLandmark) self.addLandmarkBuildingButton.pack(fill = X, padx = 20, pady = 10) self.landmarkBuildingSelector = Pmw.ComboBox( landmarkBuildingsPage, dropdown = 0, listheight = 200, labelpos = W, label_width = 12, label_anchor = W, label_text = 'Bldg type:', entry_width = 30, selectioncommand = self.setLandmarkType, scrolledlist_items = map(lambda s: s[14:], self.styleManager.getCatalogCodes( 'toon_landmark')) ) self.landmarkType = self.styleManager.getCatalogCode( 'toon_landmark',0) self.landmarkBuildingSelector.selectitem( self.styleManager.getCatalogCode('toon_landmark',0)[14:]) self.landmarkBuildingSelector.pack(expand = 1, fill = BOTH) # SIGNS Label(signPage, text = 'Signs', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) self.currentSignDNA=None self.currentBaselineDNA=None self.levelEditor.selectedNodePathHookHooks.append(self.updateSignPage) gridFrame = Frame(signPage) signSelectedFrame = Frame(gridFrame) self.currentBaselineIndex=0 self.baselineMenu = Pmw.ComboBox( signSelectedFrame, labelpos = W, label_text = 'Selected:', entry_width = 24, selectioncommand = self.selectSignBaseline, history = 0, # unique = 0, scrolledlist_items = ['']) self.baselineMenu.selectitem(self.currentBaselineIndex) self.baselineMenu.pack(side = LEFT, expand = 1, fill = X) self.baselineAddButton = Button( signSelectedFrame, text="Add Baseline", command=self.addBaseline) self.baselineAddButton.pack(side = LEFT, expand = 1, fill = X) self.baselineDeleteButton = Button( signSelectedFrame, text="Del", command=self.deleteSignItem) self.baselineDeleteButton.pack(side = LEFT, expand = 1, fill = X) signSelectedFrame.grid(row=0, column=0, columnspan=6) self.baselineString=StringVar() self.baselineString.trace("wu", self.signBaselineTrace) self.baselineTextBox = Entry( gridFrame, width = 24, textvariable=self.baselineString) self.baselineTextBox.grid(row=1, column=0, columnspan=3) fontList = [""]+self.styleManager.getCatalogCodes('font') self.fontMenu = Pmw.ComboBox( gridFrame, labelpos = W, label_text = 'Font:', entry_width = 12, selectioncommand = self.setSignBaslineFont, history = 0, scrolledlist_items = fontList) self.fontMenu.selectitem(0) self.fontMenu.grid(row=1, column=3, columnspan=3) graphicList = self.styleManager.getCatalogCodes('graphic') self.graphicMenu = Pmw.ComboBox( gridFrame, labelpos = W, label_text = 'Add Graphic:', entry_width = 24, selectioncommand = self.addSignGraphic, history = 0, scrolledlist_items = graphicList) self.graphicMenu.selectitem(0) self.graphicMenu.grid(row=2, column=0, columnspan=4) signButtonFrame = Frame(gridFrame) self.bigFirstLetterIntVar = IntVar() self.bigFirstLetterCheckbutton = Checkbutton( signButtonFrame, text = 'Big First Letter', variable=self.bigFirstLetterIntVar, command=self.setBigFirstLetter) self.bigFirstLetterCheckbutton.pack( side = LEFT, expand = 1, fill = X) self.allCapsIntVar = IntVar() self.allCapsCheckbutton = Checkbutton( signButtonFrame, text = 'All Caps', variable=self.allCapsIntVar, command=self.setAllCaps) self.allCapsCheckbutton.pack(side = LEFT, expand = 1, fill = X) self.dropShadowIntVar = IntVar() self.dropShadowCheckbutton = Checkbutton( signButtonFrame, text = 'Drop Shadow', variable=self.dropShadowIntVar, command=self.setDropShadow) self.dropShadowCheckbutton.pack(side = LEFT, expand = 1, fill = X) signButtonFrame.grid(row=3, column=0, columnspan=6) self.addKernFloater = Floater.Floater( gridFrame, text='Kern', #maxVelocity=1.0, command=self.setSignBaselineKern) self.addKernFloater.grid(row=4, column=0, rowspan=2, columnspan=3, sticky = EW) self.addWiggleFloater = Floater.Floater( gridFrame, text='Wiggle', #maxVelocity=10.0, command=self.setSignBaselineWiggle) self.addWiggleFloater.grid(row=6, column=0, rowspan=2, columnspan=3, sticky = EW) self.addStumbleFloater = Floater.Floater( gridFrame, text='Stumble', #maxVelocity=1.0, command=self.setSignBaselineStumble) self.addStumbleFloater.grid(row=8, column=0, rowspan=2, columnspan=3, sticky = EW) self.addStompFloater = Floater.Floater( gridFrame, text='Stomp', #maxVelocity=1.0, command=self.setSignBaselineStomp) self.addStompFloater.grid(row=10, column=0, rowspan=2, columnspan=3, sticky = EW) self.addCurveFloater = Floater.Floater( gridFrame, text='Curve', #maxVelocity = 1.0, command=self.setSignBaselineCurve) self.addCurveFloater.grid(row=12, column=0, rowspan=2, columnspan=3, sticky = EW) self.addXFloater = Floater.Floater( gridFrame, text='X', #maxVelocity=1.0, command=self.setDNATargetX) self.addXFloater.grid(row=4, column=3, rowspan=2, columnspan=3, sticky = EW) self.addZFloater = Floater.Floater( gridFrame, text='Z', #maxVelocity=1.0, command=self.setDNATargetZ) self.addZFloater.grid(row=6, column=3, rowspan=2, columnspan=3, sticky = EW) self.addScaleXFloater = Floater.Floater( gridFrame, text='Scale X', #maxVelocity=1.0, command=self.setDNATargetScaleX) self.addScaleXFloater.grid(row=8, column=3, rowspan=2, columnspan=3, sticky = EW) self.addScaleZFloater = Floater.Floater( gridFrame, text='Scale Z', #maxVelocity=1.0, command=self.setDNATargetScaleZ) self.addScaleZFloater.grid(row=10, column=3, rowspan=2, columnspan=3, sticky = EW) self.addRollFloater = Floater.Floater( gridFrame, text='Roll', #maxVelocity=10.0, command=self.setDNATargetRoll) self.addRollFloater.grid(row=12, column=3, rowspan=2, columnspan=3, sticky = EW) gridFrame.pack(fill=BOTH) # PROPS Label(propsPage, text = 'Props', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) self.addPropsButton = Button( propsPage, text = 'ADD PROP', command = self.addProp) self.addPropsButton.pack(fill = X, padx = 20, pady = 10) self.propSelector = Pmw.ComboBox( propsPage, dropdown = 0, listheight = 200, labelpos = W, label_width = 12, label_anchor = W, label_text = 'Prop type:', entry_width = 30, selectioncommand = self.setPropType, scrolledlist_items = self.styleManager.getCatalogCodes('prop') ) self.propType = self.styleManager.getCatalogCode('prop',0) self.propSelector.selectitem( self.styleManager.getCatalogCode('prop',0)) self.propSelector.pack(expand = 1, fill = BOTH) # SUIT PATHS Label(suitPathPage, text = 'Suit Paths', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) spButtons = Frame(suitPathPage) self.fPaths = IntVar() self.fPaths.set(0) self.pathButton = Checkbutton(spButtons, text = 'Show Paths', width = 6, variable = self.fPaths, command = self.toggleSuitPaths) self.pathButton.pack(side = LEFT, expand = 1, fill = X) self.zoneColor = IntVar() self.zoneColor.set(0) self.colorZoneButton1 = Checkbutton( spButtons, text = 'Color Zones', width = 6, variable = self.zoneColor, command = self.levelEditor.toggleZoneColors) self.colorZoneButton1.pack(side = LEFT, expand = 1, fill = X) spButtons.pack(fill = X) spButtons = Frame(suitPathPage) Label(spButtons, text = 'Highlight:').pack(side = LEFT, fill = X) self.highlightConnectedButton = Button( spButtons, text = 'Forward', width = 6, command = self.levelEditor.highlightConnected) self.highlightConnectedButton.pack(side = LEFT, expand = 1, fill = X) self.highlightConnectedButton2 = Button( spButtons, text = 'Connected', width = 6, command = lambda s = self: s.levelEditor.highlightConnected(fReversePath = 1)) self.highlightConnectedButton2.pack(side = LEFT, expand = 1, fill = X) self.clearHighlightButton = Button( spButtons, text = 'Clear', width = 6, command = self.levelEditor.clearPathHighlights) self.clearHighlightButton.pack(side = LEFT, expand = 1, fill = X) spButtons.pack(fill = X, pady = 4) self.suitPointSelector = Pmw.ComboBox( suitPathPage, dropdown = 0, listheight = 200, labelpos = W, label_text = 'Point type:', label_width = 12, label_anchor = W, entry_width = 30, selectioncommand = self.setSuitPointType, scrolledlist_items = ['street', 'front door', 'side door'] ) self.suitPointSelector.selectitem('street') self.suitPointSelector.pack(expand = 1, fill = BOTH) # BATTLE CELLS Label(battleCellPage, text = 'Battle Cells', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) bcButtons = Frame(battleCellPage) self.fCells = IntVar() self.fCells.set(0) self.cellButton = Checkbutton(bcButtons, text = 'Show Cells', width = 6, variable = self.fCells, command = self.toggleBattleCells) self.cellButton.pack(side = LEFT, expand = 1, fill = X) self.colorZoneButton2 = Checkbutton( bcButtons, text = 'Color Zones', width = 6, variable = self.zoneColor, command = self.levelEditor.toggleZoneColors) self.colorZoneButton2.pack(side = LEFT, expand = 1, fill = X) bcButtons.pack(fill = X) self.battleCellSelector = Pmw.ComboBox( battleCellPage, dropdown = 0, listheight = 200, labelpos = W, label_text = 'Cell type:', label_width = 12, label_anchor = W, entry_width = 30, selectioncommand = self.setBattleCellType, scrolledlist_items = ['20w 20l', '20w 30l', '30w 20l', '30w 30l'] ) self.battleCellSelector.selectitem('20w 20l') self.battleCellSelector.pack(expand = 1, fill = BOTH) # SCENE GRAPH EXPLORER Label(sceneGraphPage, text = 'Level Scene Graph', font=('MSSansSerif', 14, 'bold')).pack(expand = 0) self.sceneGraphExplorer = SceneGraphExplorer( parent = sceneGraphPage, nodePath = self.levelEditor, menuItems = ['Add Group', 'Add Vis Group']) self.sceneGraphExplorer.pack(expand = 1, fill = BOTH) # Compact down notebook self.notebook.setnaturalsize() self.colorEntry = VectorWidgets.ColorEntry( hull, text = 'Select Color', command = self.updateSelectedObjColor) self.colorEntry.menu.add_command( label = 'Save Color', command = self.levelEditor.saveColor) self.colorEntry.pack(fill = X) buttonFrame = Frame(hull) self.fMapVis = IntVar() self.fMapVis.set(0) self.mapSnapButton = Checkbutton(buttonFrame, text = 'Map Vis', width = 6, variable = self.fMapVis, command = self.toggleMapVis) #self.mapSnapButton.pack(side = LEFT, expand = 1, fill = X) self.fXyzSnap = IntVar() self.fXyzSnap.set(1) self.xyzSnapButton = Checkbutton(buttonFrame, text = 'XyzSnap', width = 6, variable = self.fXyzSnap, command = self.toggleXyzSnap) self.xyzSnapButton.pack(side = LEFT, expand = 1, fill = X) self.fHprSnap = IntVar() self.fHprSnap.set(1) self.hprSnapButton = Checkbutton(buttonFrame, text = 'HprSnap', width = 6, variable = self.fHprSnap, command = self.toggleHprSnap) self.hprSnapButton.pack(side = LEFT, expand = 1, fill = X) def toggleWidgetHandles(s = self): if s.fPlaneSnap.get(): direct.widget.disableHandles(['x-ring', 'x-disc', 'y-ring', 'y-disc', 'z-post']) else: direct.widget.enableHandles('all') self.fPlaneSnap = IntVar() self.fPlaneSnap.set(1) self.planeSnapButton = Checkbutton(buttonFrame, text = 'PlaneSnap', width = 6, variable = self.fPlaneSnap, command = toggleWidgetHandles) self.planeSnapButton.pack(side = LEFT, expand = 1, fill = X) self.fGrid = IntVar() self.fGrid.set(0) direct.gridButton = Checkbutton(buttonFrame, text = 'Show Grid', width = 6, variable = self.fGrid, command = self.toggleGrid) direct.gridButton.pack(side = LEFT, expand = 1, fill = X) buttonFrame.pack(fill = X) buttonFrame4 = Frame(hull) self.driveMode = IntVar() self.driveMode.set(1) self.directModeButton = Radiobutton( buttonFrame4, text = 'DIRECT Fly', value = 1, variable = self.driveMode, command = self.levelEditor.useDirectFly) self.directModeButton.pack(side = LEFT, fill = X, expand = 1) self.driveModeButton = Radiobutton( buttonFrame4, text = 'Drive', value = 0, variable = self.driveMode, command = self.levelEditor.useDriveMode) self.driveModeButton.pack(side = LEFT, fill = X, expand = 1) self.fColl = IntVar() self.fColl.set(1) direct.collButton = Checkbutton( buttonFrame4, text = 'Collide', variable = self.fColl, command = self.levelEditor.toggleCollisions) direct.collButton.pack(side = LEFT, expand = 1, fill = X) self.fVis = IntVar() self.fVis.set(1) direct.visButton = Checkbutton( buttonFrame4, text = 'Visibility', variable = self.fVis, command = self.levelEditor.toggleVisibility) direct.visButton.pack(side = LEFT, expand = 1, fill = X) self.fVisZones = IntVar() self.fVisZones.set(visualizeZones) direct.visZonesButton = Checkbutton( buttonFrame4, text = 'Show Zones', variable = self.fVisZones) direct.visZonesButton.pack(side = LEFT, expand = 1, fill = X) buttonFrame4.pack(fill = X, padx = 5) # Make sure input variables processed self.initialiseoptions(LevelEditorPanel) def updateInfo(self, page): if page == 'Signs': self.updateSignPage() def toggleGrid(self): if self.fGrid.get(): direct.grid.enable() else: direct.grid.disable() def toggleSuitPaths(self): if self.fPaths.get(): self.levelEditor.showSuitPaths() else: self.levelEditor.hideSuitPaths() def toggleBattleCells(self): if self.fCells.get(): self.levelEditor.showBattleCells() else: self.levelEditor.hideBattleCells() def toggleXyzSnap(self): direct.grid.setXyzSnap(self.fXyzSnap.get()) def toggleHprSnap(self): direct.grid.setHprSnap(self.fXyzSnap.get()) def toggleMapVis(self): self.levelEditor.toggleMapVis(self.fMapVis.get()) def setStreetModuleType(self,name): self.streetModuleType = 'street_' + name self.levelEditor.setCurrent('street_texture', self.streetModuleType) def addStreet(self): self.levelEditor.addStreet(self.streetModuleType) def setFlatBuildingType(self,name): self.toonBuildingType = name self.levelEditor.setCurrent('building_type', self.toonBuildingType) def setFlatBuildingHeight(self): height = self.heightMode.get() self.levelEditor.setCurrent('building_height', height) self.updateHeightList(height) def updateHeightList(self, height): # Update combo box heightList = self.levelEditor.getList(`height` + '_ft_wall_heights') self.toonBuildingSelector.setlist( ['random'] + map(createHeightCode,heightList)) self.toonBuildingSelector.selectitem(0) self.toonBuildingSelector.invoke() def addFlatBuilding(self): self.levelEditor.addFlatBuilding(self.toonBuildingType) def setLandmarkType(self,name): self.landmarkType = 'toon_landmark_' + name self.levelEditor.setCurrent('toon_landmark_texture', self.landmarkType) def signPanelSync(self): self.baselineMenu.delete(1, END) sign=self.findSignFromDNARoot() if not sign: return baselineList = DNAGetChildren(sign, DNA_SIGN_BASELINE) for baseline in baselineList: s=DNAGetBaselineString(baseline) self.baselineMenu.insert(END, s) self.baselineMenu.selectitem(0) self.selectSignBaseline(0) def updateSignPage(self): if (self.notebook.getcurselection() == 'Signs'): sign=self.findSignFromDNARoot() # Only update if it's not the current sign: if (self.currentSignDNA != sign): self.currentSignDNA = sign self.signPanelSync() def findSignFromDNARoot(self): dnaRoot=self.levelEditor.selectedDNARoot if not dnaRoot: return objClass=DNAGetClassType(dnaRoot) if (objClass.eq(DNA_LANDMARK_BUILDING) or objClass.eq(DNA_PROP)): target=DNAGetChildRecursive(dnaRoot, DNA_SIGN) return target def selectSignBaseline(self, val): if not self.currentSignDNA: return # Temporarily undefine DNATarget (this will speed # up setting the values, because the callbacks won't # call self.levelEditor.replaceSelected() with each # setting): self.levelEditor.DNATarget=None self.currentBaselineDNA=None target=None index=self.currentBaselineIndex=int((self.baselineMenu.curselection())[0]) if (index==0): target=self.currentSignDNA # Unset some ui elements: self.baselineString.set('') self.fontMenu.selectitem(0) self.addCurveFloater.set(0) self.addKernFloater.set(0) self.addWiggleFloater.set(0) self.addStumbleFloater.set(0) self.addStompFloater.set(0) self.bigFirstLetterIntVar.set(0) self.allCapsIntVar.set(0) self.dropShadowIntVar.set(0) else: target=DNAGetChild(self.currentSignDNA, DNA_SIGN_BASELINE, index-1) if target: # Update panel info: self.baselineString.set(DNAGetBaselineString(target)) self.fontMenu.selectitem(target.getCode()) try: val = 1.0/target.getWidth() except ZeroDivisionError: val = 0.0 self.addCurveFloater.set(val) self.addKernFloater.set(target.getKern()) self.addWiggleFloater.set(target.getWiggle()) self.addStumbleFloater.set(target.getStumble()) self.addStompFloater.set(target.getStomp()) flags=target.getFlags() self.bigFirstLetterIntVar.set('b' in flags) self.allCapsIntVar.set('c' in flags) self.dropShadowIntVar.set('d' in flags) self.currentBaselineDNA=target if target: pos=target.getPos() self.addXFloater.set(pos[0]) self.addZFloater.set(pos[2]) scale=target.getScale() self.addScaleXFloater.set(scale[0]) self.addScaleZFloater.set(scale[2]) hpr=target.getHpr() self.addRollFloater.set(hpr[2]) self.levelEditor.DNATarget=target def deleteSignItem(self): """Delete the selected sign or sign baseline""" if (self.currentBaselineDNA): # Remove the baseline: assert(int((self.baselineMenu.curselection())[0]) == self.currentBaselineIndex) DNARemoveChildOfClass(self.currentSignDNA, DNA_SIGN_BASELINE, self.currentBaselineIndex-1) self.baselineMenu.delete(self.currentBaselineIndex) self.baselineMenu.selectitem(0) self.currentBaselineIndex=0 self.currentBaselineDNA=None self.selectSignBaseline(0) self.levelEditor.replaceSelected() elif (self.currentSignDNA): # Remove the sign: assert(int((self.baselineMenu.curselection())[0]) == 0) le = self.levelEditor le.removeSign(le.DNATarget, le.DNATargetParent) self.currentBaselineDNA=None self.currentSignDNA=None self.levelEditor.replaceSelected() def signBaselineTrace(self, a, b, mode): #print self, a, b, mode, self.baselineString.get() baseline=self.currentBaselineDNA if baseline: s=self.baselineString.get() self.setBaselineString(s) def addSignGraphic(self, code): """ Create a new baseline with a graphic and add it to the current sign """ sign=self.findSignFromDNARoot() if sign: graphic=DNASignGraphic() graphic.setCode(code) baseline=DNASignBaseline() baseline.add(graphic) sign.add(baseline) # Show the UI to the new baseline: self.levelEditor.DNATarget=baseline self.baselineMenu.insert(END, '['+code+']') current=self.baselineMenu.size()-1 self.baselineMenu.selectitem(current) self.selectSignBaseline(current) self.levelEditor.replaceSelected() def addBaseline(self): sign=self.findSignFromDNARoot() if sign: baseline=DNASignBaseline() text="Zoo" DNASetBaselineString(baseline, text) sign.add(baseline) # Show the UI to the new baseline: self.levelEditor.DNATarget=baseline self.baselineMenu.insert(END, text) current=self.baselineMenu.size()-1 self.baselineMenu.selectitem(current) self.selectSignBaseline(current) self.levelEditor.replaceSelected() def addBaselineItem(self): pass def selectSignBaselineItem(self, val): baseline=self.currentBaselineDNA if baseline: baseline.setCode(val) self.levelEditor.replaceSelected() def setSignBaselineStyle(self, val): baseline=self.currentBaselineDNA if baseline == None: print "\n\nbaseline == None" return #skyler: This isn't working yet. # As a workaround, select the baseline from the tk panel. # Try to find the first baseline in the sign: sign=self.findSignFromDNARoot() if sign: self.currentSignDNA = sign baseline=DNAGetChild(sign, DNA_SIGN_BASELINE) if baseline and val: self.levelEditor.DNATarget=baseline self.currentBaselineDNA=baseline settings=val self.levelEditor.replaceSelectedEnabled=0 # Don't set string: self.baselineString.set('') if settings['curve'] != None: self.addCurveFloater.set(settings['curve']) if settings['kern'] != None: self.addKernFloater.set(settings['kern']) if settings['wiggle'] != None: self.addWiggleFloater.set(settings['wiggle']) if settings['stumble'] != None: self.addStumbleFloater.set(settings['stumble']) if settings['stomp'] != None: self.addStompFloater.set(settings['stomp']) flags=settings['flags'] if flags != None: self.bigFirstLetterIntVar.set('b' in flags) self.setBigFirstLetter(); self.allCapsIntVar.set('c' in flags) self.setAllCaps() self.dropShadowIntVar.set('d' in flags) self.setDropShadow() code = settings['code'] if code != None: self.fontMenu.selectitem(code) self.setSignBaslineFont(code) if settings['x'] != None: self.addXFloater.set(settings['x']) if settings['z'] != None: self.addZFloater.set(settings['z']) if settings['scaleX'] != None: self.addScaleXFloater.set(settings['scaleX']) if settings['scaleZ'] != None: self.addScaleZFloater.set(settings['scaleZ']) if settings['roll'] != None: self.addRollFloater.set(settings['roll']) color = settings['color'] if color != None: #self.updateSelectedObjColor(settings['color']) self.setCurrentColor(color) self.setResetColor(color) baseline.setColor(color) self.levelEditor.replaceSelectedEnabled=1 self.levelEditor.replaceSelected() def setBaselineString(self, val): baseline=self.currentBaselineDNA if baseline: DNASetBaselineString(baseline, val); self.baselineMenu.delete(self.currentBaselineIndex) self.baselineMenu.insert(self.currentBaselineIndex, val) self.baselineMenu.selectitem(self.currentBaselineIndex) self.levelEditor.replaceSelected() def adjustBaselineFlag(self, newValue, flagChar): baseline=self.currentBaselineDNA if baseline: flags=baseline.getFlags() if newValue: if not flagChar in flags: # Add the flag: baseline.setFlags(flags+flagChar) elif flagChar in flags: # Remove the flag: flags=string.join(flags.split(flagChar), '') baseline.setFlags(flags) self.levelEditor.replaceSelected() def setLandmarkHQ(self): if self.levelEditor.lastLandmarkBuildingDNA: self.levelEditor.lastLandmarkBuildingDNA.setHq(self.landmarkHQIntVar.get()) def setBigFirstLetter(self): self.adjustBaselineFlag(self.bigFirstLetterIntVar.get(), 'b') def setAllCaps(self): self.adjustBaselineFlag(self.allCapsIntVar.get(), 'c') def setDropShadow(self): self.adjustBaselineFlag(self.dropShadowIntVar.get(), 'd') def setSignBaslineFont(self, val): target=self.levelEditor.DNATarget if target and (DNAGetClassType(target).eq(DNA_SIGN_BASELINE) or DNAGetClassType(target).eq(DNA_SIGN_TEXT)): target.setCode(val) self.levelEditor.replaceSelected() def setSignBaselineCurve(self, val): baseline=self.currentBaselineDNA if baseline: try: val=1.0/val except ZeroDivisionError: val=0.0 baseline.setWidth(val) baseline.setHeight(val) self.levelEditor.replaceSelected() def setSignBaselineKern(self, val): baseline=self.currentBaselineDNA if baseline: baseline.setKern(val) self.levelEditor.replaceSelected() def setSignBaselineWiggle(self, val): baseline=self.currentBaselineDNA if baseline: baseline.setWiggle(val) self.levelEditor.replaceSelected() def setSignBaselineStumble(self, val): baseline=self.currentBaselineDNA if baseline: baseline.setStumble(val) self.levelEditor.replaceSelected() def setSignBaselineStomp(self, val): baseline=self.currentBaselineDNA if baseline: baseline.setStomp(val) self.levelEditor.replaceSelected() def setDNATargetX(self, val): target=self.levelEditor.DNATarget if target: pos=target.getPos() pos=VBase3(val, pos[1], pos[2]) target.setPos(pos) self.levelEditor.replaceSelected() def setDNATargetZ(self, val): target=self.levelEditor.DNATarget if target: pos=target.getPos() pos=VBase3(pos[0], pos[1], val) target.setPos(pos) self.levelEditor.replaceSelected() def setDNATargetScaleX(self, val): target=self.levelEditor.DNATarget if target: scale=target.getScale() scale=VBase3(val, scale[1], scale[2]) target.setScale(scale) self.levelEditor.replaceSelected() def setDNATargetScaleZ(self, val): target=self.levelEditor.DNATarget if target: scale=target.getScale() scale=VBase3(scale[0], scale[1], val) target.setScale(scale) self.levelEditor.replaceSelected() def setDNATargetRoll(self, val): target=self.levelEditor.DNATarget if target: hpr=target.getHpr() hpr=VBase3(hpr[0], hpr[1], val) target.setHpr(hpr) self.levelEditor.replaceSelected() def addLandmark(self): self.levelEditor.addLandmark(self.landmarkType, self.landmarkHQIntVar.get()) def setPropType(self,name): self.propType = name self.levelEditor.setCurrent('prop_texture', self.propType) def addProp(self): self.levelEditor.addProp(self.propType) def updateSelectedWallWidth(self, strVal): self.levelEditor.updateSelectedWallWidth(string.atof(strVal)) def setCurrentColor(self, colorVec, fUpdate = 0): # Turn on/off update of selected before updating entry self.fUpdateSelected = fUpdate self.colorEntry.set([int(colorVec[0] * 255.0), int(colorVec[1] * 255.0), int(colorVec[2] * 255.0), 255]) def setResetColor(self, colorVec): self.colorEntry['resetValue'] = ( [int(colorVec[0] * 255.0), int(colorVec[1] * 255.0), int(colorVec[2] * 255.0), 255]) def setSuitPointType(self,name): if (name == "street"): self.levelEditor.currentSuitPointType = DNASuitPoint.STREETPOINT elif (name == "front door"): self.levelEditor.currentSuitPointType = DNASuitPoint.FRONTDOORPOINT elif (name == "side door"): self.levelEditor.currentSuitPointType = DNASuitPoint.SIDEDOORPOINT print self.levelEditor.currentSuitPointType def setBattleCellType(self,name): self.levelEditor.currentBattleCellType = name def updateSelectedObjColor(self, color): try: obj = self.levelEditor.DNATarget if self.fUpdateSelected and (obj != None): objClass = DNAGetClassType(obj) if ((objClass.eq(DNA_WALL)) or (objClass.eq(DNA_WINDOWS)) or (objClass.eq(DNA_DOOR)) or (objClass.eq(DNA_FLAT_DOOR)) or (objClass.eq(DNA_CORNICE)) or (objClass.eq(DNA_PROP)) or (objClass.eq(DNA_SIGN)) or (objClass.eq(DNA_SIGN_BASELINE)) or (objClass.eq(DNA_SIGN_TEXT)) or (objClass.eq(DNA_SIGN_GRAPHIC)) ): self.levelEditor.setDNATargetColor( VBase4((color[0]/255.0), (color[1]/255.0), (color[2]/255.0), 1.0)) except AttributeError: pass # Default is to update selected self.fUpdateSelected = 1 def toggleBalloon(self): if self.toggleBalloonVar.get(): self.balloon.configure(state = 'balloon') else: self.balloon.configure(state = 'none') class VisGroupsEditor(Pmw.MegaToplevel): def __init__(self, levelEditor, visGroups = ['None'], parent = None, **kw): INITOPT = Pmw.INITOPT optiondefs = ( ('title', 'Visability Groups Editor', None), ) self.defineoptions(kw, optiondefs) Pmw.MegaToplevel.__init__(self, parent, title = self['title']) self.levelEditor = levelEditor self.visGroups = visGroups self.visGroupNames = map(lambda pair: pair[1].getName(), self.visGroups) # Initialize dictionary of visibility relationships self.visDict = {} # Group we are currently setting visGroups for self.target = None # Flag to enable/disable toggleVisGroup command self.fCommand = 1 # Handle to the toplevels hull hull = self.component('hull') balloon = self.balloon = Pmw.Balloon(hull) # Start with balloon help disabled 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 Groups Editor', 'Visability Groups Editor Operations') menuBar.addmenuitem('Vis Groups Editor', 'command', 'Exit Visability Groups Editor', label = 'Exit', command = self.preDestroy) menuBar.addmenu('Help', 'Visability Groups 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) # Create a combo box to choose target vis group self.targetSelector = Pmw.ComboBox( hull, labelpos = W, label_text = 'Target Vis Group:', entry_width = 12, selectioncommand = self.selectVisGroup, scrolledlist_items = self.visGroupNames) self.targetSelector.selectitem(self.visGroupNames[0]) self.targetSelector.pack(expand = 1, fill = X) # Scrolled frame to hold radio selector 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) # Add vis groups selector self.selected = Pmw.RadioSelect(frame, selectmode=MULTIPLE, orient = VERTICAL, pady = 0, command = self.toggleVisGroup) for groupInfo in self.visGroups: nodePath = groupInfo[0] group = groupInfo[1] name = group.getName() self.selected.add(name, width = 12) # Assemble list of groups visible from this group visible = [] for i in range(group.getNumVisibles()): visible.append(group.getVisibleName(i)) visible.sort() self.visDict[name] = [nodePath, group, visible] # Pack the widget self.selected.pack(expand = 1, fill = X) # And make sure scrolled frame is happy sf.reposition() buttonFrame = Frame(hull) buttonFrame.pack(fill=X, expand = 1) self.showMode = IntVar() self.showMode.set(0) self.showAllButton = Radiobutton(buttonFrame, text = 'Show All', value = 0, indicatoron = 1, variable = self.showMode, command = self.refreshVisibility) self.showAllButton.pack(side = LEFT, fill = X, expand = 1) self.showActiveButton = Radiobutton(buttonFrame, text = 'Show Target', value = 1, indicatoron = 1, variable = self.showMode, command = self.refreshVisibility) self.showActiveButton.pack(side = LEFT, fill = X, expand = 1) # Make sure input variables processed self.initialiseoptions(VisGroupsEditor) # Switch to current target's list self.selectVisGroup(self.visGroupNames[0]) def selectVisGroup(self, target): print 'Setting vis options for group:', target # Record current target oldTarget = self.target # Record new target self.target = target # Deselect buttons from old target (first deactivating command) self.fCommand = 0 if oldTarget: visList = self.visDict[oldTarget][2] for group in visList: self.selected.invoke(self.selected.index(group)) # Now set buttons to reflect state of new target visList = self.visDict[target][2] for group in visList: self.selected.invoke(self.selected.index(group)) # Reactivate command self.fCommand = 1 # Update scene self.refreshVisibility() def toggleVisGroup(self, groupName, state): if self.fCommand: targetInfo = self.visDict[self.target] target = targetInfo[1] visList = targetInfo[2] groupNP = self.visDict[groupName][0] group = self.visDict[groupName][1] # MRM: Add change in visibility here # Show all vs. show active if state == 1: print 'Vis Group:', self.target, 'adding group:', groupName if groupName not in visList: visList.append(groupName) target.addVisible(groupName) # Update vis and color groupNP.show() groupNP.setColor(1,0,0,1) else: print 'Vis Group:', self.target, 'removing group:', groupName if groupName in visList: visList.remove(groupName) target.removeVisible(groupName) # Update vis and color if self.showMode.get() == 1: groupNP.hide() groupNP.clearColor() def refreshVisibility(self): # Get current visibility list for target targetInfo = self.visDict[self.target] visList = targetInfo[2] for key in self.visDict.keys(): groupNP = self.visDict[key][0] if key in visList: groupNP.show() if key == self.target: groupNP.setColor(0,1,0,1) else: groupNP.setColor(1,0,0,1) else: if self.showMode.get() == 0: groupNP.show() else: groupNP.hide() groupNP.clearColor() def preDestroy(self): # First clear level editor variable self.levelEditor.vgpanel = None self.destroy() def toggleBalloon(self): if self.toggleBalloonVar.get(): self.balloon.configure(state = 'balloon') else: self.balloon.configure(state = 'none') l = LevelEditor() run()