LevelEditor.py 129 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765276627672768276927702771277227732774277527762777277827792780278127822783278427852786278727882789279027912792279327942795279627972798279928002801280228032804280528062807280828092810281128122813281428152816281728182819282028212822282328242825282628272828282928302831283228332834283528362837283828392840284128422843284428452846284728482849285028512852285328542855285628572858285928602861286228632864286528662867286828692870287128722873287428752876287728782879288028812882288328842885288628872888288928902891289228932894289528962897289828992900290129022903290429052906290729082909291029112912291329142915291629172918291929202921292229232924292529262927292829292930293129322933293429352936293729382939294029412942294329442945294629472948294929502951295229532954295529562957295829592960296129622963296429652966296729682969297029712972297329742975297629772978297929802981298229832984298529862987298829892990299129922993299429952996299729982999300030013002300330043005300630073008300930103011301230133014301530163017301830193020302130223023302430253026302730283029303030313032303330343035303630373038303930403041304230433044304530463047304830493050305130523053305430553056305730583059306030613062306330643065306630673068306930703071307230733074307530763077307830793080308130823083308430853086308730883089309030913092309330943095309630973098309931003101310231033104310531063107310831093110311131123113311431153116311731183119312031213122312331243125312631273128312931303131313231333134313531363137313831393140314131423143314431453146314731483149315031513152315331543155315631573158315931603161316231633164316531663167316831693170317131723173317431753176317731783179318031813182318331843185318631873188318931903191319231933194319531963197319831993200320132023203320432053206320732083209321032113212321332143215321632173218321932203221322232233224322532263227322832293230323132323233323432353236323732383239324032413242324332443245324632473248324932503251325232533254325532563257325832593260326132623263326432653266326732683269327032713272
  1. from PandaObject import *
  2. from PieMenu import *
  3. from OnscreenText import *
  4. from Tkinter import *
  5. from DirectGeometry import *
  6. from SceneGraphExplorer import *
  7. from tkSimpleDialog import askstring
  8. from tkMessageBox import showinfo
  9. from tkFileDialog import *
  10. from whrandom import *
  11. import Pmw
  12. import EntryScale
  13. import VectorWidgets
  14. import string
  15. import os
  16. import __builtin__
  17. # Colors used by all color menus
  18. DEFAULT_COLORS = [
  19. Vec4(1,1,1,1),
  20. Vec4(0.75, 0.75, 0.75, 1.0 ),
  21. Vec4(0.5, 0.5, 0.5, 1.0),
  22. Vec4(0.25, 0.25, 0.25, 1.0)]
  23. # The list of items with color attributes
  24. COLOR_TYPES = ['wall_color', 'window_color',
  25. 'window_awning_color', 'door_color',
  26. 'door_awning_color', 'cornice_color',
  27. 'prop_color']
  28. # The list of dna components maintained in the style attribute dictionary
  29. DNA_TYPES = ['wall', 'window', 'door', 'cornice', 'toon_landmark',
  30. 'prop', 'street']
  31. BUILDING_TYPES = ['10_10', '20', '10_20', '20_10', '10_10_10']
  32. # The list of neighborhoods to edit
  33. NEIGHBORHOODS = ['toontown_central', 'donalds_dock',
  34. 'minnies_melody_land', 'the_burrrgh']
  35. NEIGHBORHOODS = ['toontown_central', 'donalds_dock',]
  36. NEIGHBORHOOD_CODES = {'toontown_central': 'TT', 'donalds_dock': 'DD',
  37. 'minnies_melody_land': 'MM', 'the_burrrgh': 'BR'}
  38. OBJECT_SNAP_POINTS = {
  39. 'street_5x20': [(Vec3(5.0,0,0), Vec3(0)),
  40. (Vec3(0), Vec3(0))],
  41. 'street_10x20': [(Vec3(10.0,0,0), Vec3(0)),
  42. (Vec3(0), Vec3(0))],
  43. 'street_20x20': [(Vec3(20.0,0,0), Vec3(0)),
  44. (Vec3(0), Vec3(0))],
  45. 'street_40x20': [(Vec3(40.0,0,0), Vec3(0)),
  46. (Vec3(0), Vec3(0))],
  47. 'street_80x20': [(Vec3(80.0,0,0), Vec3(0)),
  48. (Vec3(0), Vec3(0))],
  49. 'street_5x40': [(Vec3(5.0,0,0), Vec3(0)),
  50. (Vec3(0), Vec3(0))],
  51. 'street_10x40': [(Vec3(10.0,0,0), Vec3(0)),
  52. (Vec3(0), Vec3(0))],
  53. 'street_20x40': [(Vec3(20.0,0,0), Vec3(0)),
  54. (Vec3(0), Vec3(0))],
  55. 'street_30x40': [(Vec3(30.0,0,0), Vec3(0)),
  56. (Vec3(0), Vec3(0))],
  57. 'street_40x40': [(Vec3(40.0,0,0), Vec3(0)),
  58. (Vec3(0), Vec3(0))],
  59. 'street_80x40': [(Vec3(80.0,0,0), Vec3(0)),
  60. (Vec3(0), Vec3(0))],
  61. 'street_angle_30': [(Vec3(0), Vec3(-30,0,0)),
  62. (Vec3(0), Vec3(0))],
  63. 'street_angle_45': [(Vec3(0), Vec3(-45,0,0)),
  64. (Vec3(0), Vec3(0))],
  65. 'street_angle_60': [(Vec3(0), Vec3(-60,0,0)),
  66. (Vec3(0), Vec3(0))],
  67. 'street_inner_corner': [(Vec3(20.0,0,0), Vec3(0)),
  68. (Vec3(0), Vec3(0))],
  69. 'street_outer_corner': [(Vec3(20.0,0,0), Vec3(0)),
  70. (Vec3(0), Vec3(0))],
  71. 'street_full_corner': [(Vec3(40.0,0,0), Vec3(0)),
  72. (Vec3(0), Vec3(0))],
  73. 'street_t_intersection': [(Vec3(40.0,0,0), Vec3(0)),
  74. (Vec3(0), Vec3(0))],
  75. 'street_y_intersection': [(Vec3(30.0,0,0), Vec3(0)),
  76. (Vec3(0), Vec3(0))],
  77. 'street_street_20x20': [(Vec3(20.0,0,0), Vec3(0)),
  78. (Vec3(0), Vec3(0))],
  79. 'street_street_40x40': [(Vec3(40.0,0,0), Vec3(0)),
  80. (Vec3(0), Vec3(0))],
  81. 'street_sidewalk_20x20': [(Vec3(20.0,0,0), Vec3(0)),
  82. (Vec3(0), Vec3(0))],
  83. 'street_sidewalk_40x40': [(Vec3(40.0,0,0), Vec3(0)),
  84. (Vec3(0), Vec3(0))],
  85. 'street_divided_transition': [(Vec3(40.0,0,0), Vec3(0)),
  86. (Vec3(0), Vec3(0))],
  87. 'street_divided_40x70': [(Vec3(40.0,0,0), Vec3(0)),
  88. (Vec3(0), Vec3(0))],
  89. 'street_stairs_40x10x5': [(Vec3(40.0,0,0), Vec3(0)),
  90. (Vec3(0), Vec3(0))],
  91. 'street_4way_intersection': [(Vec3(40.0,0,0), Vec3(0)),
  92. (Vec3(0), Vec3(0))],
  93. 'street_incline_40x40x5': [(Vec3(40.0,0,0), Vec3(0)),
  94. (Vec3(0), Vec3(0))],
  95. 'street_courtyard_70': [(Vec3(0.0,0,0), Vec3(45,0,0)),
  96. (Vec3(0), Vec3(0))],
  97. 'street_courtyard_70_exit': [(Vec3(0.0,0,0), Vec3(45,0,0)),
  98. (Vec3(0), Vec3(0))],
  99. 'street_courtyard_90': [(Vec3(0.0,0,0), Vec3(45,0,0)),
  100. (Vec3(0), Vec3(0))],
  101. 'street_courtyard_90_exit': [(Vec3(0.0,0,0), Vec3(45,0,0)),
  102. (Vec3(0), Vec3(0))],
  103. 'street_50_transition': [(Vec3(10.0,0,0), Vec3(0)),
  104. (Vec3(0), Vec3(0))],
  105. 'street_20x50': [(Vec3(20.0,0,0), Vec3(0)),
  106. (Vec3(0), Vec3(0))],
  107. 'street_40x50': [(Vec3(40.0,0,0), Vec3(0)),
  108. (Vec3(0), Vec3(0))],
  109. 'street_keyboard_10x40': [(Vec3(10.0,0,0), Vec3(0)),
  110. (Vec3(0), Vec3(0))],
  111. 'street_keyboard_20x40': [(Vec3(20.0,0,0), Vec3(0)),
  112. (Vec3(0), Vec3(0))],
  113. 'street_keyboard_40x40': [(Vec3(40.0,0,0), Vec3(0)),
  114. (Vec3(0), Vec3(0))],
  115. }
  116. SNAP_ANGLE = 15.0
  117. try:
  118. if dnaLoaded:
  119. pass
  120. except NameError:
  121. # DNAStorage instance for storing level DNA info
  122. __builtin__.DNASTORE = DNASTORE = DNAStorage()
  123. # Load the generic storage file
  124. loadDNAFile(DNASTORE, 'phase_4/dna/storage.dna', CSDefault, 1)
  125. # Load all the neighborhood specific storage files
  126. loadDNAFile(DNASTORE, 'phase_4/dna/storage_TT.dna', CSDefault, 1)
  127. loadDNAFile(DNASTORE, 'phase_6/dna/storage_DD.dna', CSDefault, 1)
  128. loadDNAFile(DNASTORE, 'phase_6/dna/storage_MM.dna', CSDefault, 1)
  129. loadDNAFile(DNASTORE, 'phase_6/dna/storage_BR.dna', CSDefault, 1)
  130. __builtin__.dnaLoaded = 1
  131. # Precompute class types for type comparisons
  132. DNA_CORNICE = DNACornice.getClassType()
  133. DNA_DOOR = DNADoor.getClassType()
  134. DNA_FLAT_BUILDING = DNAFlatBuilding.getClassType()
  135. DNA_NODE = DNANode.getClassType()
  136. DNA_GROUP = DNAGroup.getClassType()
  137. DNA_VIS_GROUP = DNAVisGroup.getClassType()
  138. DNA_LANDMARK_BUILDING = DNALandmarkBuilding.getClassType()
  139. DNA_NODE = DNANode.getClassType()
  140. DNA_PROP = DNAProp.getClassType()
  141. DNA_STREET = DNAStreet.getClassType()
  142. DNA_WALL = DNAWall.getClassType()
  143. DNA_WINDOWS = DNAWindows.getClassType()
  144. # DNA Utility functions (possible class extensions?)
  145. def DNARemoveChildren(dnaObject):
  146. """ Utility function to delete all the children of a DNANode """
  147. children = []
  148. for i in range(dnaObject.getNumChildren()):
  149. children.append(dnaObject.at(i))
  150. for child in children:
  151. dnaObject.remove(child)
  152. DNASTORE.removeDNAGroup(child)
  153. def DNARemoveChildOfClass(dnaNode, classType):
  154. """ Remove the first object of that type you come across """
  155. for i in range(dnaNode.getNumChildren()):
  156. child = dnaNode.at(i)
  157. if DNAClassEqual(child, classType):
  158. dnaNode.remove(child)
  159. DNASTORE.removeDNAGroup(child)
  160. return 1
  161. # None found
  162. return 0
  163. def DNAGetChild(dnaObject, type = DNA_NODE, childNum = 0):
  164. childCount = 0
  165. for i in range(dnaObject.getNumChildren()):
  166. child = dnaObject.at(i)
  167. if DNAClassEqual(child, type):
  168. if childCount == childNum:
  169. return child
  170. childCount = childCount + 1
  171. # Not found
  172. return None
  173. def DNAGetChildOfClass(dnaNode, classType):
  174. for i in range(dnaNode.getNumChildren()):
  175. child = dnaNode.at(i)
  176. if DNAClassEqual(child, classType):
  177. return child
  178. # Not found
  179. return None
  180. def DNAGetClassType(dnaObject):
  181. return dnaObject.__class__.getClassType()
  182. def DNAClassEqual(dnaObject, classType):
  183. return DNAGetClassType(dnaObject).eq(classType)
  184. def DNAIsDerivedFrom(dnaObject, classType):
  185. return DNAGetClassType(dnaObject).isDerivedFrom(classType)
  186. def DNAGetWallHeights(aDNAFlatBuilding):
  187. """ Get a list of wall heights for a given flat building """
  188. # Init variables
  189. heightList = []
  190. offsetList = []
  191. offset = 0.0
  192. # Compute wall heights
  193. for i in range(aDNAFlatBuilding.getNumChildren()):
  194. child = aDNAFlatBuilding.at(i)
  195. if DNAClassEqual(child, DNA_WALL):
  196. height = child.getHeight()
  197. heightList.append(height)
  198. offsetList.append(offset)
  199. offset = offset + height
  200. return heightList, offsetList
  201. class LevelEditor(NodePath, PandaObject):
  202. """Class used to create a Toontown LevelEditor object"""
  203. # Primary variables:
  204. # DNAData: DNA object holding DNA info about level
  205. # DNAToplevel: Top level DNA Node, all DNA objects descend from this node
  206. # NPTopLevel: Corresponding Node Path
  207. # DNAParent: Current DNA Node that new objects get added to
  208. # NPParent: Corresponding Node Path
  209. # selectedDNARoot: DNA Node of currently selected object
  210. # selectedNPRoot: Corresponding Node Path
  211. # DNATarget: Subcomponent being modified by Pie Menu
  212. def __init__(self):
  213. # Make the level editor a node path so that you can show/hide
  214. # The level editor separately from loading/saving the top level node
  215. # Initialize superclass
  216. NodePath.__init__(self)
  217. # Become the new node path
  218. self.assign(hidden.attachNewNode( NamedNode('LevelEditor')))
  219. # Create ancillary objects
  220. # Style manager for keeping track of styles/colors
  221. self.styleManager = LevelStyleManager()
  222. # Load neighborhood maps
  223. self.createLevelMaps()
  224. # Marker for showing next insertion point
  225. self.createInsertionMarker()
  226. # Create level Editor Panel
  227. self.panel = LevelEditorPanel(self)
  228. # Initialize LevelEditor variables DNAData, DNAToplevel, NPToplevel
  229. # DNAParent, NPParent, groupNum, lastAngle
  230. # Pass in the new toplevel group and don't clear out the old
  231. # toplevel group (since it doesn't exist yet)
  232. self.reset(fDeleteToplevel = 0, fCreateToplevel = 1)
  233. # The list of events the level editor responds to
  234. self.actionEvents = [
  235. # Actions in response to DIRECT operations
  236. ('selectedNodePath', self.selectedNodePathHook),
  237. ('deselectedNodePath', self.deselectedNodePathHook),
  238. ('manipulateObjectCleanup', self.updateSelectedPose),
  239. # Actions in response to Level Editor Panel operations
  240. ('SGENodePath_Select', self.select),
  241. ('SGENodePath_Set Name', self.getAndSetName),
  242. ('SGENodePath_Set Parent', self.setActiveParent),
  243. ('SGENodePath_Reparent', self.reparent),
  244. ('SGENodePath_Flash', self.flash),
  245. ('SGENodePath_Isolate', self.isolate),
  246. ('SGENodePath_Toggle Viz', self.toggleViz),
  247. ('SGENodePath_Add Group', self.addGroup),
  248. ('SGENodePath_Add Vis Group', self.addVisGroup),
  249. # Actions in response to Pie Menu interaction
  250. ('select_building_style', self.setBuildingStyle),
  251. ('select_building_type', self.setBuildingType),
  252. ('select_building_width', self.setBuildingWidth),
  253. ('select_cornice_color', self.setDNATargetColor),
  254. ('select_cornice_orientation', self.setDNATargetOrientation),
  255. ('select_cornice_texture', self.setDNATargetCode, ['cornice']),
  256. ('select_door_color', self.setDNATargetColor),
  257. ('select_door_orientation', self.setDNATargetOrientation),
  258. ('select_door_texture', self.setDNATargetCode, ['door']),
  259. ('select_door_awning_texture', self.setDNATargetCode,
  260. ['door_awning']),
  261. ('select_door_awning_color', self.setDNATargetColor),
  262. ('select_window_color', self.setDNATargetColor),
  263. ('select_window_count', self.setWindowCount),
  264. ('select_window_orientation', self.setDNATargetOrientation),
  265. ('select_window_texture', self.setDNATargetCode, ['windows']),
  266. ('select_window_awning_texture', self.setDNATargetCode,
  267. ['window_awning']),
  268. ('select_window_awning_color', self.setDNATargetColor),
  269. ('select_wall_style', self.setWallStyle),
  270. ('select_wall_color', self.setDNATargetColor),
  271. ('select_wall_orientation', self.setDNATargetOrientation),
  272. ('select_wall_texture', self.setDNATargetCode, ['wall']),
  273. ('select_toon_landmark_texture', self.setDNATargetCode,
  274. ['landmark']),
  275. ('select_toon_landmark_door_color', self.setDNATargetColor),
  276. ('select_toon_landmark_door_orientation',
  277. self.setDNATargetOrientation),
  278. ('select_landmark_door_texture', self.setDNATargetCode,
  279. ['landmark_door']),
  280. ('select_street_texture', self.setDNATargetCode, ['street']),
  281. ('select_prop_texture', self.setDNATargetCode, ['prop']),
  282. ('select_prop_color', self.setDNATargetColor),
  283. # Hot key actions
  284. ('a', self.autoPositionGrid),
  285. ('.', self.jumpToInsertionPoint),
  286. ('p', self.plantSelectedNodePath),
  287. ('left', self.keyboardXformSelected, ['left']),
  288. ('right', self.keyboardXformSelected, ['right']),
  289. ('up', self.keyboardXformSelected, ['up']),
  290. ('down', self.keyboardXformSelected, ['down']),
  291. ]
  292. # Initialize state
  293. # Make sure direct is running
  294. direct.enable()
  295. # And only the appropriate handles are showing
  296. direct.widget.disableHandles(['x-ring', 'x-disc',
  297. 'y-ring', 'y-disc',
  298. 'z-post'])
  299. # Initialize camera
  300. base.cam.node().setNear(5.0)
  301. base.cam.node().setFar(3000)
  302. direct.camera.setPos(0,-10,10)
  303. # Hide (disable) grid initially
  304. self.showGrid(0)
  305. # Create variable for vis groups panel
  306. self.vgpanel = None
  307. # Start off enabled
  308. self.enable()
  309. # Editing toontown_central
  310. self.setEditMode('toontown_central')
  311. # ENABLE/DISABLE
  312. def enable(self):
  313. """ Enable level editing and show level """
  314. # Make sure level is visible
  315. self.reparentTo(render)
  316. self.show()
  317. # Add all the action events
  318. for event in self.actionEvents:
  319. if len(event) == 3:
  320. self.accept(event[0], event[1], event[2])
  321. else:
  322. self.accept(event[0], event[1])
  323. # Enable mouse interaction (pie menus)
  324. self.enableMouse()
  325. # Spawn task to keep insertion marker aligned with grid
  326. self.spawnInsertionMarkerTask()
  327. def disable(self):
  328. """ Disable level editing and hide level """
  329. # Deselect everything as a precaution
  330. direct.deselectAll()
  331. # Hide the level
  332. self.reparentTo(hidden)
  333. # Ignore the hooks
  334. for eventPair in self.actionEvents:
  335. self.ignore(eventPair[0])
  336. # These are added outside of actionEvents list
  337. self.ignore('insert')
  338. self.ignore('space')
  339. # Disable Pie Menu mouse interaction
  340. self.disableMouse()
  341. # Remove insertion marker task
  342. taskMgr.removeTasksNamed('insertionMarkerTask')
  343. def reset(self, fDeleteToplevel = 1, fCreateToplevel = 1):
  344. """
  345. Reset level and re-initialize main class variables
  346. Pass in the new top level group
  347. """
  348. if fDeleteToplevel:
  349. # First destroy existing scene-graph/DNA hierarchy
  350. self.deleteToplevel()
  351. # Clear DNASTORE
  352. DNASTORE.resetDNAGroups()
  353. # Reset DNA VIS Groups
  354. DNASTORE.resetDNAVisGroups()
  355. # Create fresh DNA DATA
  356. self.DNAData = DNAData('level_data')
  357. # Create new toplevel node path and DNA
  358. if fCreateToplevel:
  359. self.createToplevel(DNANode('level'))
  360. # Initialize variables
  361. # Reset grid
  362. direct.grid.setPosHprScale(0,0,0,0,0,0,1,1,1)
  363. # The selected DNA Object/NodePath
  364. self.selectedDNARoot = None
  365. self.selectedNPRoot = None
  366. # Set active target (the subcomponent being modified)
  367. self.DNATarget = None
  368. self.DNATargetParent = None
  369. # Set count of groups added to level
  370. self.setGroupNum(0)
  371. # Heading angle of last object added to level
  372. self.setLastAngle(0.0)
  373. # Last wall and building modified using pie menus
  374. self.lastWall = None
  375. self.lastBuilding = None
  376. # Code of last selected object (for autopositionGrid)
  377. self.snapList = []
  378. def deleteToplevel(self):
  379. # Destory old toplevel node path and DNA
  380. # First the toplevel DNA
  381. self.DNAData.remove(self.DNAToplevel)
  382. # Then the toplevel Node Path
  383. self.NPToplevel.reparentTo(hidden)
  384. self.NPToplevel.remove()
  385. def createToplevel(self, dnaNode, nodePath = None):
  386. # When you create a new level, data is added to this node
  387. # When you load a DNA file, you replace this node with the new data
  388. self.DNAToplevel = dnaNode
  389. self.DNAData.add(self.DNAToplevel)
  390. if nodePath:
  391. # Node path given, use it
  392. self.NPToplevel = nodePath
  393. self.NPToplevel.reparentTo(self)
  394. else:
  395. # No node path given, traverse
  396. self.NPToplevel = self.DNAToplevel.traverse(self, DNASTORE, 1)
  397. # Update parent pointers
  398. self.DNAParent = self.DNAToplevel
  399. self.NPParent = self.NPToplevel
  400. # Update scene graph explorer
  401. # self.panel.sceneGraphExplorer.update()
  402. def destroy(self):
  403. """ Disable level editor and destroy node path """
  404. self.disable()
  405. self.removeNode()
  406. self.panel.destroy()
  407. if self.vgpanel:
  408. self.vgpanel.destroy()
  409. def useDirectFly(self):
  410. """ Disable player camera controls/enable direct camera control """
  411. self.enableMouse()
  412. direct.enable()
  413. def useDriveMode(self):
  414. """ Lerp down to eye level then switch to Drive mode """
  415. pos = direct.camera.getPos()
  416. pos.setZ(4.0)
  417. hpr = direct.camera.getHpr()
  418. hpr.set(hpr[0], 0.0, 0.0)
  419. t = direct.camera.lerpPosHpr(pos, hpr, 1.0, blendType = 'easeInOut',
  420. task = 'manipulateCamera')
  421. # Note, if this dies an unatural death, this could screw things up
  422. t.uponDeath = self.switchToDriveMode
  423. def switchToDriveMode(self,state):
  424. """ Disable direct camera manipulation and enable player drive mode """
  425. direct.minimumConfiguration()
  426. direct.manipulationControl.disableManipulation()
  427. base.useDrive()
  428. # Make sure we're where we want to be
  429. pos = direct.camera.getPos()
  430. pos.setZ(4.0)
  431. hpr = direct.camera.getHpr()
  432. hpr.set(hpr[0], 0.0, 0.0)
  433. # Fine tune the drive mode
  434. base.mouseInterface.getBottomNode().setPos(pos)
  435. base.mouseInterface.getBottomNode().setHpr(hpr)
  436. base.mouseInterface.getBottomNode().setForwardSpeed(20.0)
  437. base.mouseInterface.getBottomNode().setReverseSpeed(20.0)
  438. def enableMouse(self):
  439. """ Enable Pie Menu interaction (and disable player camera control) """
  440. # Turn off player camera control
  441. base.disableMouse()
  442. # Handle mouse events for pie menus
  443. self.accept('handleMouse3',self.levelHandleMouse3)
  444. self.accept('handleMouse3Up',self.levelHandleMouse3Up)
  445. def disableMouse(self):
  446. """ Disable Pie Menu interaction """
  447. # Disable handling of mouse events
  448. self.ignore('handleMouse3')
  449. self.ignore('handleMouse3Up')
  450. # LEVEL OBJECT MANAGEMENT FUNCTIONS
  451. def findDNANode(self, nodePath):
  452. """ Find node path's DNA Object in DNAStorage (if any) """
  453. return DNASTORE.findDNAGroup(nodePath.id())
  454. def replaceSelected(self):
  455. # Update visible geometry using new DNA
  456. newRoot = self.replace(self.selectedNPRoot, self.selectedDNARoot)
  457. # Reselect node path and respawn followSelectedNodePathTask
  458. direct.select(newRoot)
  459. def replace(self, nodePath, dnaNode):
  460. """ Replace a node path with the results of a DNANode traversal """
  461. # Find node path's parent
  462. parent = nodePath.getParent()
  463. dnaParent = dnaNode.getParent()
  464. # Get rid of the old node path and remove its DNA and
  465. # node relations from the DNA Store
  466. self.remove(dnaNode, nodePath)
  467. # Traverse the old (modified) dna to create the new node path
  468. newNodePath = dnaNode.traverse(parent, DNASTORE, 1)
  469. # Add it back to the dna parent
  470. dnaParent.add(dnaNode)
  471. # Update scene graph explorer
  472. # self.panel.sceneGraphExplorer.update()
  473. # Return new node path
  474. return newNodePath
  475. def remove(self, dnaNode, nodePath):
  476. """
  477. Delete DNA and Node relation from DNA Store and remove the node
  478. path from the scene graph.
  479. """
  480. # First delete DNA and node relation from the DNA Store
  481. if dnaNode:
  482. # Get DNANode's parent
  483. parentDNANode = dnaNode.getParent()
  484. if parentDNANode:
  485. # Remove DNANode from its parent
  486. parentDNANode.remove(dnaNode)
  487. # Delete DNA and associated Node Relations from DNA Store
  488. DNASTORE.removeDNAGroup(dnaNode)
  489. if nodePath:
  490. # Next deselect nodePath to avoid having bad node paths in the dict
  491. direct.deselect(nodePath)
  492. # Now you can get rid of the node path
  493. nodePath.removeNode()
  494. def reparent(self, nodePath):
  495. """ Move node path (and its DNA) to active parent """
  496. # Do we have a node path?
  497. if nodePath:
  498. dnaNode = self.findDNANode(nodePath)
  499. # Does the node path correspond to a DNA Object
  500. if dnaNode:
  501. # Yes, get its parent and see if it has a DNA Object
  502. parent = nodePath.getParent()
  503. parentDNAObject = self.findDNANode(parent)
  504. if parentDNAObject:
  505. # Yes it does, move node path (and DNA)
  506. # to new parent (if active parent set)
  507. if ((self.NPParent != None) &
  508. (self.DNAParent != None)):
  509. nodePath.reparentTo(self.NPParent)
  510. parentDNAObject.remove(dnaNode)
  511. self.DNAParent.add(dnaNode)
  512. # Update scene graph explorer to reflect change
  513. # self.panel.sceneGraphExplorer.update()
  514. def reparentSelected(self):
  515. for nodePath in direct.selected:
  516. self.reparent(nodePath)
  517. def setActiveParent(self, nodePath = None):
  518. """ Set NPParent and DNAParent to node path and its DNA """
  519. # If nothing passed in, try currently selected node path
  520. if not nodePath:
  521. nodePath = direct.selected.last
  522. # If we've got a valid node path
  523. if nodePath:
  524. # See if this is in the DNA database
  525. newDNAParent = self.findDNANode(nodePath)
  526. if newDNAParent:
  527. self.DNAParent = newDNAParent
  528. self.NPParent = nodePath
  529. else:
  530. print 'LevelEditor.setActiveParent: nodePath not found'
  531. else:
  532. print 'LevelEditor.setActiveParent: nodePath == None'
  533. def getAndSetName(self, nodePath):
  534. """ Prompt user for new node path name """
  535. newName = askstring('Node Path', 'Enter new name:')
  536. if newName:
  537. self.setName(nodePath, newName)
  538. def setName(self, nodePath, newName):
  539. """ Set name of a node path and its DNA (if it exists) """
  540. # First, set name of the node path
  541. nodePath.setName(newName)
  542. # Now find the DNA that corresponds to this node path
  543. dnaNode = self.findDNANode(nodePath)
  544. if dnaNode:
  545. # If it exists, set the name of the DNA Node
  546. dnaNode.setName(newName)
  547. def updateSelectedPose(self):
  548. """
  549. Update the DNA database to reflect selected objects current positions
  550. """
  551. for selectedNode in direct.selected:
  552. # Is this a DNA Object in the DNASTORE database?
  553. dnaObject = self.findDNANode(selectedNode)
  554. if dnaObject:
  555. # It is, is it a DNA_NODE (i.e. does it have pos/hpr/scale)?
  556. if DNAIsDerivedFrom(dnaObject, DNA_NODE):
  557. # First snap selected node path to grid
  558. pos = selectedNode.getPos(direct.grid)
  559. snapPos = direct.grid.computeSnapPoint(pos)
  560. selectedNode.setPos(direct.grid, snapPos[0], snapPos[1], 0)
  561. # Angle snap
  562. h = direct.grid.computeSnapAngle(selectedNode.getH())
  563. selectedNode.setH(h)
  564. if selectedNode == direct.selected.last:
  565. self.setLastAngle(h)
  566. # Update DNA
  567. self.updatePose(dnaObject, selectedNode)
  568. def updatePose(self, dnaObject, nodePath):
  569. """
  570. Update a DNA Object's pos, hpr, and scale based upon
  571. node path's current pose
  572. """
  573. # Set DNA's pos, hpr, and scale
  574. dnaObject.setPos(nodePath.getPos())
  575. dnaObject.setHpr(nodePath.getHpr())
  576. dnaObject.setScale(nodePath.getScale())
  577. # LEVEL OBJECT CREATION FUNCTIONS
  578. def initDNANode(self, dnaNode):
  579. """
  580. This method adds a new DNA object to the scene and adds hooks that
  581. allow duplicate copies of this DNA node to be added using the
  582. space bar. For DNAFlatBuildings, a new copy with random style is
  583. generated by hitting the insert key.
  584. """
  585. # First create the visible geometry for this DNA Node
  586. self.initNodePath(dnaNode)
  587. # And add hooks to insert copies of dnaNode
  588. self.addReplicationHooks(dnaNode)
  589. def addReplicationHooks(self, dnaNode):
  590. # Now add hook to allow placement of a new dna Node of this type
  591. # by simply hitting the space bar or insert key. Note, extra paramter
  592. # indicates new dnaNode should be a copy
  593. self.accept('space', self.initNodePath, [dnaNode, 'space'])
  594. self.accept('insert', self.initNodePath, [dnaNode, 'insert'])
  595. def setRandomBuildingStyle(self, dnaNode, name = 'building'):
  596. """ Initialize a new DNA Flat building to a random building style """
  597. buildingType = self.getCurrent('building_type')
  598. # Select a list of wall heights
  599. if buildingType == 'random20':
  600. chance = randint(1,100)
  601. if chance <= 65:
  602. buildingType = '10_10'
  603. else:
  604. buildingType = '20'
  605. elif buildingType == 'random30':
  606. chance = randint(1,100)
  607. if chance <= 40:
  608. buildingType = '10_20'
  609. elif (chance > 80):
  610. buildingType = '10_10_10'
  611. else:
  612. buildingType = '20_10'
  613. # The building_style attribute dictionary for this number of stories
  614. dict = self.getAttribute('building_style_' + buildingType).getDict()
  615. style = self.getRandomDictionaryEntry(dict)
  616. self.styleManager.setDNAFlatBuildingStyle(
  617. dnaNode, style, width = self.getRandomWallWidth(), name = name)
  618. def getRandomWallWidth(self):
  619. chance = randint(0,100)
  620. if chance <= 15:
  621. return 5.0
  622. elif (chance > 15) & (chance <= 30):
  623. return 10.0
  624. elif (chance > 30) & (chance <= 65):
  625. return 15.0
  626. elif (chance > 65) & (chance <= 85):
  627. return 20.0
  628. elif (chance > 85):
  629. return 25.0
  630. def initNodePath(self, dnaNode, hotKey = None):
  631. """
  632. Update DNA to reflect latest style choices and then generate
  633. new node path and add it to the scene graph
  634. """
  635. # Determine dnaNode Class Type
  636. nodeClass = DNAGetClassType(dnaNode)
  637. # Did the user hit insert or space?
  638. if hotKey:
  639. # Yes, make a new copy of the dnaNode
  640. dnaNode = dnaNode.__class__(dnaNode)
  641. # And determine dnaNode type and perform any type specific updates
  642. if nodeClass.eq(DNA_PROP):
  643. dnaNode.setCode(self.getCurrent('prop_texture'))
  644. elif nodeClass.eq(DNA_STREET):
  645. dnaNode.setCode(self.getCurrent('street_texture'))
  646. elif nodeClass.eq(DNA_FLAT_BUILDING):
  647. # If insert, pick a new random style
  648. if hotKey == 'insert':
  649. self.setRandomBuildingStyle(dnaNode, dnaNode.getName())
  650. # Get a new building width
  651. self.setCurrent('building_width', self.getRandomWallWidth())
  652. dnaNode.setWidth(self.getCurrent('building_width'))
  653. # Add the DNA to the active parent
  654. self.DNAParent.add(dnaNode)
  655. # And create the geometry
  656. newNodePath = dnaNode.traverse(self.NPParent, DNASTORE, 1)
  657. # Update scene graph explorer
  658. # self.panel.sceneGraphExplorer.update()
  659. # Position it
  660. if (hotKey is not None) & nodeClass.eq(DNA_PROP):
  661. # If its a prop and a copy, place it based upon current
  662. # mouse position
  663. hitPt = self.getGridIntersectionPoint()
  664. newNodePath.setPos(direct.grid, hitPt)
  665. else:
  666. # Place the new node path at the current grid origin
  667. newNodePath.setPos(direct.grid,0,0,0)
  668. # Initialize angle to match last object
  669. newNodePath.setHpr(self.getLastAngle(), 0, 0)
  670. # Now update DNA pos and hpr to reflect final pose
  671. dnaNode.setPos(newNodePath.getPos())
  672. dnaNode.setHpr(newNodePath.getHpr())
  673. # Reset last Code (for autoPositionGrid)
  674. if DNAClassEqual(dnaNode, DNA_STREET):
  675. self.snapList = OBJECT_SNAP_POINTS[dnaNode.getCode()]
  676. # Select the instance
  677. self.select(newNodePath)
  678. # Update grid to get ready for the next object
  679. self.autoPositionGrid()
  680. def addGroup(self, nodePath):
  681. """ Add a new DNA Node Group to the specified Node Path """
  682. # Set the node path as the current parent
  683. self.setActiveParent(nodePath)
  684. # Add a new group to the selected parent
  685. self.createNewGroup()
  686. def addVisGroup(self, nodePath):
  687. """ Add a new DNA Group to the specified Node Path """
  688. # Set the node path as the current parent
  689. self.setActiveParent(nodePath)
  690. # Add a new group to the selected parent
  691. self.createNewGroup(type = 'vis')
  692. def createNewGroup(self, type = 'dna'):
  693. """ Create a new DNA Node group under the active parent """
  694. # Create a new DNA Node group
  695. if type == 'dna':
  696. newDNANode = DNANode('group_' + `self.getGroupNum()`)
  697. else:
  698. newDNANode = DNAVisGroup('VisGroup_' + `self.getGroupNum()`)
  699. # Increment group counter
  700. self.setGroupNum(self.getGroupNum() + 1)
  701. # Add new DNA Node group to the current parent DNA Object
  702. self.DNAParent.add(newDNANode)
  703. # The new Node group becomes the active parent
  704. self.DNAParent = newDNANode
  705. # Traverse it to generate the new node path as a child of NPParent
  706. newNodePath = self.DNAParent.traverse(self.NPParent, DNASTORE, 1)
  707. # Update NPParent to point to the new node path
  708. self.NPParent = newNodePath
  709. # Update scene graph explorer
  710. # self.panel.sceneGraphExplorer.update()
  711. def addFlatBuilding(self, buildingType):
  712. # Create new building
  713. newDNAFlatBuilding = DNAFlatBuilding()
  714. self.setRandomBuildingStyle(newDNAFlatBuilding,
  715. name = buildingType + '_DNARoot')
  716. # Initialize its position and hpr
  717. newDNAFlatBuilding.setPos(VBase3(0))
  718. newDNAFlatBuilding.setHpr(VBase3(0))
  719. # Now place new building in the world
  720. self.initDNANode(newDNAFlatBuilding)
  721. def addLandmark(self, landmarkType):
  722. # Record new landmark type
  723. self.setCurrent('toon_landmark_texture', landmarkType)
  724. # And create new landmark building
  725. newDNALandmarkBuilding = DNALandmarkBuilding(landmarkType + '_DNARoot')
  726. newDNALandmarkBuilding.setCode(landmarkType)
  727. newDNALandmarkBuilding.setPos(VBase3(0))
  728. newDNALandmarkBuilding.setHpr(VBase3(0))
  729. newDNADoor = self.createDoor()
  730. newDNALandmarkBuilding.add(newDNADoor)
  731. # Now place new landmark building in the world
  732. self.initDNANode(newDNALandmarkBuilding)
  733. def addProp(self, propType):
  734. # Record new prop type
  735. self.setCurrent('prop_texture', propType)
  736. # And create new prop
  737. newDNAProp = DNAProp(propType + '_DNARoot')
  738. newDNAProp.setCode(propType)
  739. newDNAProp.setPos(VBase3(0))
  740. newDNAProp.setHpr(VBase3(0))
  741. # Now place new prop in the world
  742. self.initDNANode(newDNAProp)
  743. def addStreet(self, streetType):
  744. # Record new street type
  745. self.setCurrent('street_texture', streetType)
  746. # And create new street
  747. newDNAStreet = DNAStreet(streetType + '_DNARoot')
  748. newDNAStreet.setCode(streetType)
  749. newDNAStreet.setPos(VBase3(0))
  750. newDNAStreet.setHpr(VBase3(0))
  751. # Set street texture to neighborhood dependant texture
  752. newDNAStreet.setStreetTexture(
  753. 'street_street_' + self.neighborhoodCode + '_tex')
  754. newDNAStreet.setSidewalkTexture(
  755. 'street_sidewalk_' + self.neighborhoodCode + '_tex')
  756. # Now place new street in the world
  757. self.initDNANode(newDNAStreet)
  758. def createCornice(self):
  759. newDNACornice = DNACornice('cornice')
  760. newDNACornice.setCode(self.getCurrent('cornice_texture'))
  761. newDNACornice.setColor(self.getCurrent('cornice_color'))
  762. return newDNACornice
  763. def createDoor(self):
  764. newDNADoor = DNADoor('door')
  765. newDNADoor.setCode(self.getCurrent('door_texture'))
  766. newDNADoor.setColor(self.getCurrent('door_color'))
  767. return newDNADoor
  768. def createWindows(self):
  769. newDNAWindows = DNAWindows()
  770. newDNAWindows.setCode(self.getCurrent('window_texture'))
  771. newDNAWindows.setWindowCount(self.getCurrent('window_count'))
  772. newDNAWindows.setColor(self.getCurrent('window_color'))
  773. return newDNAWindows
  774. def removeCornice(self, cornice, parent):
  775. self.setCurrent('cornice_color', cornice.getColor())
  776. DNARemoveChildOfClass(parent, DNA_CORNICE)
  777. def removeLandmarkDoor(self, door, parent):
  778. self.setCurrent('door_color', door.getColor())
  779. DNARemoveChildOfClass(parent, DNA_DOOR)
  780. def removeDoor(self, door, parent):
  781. self.setCurrent('door_color', door.getColor())
  782. DNARemoveChildOfClass(parent, DNA_DOOR)
  783. def removeWindows(self, windows, parent):
  784. # And record number of windows
  785. self.setCurrent('window_color', windows.getColor())
  786. self.setCurrent('window_count', windows.getWindowCount())
  787. DNARemoveChildOfClass(parent, DNA_WINDOWS)
  788. # LEVEL-OBJECT MODIFICATION FUNCTIONS
  789. def levelHandleMouse3(self):
  790. # Initialize dna target
  791. self.DNATarget = None
  792. # If nothing selected, just return
  793. if not self.selectedNPRoot:
  794. return
  795. # Next check to see if the selected object is a DNA object
  796. dnaObject = self.findDNANode(self.selectedNPRoot)
  797. # Nope, not a DNA object, just return
  798. if not dnaObject:
  799. return
  800. # Pick a menu based upon object type
  801. if DNAClassEqual(dnaObject, DNA_FLAT_BUILDING):
  802. # FLAT BUILDING OPERATIONS
  803. menuMode, wallNum = self.getFlatBuildingMode(dnaObject)
  804. # Find appropriate target
  805. wall = self.getWall(dnaObject, wallNum)
  806. # Record bldg/wall
  807. self.lastBuilding = dnaObject
  808. self.lastWall = wall
  809. if string.find(menuMode,'wall') >= 0:
  810. self.DNATarget = wall
  811. self.DNATargetParent = dnaObject
  812. elif string.find(menuMode,'door') >= 0:
  813. self.DNATarget = DNAGetChildOfClass(wall, DNA_DOOR)
  814. self.DNATargetParent = wall
  815. elif string.find(menuMode, 'window') >= 0:
  816. self.DNATarget = DNAGetChildOfClass(wall, DNA_WINDOWS)
  817. self.DNATargetParent = wall
  818. elif string.find(menuMode,'cornice') >= 0:
  819. self.DNATarget = DNAGetChildOfClass(wall, DNA_CORNICE)
  820. self.DNATargetParent = wall
  821. else:
  822. self.DNATarget = dnaObject
  823. elif DNAClassEqual(dnaObject, DNA_PROP):
  824. # PROP OPERATIONS
  825. self.DNATarget = dnaObject
  826. if direct.fControl:
  827. menuMode = 'prop_color'
  828. else:
  829. menuMode = 'prop_texture'
  830. elif DNAClassEqual(dnaObject, DNA_LANDMARK_BUILDING):
  831. # LANDMARK BUILDING OPERATIONS
  832. self.DNATarget = DNAGetChildOfClass(dnaObject, DNA_DOOR)
  833. self.DNATargetParent = dnaObject
  834. if direct.fAlt:
  835. menuMode = 'door_orientation'
  836. elif direct.fControl:
  837. menuMode = 'door_color'
  838. else:
  839. menuMode = 'door_texture'
  840. elif DNAClassEqual(dnaObject, DNA_STREET):
  841. # STREET OPERATIONS
  842. self.DNATarget = dnaObject
  843. menuMode = 'street_texture'
  844. # Now spawn apropriate menu task if menu selected
  845. self.activeMenu = self.getMenu(menuMode)
  846. # Set initial state
  847. state = None
  848. if self.DNATarget:
  849. if string.find(menuMode,'texture') >= 0:
  850. state = self.DNATarget.getCode()
  851. elif string.find(menuMode, 'color') >= 0:
  852. state = self.DNATarget.getColor()
  853. self.panel.setCurrentColor(state)
  854. self.panel.setResetColor(state)
  855. elif string.find(menuMode, 'orientation') >= 0:
  856. state = self.DNATarget.getCode()[-3:]
  857. elif menuMode == 'building_width' >= 0:
  858. state = self.DNATarget.getWidth()
  859. elif menuMode == 'window_count' >= 0:
  860. state = self.DNATarget.getWindowCount()
  861. elif menuMode == 'building_style' >= 0:
  862. # Extract the building style from the current building
  863. state = DNAFlatBuildingStyle(building = self.DNATarget)
  864. elif menuMode == 'wall_style' >= 0:
  865. # Extract the wall style from the current wall
  866. state = DNAWallStyle(wall = self.DNATarget)
  867. self.activeMenu.setInitialState(state)
  868. # Spawn active menu's tatsk
  869. self.activeMenu.spawnPieMenuTask()
  870. def getFlatBuildingMode(self, dnaObject):
  871. # Where are we hitting the building?
  872. hitPt = self.getWallIntersectionPoint()
  873. wallNum = self.computeWallNum(dnaObject, hitPt)
  874. if wallNum < 0:
  875. # Do building related operations
  876. if direct.fShift:
  877. menuMode = 'building_type'
  878. elif direct.fAlt:
  879. menuMode = 'building_width'
  880. else:
  881. menuMode = 'building_style'
  882. else:
  883. # Otherwise, do wall specific operations
  884. # Figure out where you are hitting on the wall
  885. wallHeights, offsetList = DNAGetWallHeights(dnaObject)
  886. # Find a normalized X and Z coordinate
  887. xPt = hitPt[0]/dnaObject.getWidth()
  888. # Adjust zPt depending on what wall you are pointing at
  889. wallHeight = wallHeights[wallNum]
  890. zPt = (hitPt[2] - offsetList[wallNum])/wallHeight
  891. # Record current wall height
  892. self.setCurrent('wall_height', wallHeight)
  893. # Determine which zone you are pointing at
  894. if (zPt > 0.8):
  895. # Do cornice operations
  896. if direct.fControl:
  897. menuMode = 'cornice_color'
  898. elif direct.fAlt:
  899. menuMode = 'cornice_orientation'
  900. else:
  901. menuMode = 'cornice_texture'
  902. elif ((xPt < 0.3) | (xPt > 0.7)):
  903. # Do wall operations
  904. if direct.fControl:
  905. menuMode = 'wall_color'
  906. elif direct.fAlt:
  907. menuMode = 'wall_orientation'
  908. elif direct.fShift:
  909. menuMode = 'wall_texture'
  910. else:
  911. menuMode = 'wall_style'
  912. elif (zPt < 0.4):
  913. # Do door operations
  914. if direct.fControl:
  915. menuMode = 'door_color'
  916. elif direct.fAlt:
  917. menuMode = 'door_orientation'
  918. else:
  919. menuMode = 'door_texture'
  920. else:
  921. # Do window operations
  922. if direct.fControl:
  923. menuMode = 'window_color'
  924. elif direct.fAlt:
  925. menuMode = 'window_orientation'
  926. elif direct.fShift:
  927. menuMode = 'window_count'
  928. else:
  929. menuMode = 'window_texture'
  930. return menuMode, wallNum
  931. def levelHandleMouse3Up(self):
  932. if self.activeMenu:
  933. self.activeMenu.removePieMenuTask()
  934. # Update panel color if appropriate
  935. if self.DNATarget:
  936. objClass = DNAGetClassType(self.DNATarget)
  937. if ((objClass.eq(DNA_WALL)) |
  938. (objClass.eq(DNA_WINDOWS)) |
  939. (objClass.eq(DNA_DOOR)) |
  940. (objClass.eq(DNA_CORNICE)) |
  941. (objClass.eq(DNA_PROP))
  942. ):
  943. self.panel.setCurrentColor(self.DNATarget.getColor())
  944. def setDNATargetColor(self, color):
  945. if self.DNATarget:
  946. self.DNATarget.setColor(color)
  947. self.replaceSelected()
  948. def setDNATargetCode(self, type, code):
  949. if (self.DNATarget != None) & (code != None):
  950. # Update code
  951. self.DNATarget.setCode(code)
  952. elif (self.DNATarget != None) & (code == None):
  953. # Delete object, record pertinant properties before
  954. # you delete the object so you can restore them later
  955. # Remove object
  956. if (type == 'cornice'):
  957. self.removeCornice(self.DNATarget, self.DNATargetParent)
  958. elif (type == 'landmark_door'):
  959. self.removeLandmarkDoor(self.DNATarget, self.DNATargetParent)
  960. elif (type == 'doorToBeImplemented'):
  961. self.removeDoor(self.DNATarget, self.DNATargetParent)
  962. elif (type == 'windows'):
  963. self.removeWindows(self.DNATarget, self.DNATargetParent)
  964. # Clear out DNATarget
  965. self.DNATarget = None
  966. elif (self.DNATarget == None) & (code != None):
  967. # Add new object
  968. if (type == 'cornice'):
  969. self.DNATarget = self.createCornice()
  970. elif (type == 'landmark_door'):
  971. self.DNATarget = self.createDoor()
  972. elif (type == 'doorToBeImplemented'):
  973. self.DNATarget = self.createDoor()
  974. elif (type == 'windows'):
  975. # Make sure window_count n.e. 0
  976. if self.getCurrent('window_count') == 0:
  977. self.setCurrent(
  978. 'window_count',
  979. self.getRandomWindowCount())
  980. # Now create the windows
  981. self.DNATarget = self.createWindows()
  982. self.DNATargetParent.add(self.DNATarget)
  983. # Update visible representation
  984. self.replaceSelected()
  985. def setDNATargetOrientation(self, orientation):
  986. if (self.DNATarget != None) & (orientation != None):
  987. oldCode = self.DNATarget.getCode()[:-3]
  988. self.DNATarget.setCode(oldCode + '_' + orientation)
  989. self.replaceSelected()
  990. def setBuildingStyle(self, style):
  991. if (self.DNATarget != None) & (style != None):
  992. self.styleManager.setDNAFlatBuildingStyle(
  993. self.DNATarget, style,
  994. width = self.DNATarget.getWidth(),
  995. name = self.DNATarget.getName())
  996. # MRM: Need to disable dna store warning
  997. self.replaceSelected()
  998. # Re-add replication hooks so we get right kind of copy
  999. self.addReplicationHooks(self.DNATarget)
  1000. def setBuildingType(self, type):
  1001. print 'setBuildingType: ', `type`
  1002. def setBuildingWidth(self, width):
  1003. if self.DNATarget:
  1004. self.DNATarget.setWidth(width)
  1005. self.replaceSelected()
  1006. def setWindowCount(self, count):
  1007. if self.DNATarget:
  1008. if count == 0:
  1009. # Remove windows and clear out DNATarget
  1010. self.removeWindows(self.DNATarget, self.DNATargetParent)
  1011. self.DNATarget = None
  1012. else:
  1013. self.DNATarget.setWindowCount(count)
  1014. self.replaceSelected()
  1015. def setWallStyle(self, style):
  1016. if (self.DNATarget != None) & (style != None):
  1017. self.styleManager.setDNAWallStyle(
  1018. self.DNATarget, style,
  1019. self.DNATarget.getHeight())
  1020. self.replaceSelected()
  1021. # SELECTION FUNCTIONS
  1022. def select(self, nodePath):
  1023. """ Call direct function to select node path """
  1024. # Select new node path
  1025. direct.select(nodePath)
  1026. def selectedNodePathHook(self, nodePath):
  1027. """
  1028. Hook called upon selection of a node path used to restrict
  1029. selection to DNA Objects. Press control to select any type of
  1030. DNA Object, with no control key pressed, hook selects only
  1031. DNA Root objects
  1032. """
  1033. # Clear out old root variables
  1034. self.selectedDNARoot = None
  1035. self.selectedNPRoot = None
  1036. # Now process newly selected node path
  1037. dnaParent = None
  1038. dnaNode = self.findDNANode(nodePath)
  1039. if direct.fControl:
  1040. # Is the current node a DNA Object?
  1041. if not dnaNode:
  1042. # No it isn't, look for a parent DNA object
  1043. dnaParent = self.findDNAParent(nodePath.getParent())
  1044. else:
  1045. # Is the current node a DNA Root object?
  1046. if nodePath.getName()[-8:] != '_DNARoot':
  1047. # No it isn't, look for a parent DNA Root object
  1048. dnaParent = self.findDNARoot(nodePath.getParent())
  1049. # Do we need to switch selection to a parent object?
  1050. if dnaParent:
  1051. # Yes, deselect currently selected node path
  1052. direct.deselect(nodePath)
  1053. # And select parent
  1054. direct.select(dnaParent, direct.fShift)
  1055. else:
  1056. # We got a valid node path/DNA object, continue
  1057. self.selectedNPRoot = nodePath
  1058. self.selectedDNARoot = dnaNode
  1059. # Reset last Code (for autoPositionGrid)
  1060. if DNAClassEqual(dnaNode, DNA_STREET):
  1061. self.snapList = OBJECT_SNAP_POINTS[dnaNode.getCode()]
  1062. def deselectedNodePathHook(self, nodePath):
  1063. # Clear out old root variables
  1064. self.selectedDNARoot = None
  1065. self.selectedNPRoot = None
  1066. def findDNAParent(self, nodePath):
  1067. """ Walk up a node path's ancestry looking for its DNA Root """
  1068. # Check to see if current node is a dna object
  1069. if self.findDNANode(nodePath):
  1070. # Its a root!
  1071. return nodePath
  1072. else:
  1073. # If reached the top: fail
  1074. if not nodePath.hasParent():
  1075. return 0
  1076. else:
  1077. # Try parent
  1078. return self.findDNAParent(nodePath.getParent())
  1079. def findDNARoot(self, nodePath):
  1080. """ Walk up a node path's ancestry looking for its DNA Root """
  1081. # Check current node's name for root marker
  1082. if (nodePath.getName()[-8:] == '_DNARoot'):
  1083. # Its a root!
  1084. return nodePath
  1085. else:
  1086. # If reached the top: fail
  1087. if not nodePath.hasParent():
  1088. return None
  1089. else:
  1090. # Try parent
  1091. return self.findDNARoot(nodePath.getParent())
  1092. # MANIPULATION FUNCTIONS
  1093. def keyboardRotateSelected(self, arrowDirection):
  1094. """ Rotate selected objects using arrow keys """
  1095. if ((arrowDirection == 'left') | (arrowDirection == 'up')):
  1096. self.setLastAngle(self.getLastAngle() + SNAP_ANGLE)
  1097. else:
  1098. self.setLastAngle(self.getLastAngle() - SNAP_ANGLE)
  1099. if (self.getLastAngle() < -180.0):
  1100. self.setLastAngle(self.getLastAngle() + 360.0)
  1101. elif (self.getLastAngle() > 180.0):
  1102. self.setLastAngle(self.getLastAngle() - 360.0)
  1103. # Move selected objects
  1104. for selectedNode in direct.selected:
  1105. selectedNode.setHpr(self.getLastAngle(), 0, 0)
  1106. # Snap objects to grid and update DNA if necessary
  1107. self.updateSelectedPose()
  1108. def keyboardTranslateSelected(self, arrowDirection):
  1109. gridToCamera = direct.grid.getMat(direct.camera)
  1110. camXAxis = gridToCamera.xformVec(X_AXIS)
  1111. xxDot = camXAxis.dot(X_AXIS)
  1112. xzDot = camXAxis.dot(Z_AXIS)
  1113. # what is the current grid spacing?
  1114. deltaMove = direct.grid.gridSpacing
  1115. # Compute the specified delta
  1116. deltaPos = Vec3(0)
  1117. if (abs(xxDot) > abs(xzDot)):
  1118. if (xxDot < 0.0):
  1119. deltaMove = -deltaMove
  1120. # Compute delta
  1121. if (arrowDirection == 'right'):
  1122. deltaPos.setX(deltaPos[0] + deltaMove)
  1123. elif (arrowDirection == 'left'):
  1124. deltaPos.setX(deltaPos[0] - deltaMove)
  1125. elif (arrowDirection == 'up'):
  1126. deltaPos.setY(deltaPos[1] + deltaMove)
  1127. elif (arrowDirection == 'down'):
  1128. deltaPos.setY(deltaPos[1] - deltaMove)
  1129. else:
  1130. if (xzDot < 0.0):
  1131. deltaMove = -deltaMove
  1132. # Compute delta
  1133. if (arrowDirection == 'right'):
  1134. deltaPos.setY(deltaPos[1] - deltaMove)
  1135. elif (arrowDirection == 'left'):
  1136. deltaPos.setY(deltaPos[1] + deltaMove)
  1137. elif (arrowDirection == 'up'):
  1138. deltaPos.setX(deltaPos[0] + deltaMove)
  1139. elif (arrowDirection == 'down'):
  1140. deltaPos.setX(deltaPos[0] - deltaMove)
  1141. # Move selected objects
  1142. for selectedNode in direct.selected:
  1143. # Move it
  1144. selectedNode.setPos(direct.grid,
  1145. selectedNode.getPos(direct.grid) + deltaPos)
  1146. # Snap objects to grid and update DNA if necessary
  1147. self.updateSelectedPose()
  1148. def keyboardXformSelected(self, arrowDirection):
  1149. if direct.fControl:
  1150. self.keyboardRotateSelected(arrowDirection)
  1151. else:
  1152. self.keyboardTranslateSelected(arrowDirection)
  1153. # VISIBILITY FUNCTIONS
  1154. def editDNAVisGroups(self):
  1155. visGroups = self.getDNAVisGroups(self.NPToplevel)
  1156. if visGroups:
  1157. self.vgpanel = VisGroupsEditor(self, visGroups)
  1158. else:
  1159. showinfo('Vis Groups Editor','No DNA Vis Groups Found!')
  1160. def getDNAVisGroups(self, nodePath):
  1161. """ Find the highest level vis groups in the scene graph """
  1162. dnaNode = self.findDNANode(nodePath)
  1163. if dnaNode:
  1164. if DNAClassEqual(dnaNode, DNA_VIS_GROUP):
  1165. return [[nodePath, dnaNode]]
  1166. childVisGroups = []
  1167. children = nodePath.getChildrenAsList()
  1168. for child in children:
  1169. childVisGroups = (childVisGroups + self.getDNAVisGroups(child))
  1170. return childVisGroups
  1171. def flash(self, nodePath = None):
  1172. if not nodePath:
  1173. # If nothing specified, try selected node path
  1174. nodePath = direct.selected.last
  1175. if nodePath:
  1176. """ Spawn a task to flash a node several times """
  1177. taskMgr.removeTasksNamed('flashNodePath')
  1178. t = Task.Task(self.flashNodePathTask)
  1179. t.nodePath = nodePath
  1180. t.initState = t.hidden = nodePath.isHidden()
  1181. t.flashCount = 0
  1182. t.frameCount = 0
  1183. t.uponDeath = self.flashDone
  1184. taskMgr.spawnTaskNamed(t, 'flashNodePath')
  1185. def flashNodePathTask(self, state):
  1186. nodePath = state.nodePath
  1187. initState = state.initState
  1188. hidden = state.hidden
  1189. flashCount = state.flashCount
  1190. frameCount = state.frameCount
  1191. if (flashCount < 4):
  1192. if (frameCount % 3) == 0:
  1193. if hidden:
  1194. nodePath.show()
  1195. else:
  1196. nodePath.hide()
  1197. state.hidden = not state.hidden
  1198. state.flashCount = flashCount + 1
  1199. state.frameCount = frameCount + 1
  1200. return Task.cont
  1201. else:
  1202. return Task.done
  1203. def flashDone(self,state):
  1204. if state.initState:
  1205. state.nodePath.hide()
  1206. else:
  1207. state.nodePath.show()
  1208. def isolate(self, nodePath = None):
  1209. """ Show a node path and hide its siblings """
  1210. if not nodePath:
  1211. # If nothing specified, try selected node path
  1212. nodePath = direct.selected.last
  1213. if nodePath:
  1214. # First show everything in level
  1215. self.showAll()
  1216. # Now hide all of this node path's siblings
  1217. nodePath.hideSiblings()
  1218. def toggleViz(self, nodePath = None):
  1219. """ Toggle visibility of node path """
  1220. if not nodePath:
  1221. # If nothing specified, try selected node path
  1222. nodePath = direct.selected.last
  1223. if nodePath:
  1224. # First kill the flashing task to avoid complications
  1225. taskMgr.removeTasksNamed('flashNodePath')
  1226. # Now toggle node path's visibility state
  1227. if nodePath.isHidden():
  1228. nodePath.show()
  1229. else:
  1230. nodePath.hide()
  1231. def showAll(self):
  1232. """ Show the level and its descendants """
  1233. self.showAllDescendants()
  1234. render.hideCollisionSolids()
  1235. def showGrid(self,flag):
  1236. """ toggle direct grid """
  1237. if flag:
  1238. direct.grid.enable()
  1239. else:
  1240. direct.grid.disable()
  1241. # LEVEL MAP/MARKER FUNCTIONS
  1242. def createLevelMaps(self):
  1243. """
  1244. Load up the various neighborhood maps
  1245. """
  1246. # For neighborhood maps
  1247. self.levelMap = hidden.attachNewNode(NamedNode('level-map'))
  1248. self.activeMap = None
  1249. self.mapDictionary = {}
  1250. for neighborhood in NEIGHBORHOODS:
  1251. self.createMap(neighborhood)
  1252. def createMap(self, neighborhood):
  1253. map = loader.loadModel('models/level_editor/' + neighborhood +
  1254. '_layout')
  1255. map.getBottomArc().setTransition(TransparencyTransition(1))
  1256. map.setColor(Vec4(1,1,1,.4))
  1257. self.mapDictionary[neighborhood] = map
  1258. # Make sure this item isn't pickable
  1259. direct.manipulationControl.addUnpickable(neighborhood + '_layout')
  1260. def selectMap(self, neighborhood):
  1261. if self.activeMap:
  1262. self.activeMap.reparentTo(hidden)
  1263. self.activeMap = self.mapDictionary[neighborhood]
  1264. self.activeMap.reparentTo(self.levelMap)
  1265. def toggleMapViz(self, flag):
  1266. if flag:
  1267. self.levelMap.reparentTo(render)
  1268. else:
  1269. self.levelMap.reparentTo(hidden)
  1270. def createInsertionMarker(self):
  1271. self.insertionMarker = LineNodePath(self)
  1272. self.insertionMarker.lineNode.setName('insertionMarker')
  1273. self.insertionMarker.setColor(VBase4(0.785, 0.785, 0.5,1))
  1274. self.insertionMarker.setThickness(1)
  1275. self.insertionMarker.reset()
  1276. self.insertionMarker.moveTo(-75,0,0)
  1277. self.insertionMarker.drawTo(75,0,0)
  1278. self.insertionMarker.moveTo(0,-75,0)
  1279. self.insertionMarker.drawTo(0,75,0)
  1280. self.insertionMarker.moveTo(0,0,-75)
  1281. self.insertionMarker.drawTo(0,0,75)
  1282. self.insertionMarker.create()
  1283. def spawnInsertionMarkerTask(self):
  1284. t = Task.Task(self.insertionMarkerTask)
  1285. taskMgr.spawnTaskNamed(t, 'insertionMarkerTask')
  1286. def insertionMarkerTask(self, state):
  1287. self.insertionMarker.setPosHpr(direct.grid, 0,0,0, 0,0,0)
  1288. # MRM: Why is this necessary?
  1289. self.insertionMarker.setScale(1,1,1)
  1290. return Task.cont
  1291. # UTILITY FUNCTIONS
  1292. def getRandomDictionaryEntry(self,dict):
  1293. numKeys = len(dict)
  1294. if numKeys > 0:
  1295. keys = dict.keys()
  1296. key = keys[randint(0,numKeys - 1)]
  1297. return dict[key]
  1298. else:
  1299. return None
  1300. def getRandomWindowCount(self):
  1301. if ((self.lastWall != None) & (self.lastBuilding != None)):
  1302. h = ROUND_INT(self.lastWall.getHeight())
  1303. w = ROUND_INT(self.lastBuilding.getWidth())
  1304. # Otherwise....
  1305. if w == 5:
  1306. # 5 ft walls can have 1 window
  1307. return 1
  1308. elif h == 10:
  1309. # All other 10 ft high bldgs can have 1 or 2
  1310. return randint(1,2)
  1311. else:
  1312. # All others can have 1 - 4
  1313. return randint(1,4)
  1314. else:
  1315. return 1
  1316. def autoPositionGrid(self):
  1317. # Move grid to prepare for placement of next object
  1318. selectedNode = direct.selected.last
  1319. if selectedNode:
  1320. dnaNode = self.findDNANode(selectedNode)
  1321. if dnaNode == None:
  1322. return
  1323. nodeClass = DNAGetClassType(dnaNode)
  1324. deltaPos = Point3(20,0,0)
  1325. deltaHpr = VBase3(0)
  1326. if nodeClass.eq(DNA_FLAT_BUILDING):
  1327. deltaPos.setX(dnaNode.getWidth())
  1328. elif nodeClass.eq(DNA_STREET):
  1329. objectCode = dnaNode.getCode()
  1330. deltas = self.getNextSnapPoint()
  1331. deltaPos.assign(deltas[0])
  1332. deltaHpr.assign(deltas[1])
  1333. elif nodeClass.eq(DNA_LANDMARK_BUILDING):
  1334. objectCode = dnaNode.getCode()
  1335. if objectCode[-2:-1] == 'A':
  1336. deltaPos.setX(25.0)
  1337. elif objectCode[-2:-1] == 'B':
  1338. deltaPos.setX(15.0)
  1339. elif objectCode[-2:-1] == 'C':
  1340. deltaPos.setX(20.0)
  1341. # Position grid for placing next object
  1342. # Eventually we need to setHpr too
  1343. taskMgr.removeTasksNamed('autoPositionGrid')
  1344. t = direct.grid.lerpPosHpr(
  1345. deltaPos, deltaHpr, 0.25,
  1346. other = selectedNode,
  1347. blendType = 'easeInOut',
  1348. task = 'autoPositionGrid')
  1349. t.deltaPos = deltaPos
  1350. t.deltaHpr = deltaHpr
  1351. t.selectedNode = selectedNode
  1352. t.uponDeath = self.autoPositionCleanup
  1353. # Also move the camera
  1354. taskMgr.removeTasksNamed('autoMoveDelay')
  1355. handlesToCam = direct.widget.getPos(direct.camera)
  1356. handlesToCam = handlesToCam * ( direct.chan.near/handlesToCam[1])
  1357. if ((abs(handlesToCam[0]) > (direct.chan.nearWidth * 0.4)) |
  1358. (abs(handlesToCam[2]) > (direct.chan.nearHeight * 0.4))):
  1359. taskMgr.removeTasksNamed('manipulateCamera')
  1360. direct.cameraControl.centerCamIn(direct.chan, 0.5)
  1361. def autoPositionCleanup(self,state):
  1362. direct.grid.setPosHpr(state.selectedNode, state.deltaPos,
  1363. state.deltaHpr)
  1364. if direct.grid.getHprSnap():
  1365. # Clean up grid angle
  1366. direct.grid.setH(ROUND_TO(direct.grid.getH(), SNAP_ANGLE))
  1367. # MRM: What to do about pos?
  1368. if 0 & direct.grid.getXyzSnap():
  1369. # Tighten up grid position
  1370. pos = direct.grid.getPos()
  1371. roundVal = ROUND_TO(direct.grid.getGridSpacing(), 1)
  1372. x = ROUND_TO(pos[0], roundVal)
  1373. y = ROUND_TO(pos[1], roundVal)
  1374. z = ROUND_TO(pos[2], roundVal)
  1375. direct.grid.setPos(x,y,z)
  1376. def getNextSnapPoint(self):
  1377. """ Pull next pos hpr deltas off of snap list then rotate list """
  1378. if self.snapList:
  1379. deltas = self.snapList[0]
  1380. # Rotate list by one
  1381. self.snapList = self.snapList[1:] + self.snapList[:1]
  1382. return deltas
  1383. else:
  1384. return (ZERO_VEC, ZERO_VEC)
  1385. def getWallIntersectionPoint(self):
  1386. """
  1387. Return point of intersection between building's wall and line from cam
  1388. through mouse. Return false, if nothing selected
  1389. """
  1390. selectedNode = direct.selected.last
  1391. if not selectedNode:
  1392. return 0
  1393. # Find mouse point on near plane
  1394. chan = direct.chan
  1395. mouseX = chan.mouseX
  1396. mouseY = chan.mouseY
  1397. nearX = math.tan(deg2Rad(chan.fovH)/2.0) * mouseX * chan.near
  1398. nearZ = math.tan(deg2Rad(chan.fovV)/2.0) * mouseY * chan.near
  1399. # Initialize points
  1400. mCam2Wall = chan.camera.getMat(selectedNode)
  1401. mouseOrigin = Point3(0)
  1402. mouseOrigin.assign(mCam2Wall.getRow3(3))
  1403. mouseDir = Vec3(0)
  1404. mouseDir.set(nearX, chan.near, nearZ)
  1405. mouseDir.assign(mCam2Wall.xformVec(mouseDir))
  1406. # Calc intersection point
  1407. return planeIntersect(mouseOrigin, mouseDir, ZERO_POINT, NEG_Y_AXIS)
  1408. def getGridIntersectionPoint(self):
  1409. """
  1410. Return point of intersection between ground plane and line from cam
  1411. through mouse. Return false, if nothing selected
  1412. """
  1413. # Find mouse point on near plane
  1414. chan = direct.chan
  1415. mouseX = chan.mouseX
  1416. mouseY = chan.mouseY
  1417. nearX = math.tan(deg2Rad(chan.fovH)/2.0) * mouseX * chan.near
  1418. nearZ = math.tan(deg2Rad(chan.fovV)/2.0) * mouseY * chan.near
  1419. # Initialize points
  1420. mCam2Grid = chan.camera.getMat(direct.grid)
  1421. mouseOrigin = Point3(0)
  1422. mouseOrigin.assign(mCam2Grid.getRow3(3))
  1423. mouseDir = Vec3(0)
  1424. mouseDir.set(nearX, chan.near, nearZ)
  1425. mouseDir.assign(mCam2Grid.xformVec(mouseDir))
  1426. # Calc intersection point
  1427. return planeIntersect(mouseOrigin, mouseDir, ZERO_POINT, Z_AXIS)
  1428. def plantSelectedNodePath(self):
  1429. """ Move selected object to intersection point of cursor on grid """
  1430. selectedNode = direct.selected.last
  1431. if selectedNode:
  1432. # Where is the mouse relative to the grid?
  1433. hitPt = self.getGridIntersectionPoint()
  1434. selectedNode.setPos(direct.grid, hitPt)
  1435. dnaNode = self.findDNANode(selectedNode)
  1436. if dnaNode:
  1437. # Update props placement to reflect current mouse position
  1438. dnaNode.setPos(direct.selected.last.getPos())
  1439. def jumpToInsertionPoint(self):
  1440. """ Move selected object to insertion point """
  1441. selectedNode = direct.selected.last
  1442. if selectedNode:
  1443. # Check if its a dna node
  1444. dnaNode = self.findDNANode(selectedNode)
  1445. if dnaNode:
  1446. # Place the new node path at the current grid origin
  1447. selectedNode.setPos(direct.grid,0,0,0)
  1448. # Initialize angle to match last object
  1449. selectedNode.setHpr(self.getLastAngle(), 0, 0)
  1450. # Now update DNA pos and hpr to reflect final pose
  1451. dnaNode.setPos(selectedNode.getPos())
  1452. dnaNode.setHpr(selectedNode.getHpr())
  1453. # Update grid to get ready for the next object
  1454. self.autoPositionGrid()
  1455. # STYLE/DNA FILE FUNCTIONS
  1456. def loadSpecifiedDNAFile(self):
  1457. f = Filename(self.styleManager.stylePathPrefix +
  1458. '/alpha/DIRECT/LevelEditor/DNAFiles')
  1459. path = os.path.join(f.toOsSpecific(), self.outputDir)
  1460. if not os.path.isdir(path):
  1461. print 'LevelEditor Warning: Invalid default DNA directory!'
  1462. print 'Using: C:\\'
  1463. path = 'C:\\'
  1464. dnaFilename = askopenfilename(
  1465. defaultextension = '.dna',
  1466. filetypes = (('DNA Files', '*.dna'),('All files', '*')),
  1467. initialdir = path,
  1468. title = 'Load DNA File',
  1469. parent = self.panel.component('hull'))
  1470. if dnaFilename:
  1471. self.loadDNAFromFile(dnaFilename)
  1472. def saveToSpecifiedDNAFile(self):
  1473. f = Filename(self.styleManager.stylePathPrefix +
  1474. '/alpha/DIRECT/LevelEditor/DNAFiles')
  1475. path = os.path.join(f.toOsSpecific(), self.outputDir)
  1476. if not os.path.isdir(path):
  1477. print 'LevelEditor Warning: Invalid DNA save directory!'
  1478. print 'Using: C:\\'
  1479. path = 'C:\\'
  1480. dnaFilename = asksaveasfilename(
  1481. defaultextension = '.dna',
  1482. filetypes = (('DNA Files', '*.dna'),('All files', '*')),
  1483. initialdir = path,
  1484. title = 'Save DNA File as',
  1485. parent = self.panel.component('hull'))
  1486. if dnaFilename:
  1487. self.outputDNA(dnaFilename)
  1488. def loadDNAFromFile(self, filename):
  1489. # Reset level, destroying existing scene/DNA hierarcy
  1490. self.reset(fDeleteToplevel = 1, fCreateToplevel = 0)
  1491. # Now load in new file
  1492. newNPToplevel = loadDNAFile(DNASTORE, filename, CSDefault, 1)
  1493. # Make sure the topmost file DNA object gets put under DNARoot
  1494. newDNAToplevel = self.findDNANode(newNPToplevel)
  1495. # Update toplevel variables
  1496. self.createToplevel(newDNAToplevel, newNPToplevel)
  1497. def outputDNADefaultFile(self):
  1498. f = Filename(self.styleManager.stylePathPrefix +
  1499. '/alpha/DIRECT/LevelEditor/DNAFiles')
  1500. file = os.path.join(f.toOsSpecific(), self.outputDir, self.outputFile)
  1501. self.outputDNA(file)
  1502. def outputDNA(self,filename):
  1503. print 'Saving DNA to: ', filename
  1504. self.DNAData.writeDna(Filename(filename),
  1505. Notify.out(),DNASTORE)
  1506. def saveColor(self):
  1507. self.appendColorToColorPaletteFile(self.panel.colorEntry.get())
  1508. def appendColorToColorPaletteFile(self, color):
  1509. obj = self.DNATarget
  1510. if obj:
  1511. classType = DNAGetClassType(obj)
  1512. if classType.eq(DNA_WALL):
  1513. tag = 'wall_color:'
  1514. elif classType.eq(DNA_WINDOWS):
  1515. tag = 'window_color:'
  1516. elif classType.eq(DNA_DOOR):
  1517. tag = 'door_color:'
  1518. elif classType.eq(DNA_CORNICE):
  1519. tag = 'cornice_color:'
  1520. elif classType.eq(DNA_PROP):
  1521. tag = 'prop_color:'
  1522. else:
  1523. return
  1524. # Valid type, add color to file
  1525. filename = self.neighborhood + '_colors.txt'
  1526. fname = Filename(self.styleManager.stylePathPrefix +
  1527. '/alpha/DIRECT/LevelEditor/StyleFiles/' +
  1528. filename)
  1529. f = open(fname.toOsSpecific(), 'a')
  1530. f.write('%s Vec4(%.2f, %.2f, %.2f, 1.0)\n' %
  1531. (tag,
  1532. color[0]/255.0,
  1533. color[1]/255.0,
  1534. color[2]/255.0))
  1535. f.close()
  1536. def saveWallStyle(self):
  1537. if self.lastWall:
  1538. # Valid wall, add style to file
  1539. filename = self.neighborhood + '_wall_styles.txt'
  1540. fname = Filename(self.styleManager.stylePathPrefix +
  1541. '/alpha/DIRECT/LevelEditor/StyleFiles/' +
  1542. filename)
  1543. f = open(fname.toOsSpecific(), 'a')
  1544. # Add a blank line
  1545. f.write('\n')
  1546. # Now output style details to file
  1547. style = DNAWallStyle(wall = self.lastWall)
  1548. style.output(f)
  1549. # Close the file
  1550. f.close()
  1551. def saveBuildingStyle(self):
  1552. if self.lastBuilding:
  1553. # Valid wall, add style to file
  1554. filename = self.neighborhood + '_building_styles.txt'
  1555. fname = Filename(self.styleManager.stylePathPrefix +
  1556. '/alpha/DIRECT/LevelEditor/StyleFiles/' +
  1557. filename)
  1558. f = open(fname.toOsSpecific(), 'a')
  1559. # Add a blank line
  1560. f.write('\n')
  1561. # Now output style details to file
  1562. style = DNAFlatBuildingStyle(building = self.lastBuilding)
  1563. style.output(f)
  1564. # Close the file
  1565. f.close()
  1566. # GET/SET
  1567. # DNA Object elements
  1568. def getWall(self, dnaFlatBuilding, wallNum):
  1569. wallCount = 0
  1570. for i in range(dnaFlatBuilding.getNumChildren()):
  1571. child = dnaFlatBuilding.at(i)
  1572. if DNAClassEqual(child, DNA_WALL):
  1573. if wallCount == wallNum:
  1574. return child
  1575. wallCount = wallCount + 1
  1576. # Not found
  1577. return None
  1578. def computeWallNum(self, aDNAFlatBuilding, hitPt):
  1579. """
  1580. Given a hitPt, return wall number if cursor is over building
  1581. Return -1 if cursor is outside of building
  1582. """
  1583. xPt = hitPt[0]
  1584. zPt = hitPt[2]
  1585. # Left or right of building
  1586. if ((xPt < 0) | (xPt > aDNAFlatBuilding.getWidth())):
  1587. return -1
  1588. # Below the building
  1589. if zPt < 0:
  1590. return -1
  1591. # Above z = 0 and within wall width, check height of walls
  1592. heightList, offsetList = DNAGetWallHeights(aDNAFlatBuilding)
  1593. wallNum = 0
  1594. for i in range(len(heightList)):
  1595. # Compute top of wall segment
  1596. top = offsetList[i] + heightList[i]
  1597. if zPt < top:
  1598. return wallNum
  1599. wallNum = wallNum + 1
  1600. return -1
  1601. def getWindowCount(self, dnaWall):
  1602. windowCount = 0
  1603. for i in range(dnaWall.getNumChildren()):
  1604. child = dnaWall.at(i)
  1605. if DNAClassEqual(child, DNA_WINDOWS):
  1606. windowCount = windowCount + 1
  1607. # Not found
  1608. return windowCount
  1609. # Style manager edit mode
  1610. def setEditMode(self, neighborhood):
  1611. self.neighborhood = neighborhood
  1612. self.neighborhoodCode = NEIGHBORHOOD_CODES[self.neighborhood]
  1613. self.outputFile = neighborhood + '_working.dna'
  1614. if neighborhood == 'toontown_central':
  1615. self.outputDir = 'ToontownCentral'
  1616. elif neighborhood == 'donalds_dock':
  1617. self.outputDir = 'DonaldsDock'
  1618. elif neighborhood == 'minnies_melody_land':
  1619. self.outputDir = 'MinniesMelodyLand'
  1620. elif neighborhood == 'the_burrgh':
  1621. self.outputDir = 'TheBurrrgh'
  1622. self.panel.editMenu.selectitem(neighborhood)
  1623. self.styleManager.setEditMode(neighborhood)
  1624. self.selectMap(neighborhood)
  1625. def getEditMode(self):
  1626. return self.styleManager.getEditMode()
  1627. # Level Style Attributes
  1628. def __getitem__(self,attribute):
  1629. """ Return top level entry in attribute dictionary """
  1630. return self.styleManager.attributeDictionary[attribute]
  1631. def getAttribute(self, attribute):
  1632. """ Return specified attribute for current neighborhood """
  1633. return self.styleManager.getAttribute(attribute)
  1634. def getCurrent(self, attribute):
  1635. """ Return neighborhood's current selection for specified attribute """
  1636. return self.getAttribute(attribute).getCurrent()
  1637. def setCurrent(self, attribute, newCurrent):
  1638. """ Set neighborhood's current selection for specified attribute """
  1639. self.getAttribute(attribute).setCurrent(newCurrent, fEvent = 0)
  1640. def getMenu(self, attribute):
  1641. """ Return neighborhood's Pie Menu object for specified attribute """
  1642. return self.getAttribute(attribute).getMenu()
  1643. def getDict(self, attribute):
  1644. """ Return neighborhood's Dictionary for specified attribute """
  1645. return self.getAttribute(attribute).getDict()
  1646. def getList(self, attribute):
  1647. """ Return neighborhood's List for specified attribute """
  1648. return self.getAttribute(attribute).getList()
  1649. # DNA variables
  1650. def getDNAData(self):
  1651. return self.DNAData
  1652. def getDNAToplevel(self):
  1653. return self.DNAToplevel
  1654. def getDNAParent(self):
  1655. return self.DNAParent
  1656. def getDNATarget(self):
  1657. return self.DNATarget
  1658. # Node Path variables
  1659. def getNPToplevel(self):
  1660. return self.NPToplevel
  1661. def getNPParent(self):
  1662. return self.NPParent
  1663. # Count of groups added to level
  1664. def setGroupNum(self,num):
  1665. self.groupNum = num
  1666. def getGroupNum(self):
  1667. return self.groupNum
  1668. # Angle of last object added to level
  1669. def setLastAngle(self, angle):
  1670. self.lastAngle = angle
  1671. def getLastAngle(self):
  1672. return self.lastAngle
  1673. class LevelStyleManager:
  1674. """Class which reads in style files and manages class variables"""
  1675. def __init__(self):
  1676. # Used to locate the alpha mount on windows (i.e. on what drive)
  1677. self.stylePathPrefix = base.config.GetString('style-path-prefix', '')
  1678. # The main dictionary holding all attribute objects
  1679. self.attributeDictionary = {}
  1680. # Create the style samples
  1681. self.createWallStyleAttributes()
  1682. self.createBuildingStyleAttributes()
  1683. self.createColorAttributes()
  1684. self.createDNAAttributes()
  1685. self.createMiscAttributes()
  1686. # WALL STYLE FUNCTIONS
  1687. def createWallStyleAttributes(self):
  1688. """
  1689. Create a wallStyle entry in the attribute dictionary
  1690. This will be a dictionary of style attributes, one per neighborhood
  1691. """
  1692. # First create an empty dictionary
  1693. dict = self.attributeDictionary['wall_style'] = {}
  1694. # Create a attribute object for each neighborhood
  1695. for neighborhood in NEIGHBORHOODS:
  1696. attribute = LevelAttribute('wall_style')
  1697. attribute.setDict(
  1698. # Create a wall style dictionary for each neighborhood
  1699. self.createWallStyleDictionary(neighborhood))
  1700. # Using this dictionary, create color pie menus
  1701. attribute.setMenu(
  1702. self.createWallStyleMenu(neighborhood, attribute.getDict()))
  1703. dict[neighborhood] = attribute
  1704. def createWallStyleDictionary(self, neighborhood):
  1705. """
  1706. Create a dictionary of wall styles for a neighborhood
  1707. """
  1708. filename = neighborhood + '_wall_styles.txt'
  1709. print 'Loading wall styles from: ' + filename
  1710. styleData = self.getStyleFileData(filename)
  1711. return self.initializeWallStyleDictionary(styleData, neighborhood)
  1712. def initializeWallStyleDictionary(self, styleData, neighborhood):
  1713. """
  1714. Fill in the wall style dictionary with data from the style file
  1715. """
  1716. styleDictionary = {}
  1717. styleCount = 0
  1718. code = NEIGHBORHOOD_CODES[neighborhood]
  1719. while styleData:
  1720. l = styleData[0]
  1721. if l == 'wallStyle':
  1722. # Start of new style, strip off first line then extract style
  1723. style, styleData = self.extractWallStyle(styleData)
  1724. style.name = code + '_wall_style_' + `styleCount`
  1725. # Store style in dictionary
  1726. styleDictionary[style.name] = style
  1727. styleCount = styleCount + 1
  1728. # Move to next line
  1729. styleData = styleData[1:]
  1730. return styleDictionary
  1731. def extractWallStyle(self, styleData):
  1732. """
  1733. Pull out one style from a list of style data. Will keep
  1734. processing data until endWallStyle of end of data is reached.
  1735. Returns a wall style and remaining styleData.
  1736. """
  1737. # Create default style
  1738. style = DNAWallStyle()
  1739. # Strip off first line
  1740. styleData = styleData[1:]
  1741. while styleData:
  1742. l = styleData[0]
  1743. if l == 'endWallStyle':
  1744. # End of style found, break out of while loop
  1745. # Note, endWallStyle line is *not* stripped off
  1746. return style, styleData
  1747. else:
  1748. pair = map(string.strip, l.split(':'))
  1749. if style.__dict__.has_key(pair[0]):
  1750. # Convert colors and count strings to numerical values
  1751. if ((string.find(pair[0],'_color') >= 0) |
  1752. (string.find(pair[0],'_count') >= 0)):
  1753. style[pair[0]] = eval(pair[1])
  1754. else:
  1755. style[pair[0]] = pair[1]
  1756. else:
  1757. print 'getStyleDictionaryFromStyleData: Invalid Key'
  1758. print pair[0]
  1759. styleData = styleData[1:]
  1760. # No end of style found, return style data as is
  1761. return style, None
  1762. def createWallStyleMenu(self, neighborhood, dictionary):
  1763. """
  1764. Create a wall style pie menu
  1765. """
  1766. numItems = len(dictionary)
  1767. newStyleMenu = hidden.attachNewNode(
  1768. NamedNode(neighborhood + '_style_menu'))
  1769. radius = 0.7
  1770. angle = deg2Rad(360.0/numItems)
  1771. keys = dictionary.keys()
  1772. keys.sort()
  1773. styles = map(lambda x, d = dictionary: d[x], keys)
  1774. sf = 0.03
  1775. aspectRatio = (direct.chan.width/float(direct.chan.height))
  1776. for i in range(numItems):
  1777. # Get the node
  1778. node = self.createWallStyleSample(styles[i])
  1779. bounds = node.getBounds()
  1780. center = bounds.getCenter()
  1781. center = center * sf
  1782. # Reposition it
  1783. node.setPos((radius * math.cos(i * angle)) - center[0],
  1784. 0.0,
  1785. ((radius * aspectRatio * math.sin(i * angle)) -
  1786. center[2]))
  1787. # Scale it
  1788. node.setScale(sf)
  1789. # Add it to the styleMenu
  1790. node.reparentTo(newStyleMenu)
  1791. # Scale the whole shebang down by 0.5
  1792. newStyleMenu.setScale(0.5)
  1793. # Create and return a pie menu
  1794. return PieMenu(newStyleMenu, styles)
  1795. def createWallStyleSample(self, wallStyle):
  1796. """
  1797. Create a style sample using the DNA info in the style
  1798. """
  1799. bldg = DNAFlatBuilding()
  1800. bldgStyle = DNAFlatBuildingStyle(styleList = [(wallStyle, 10.0)])
  1801. self.setDNAFlatBuildingStyle(bldg, bldgStyle, width = 12.0,
  1802. name = 'wall_style_sample')
  1803. return bldg.traverse(hidden, DNASTORE, 1)
  1804. # BUILDING STYLE FUNCTIONS
  1805. def createBuildingStyleAttributes(self):
  1806. """
  1807. Create a buildingStyle entry in the attribute dictionary
  1808. This will be a dictionary of style attributes, one per neighborhood
  1809. """
  1810. # First create an empty dictionary
  1811. styleDict = self.attributeDictionary['building_style'] = {}
  1812. # Create an attribute object of all styles for each neighborhood
  1813. for neighborhood in NEIGHBORHOODS:
  1814. attribute = LevelAttribute('building_style')
  1815. attribute.setDict(
  1816. # Create a wall style dictionary for each neighborhood
  1817. self.createBuildingStyleDictionary(neighborhood))
  1818. # Using this dictionary, create color pie menus
  1819. attribute.setMenu(
  1820. self.createBuildingStyleMenu(neighborhood,
  1821. attribute.getDict()))
  1822. styleDict[neighborhood] = attribute
  1823. # Now create attribute entries sorted according to building
  1824. # height styles
  1825. attrDict = {}
  1826. # Create an attribute dictionary entry for each building height type
  1827. for type in BUILDING_TYPES:
  1828. key = 'building_style_' + type
  1829. attrDict[type] = self.attributeDictionary[key] = {}
  1830. # For each neighborhood create attribute for each height type
  1831. for neighborhood in NEIGHBORHOODS:
  1832. # Temp lists to accumulate neighborhood styles
  1833. # sorted by height type
  1834. styleLists = {}
  1835. for type in BUILDING_TYPES:
  1836. styleLists[type] = []
  1837. # Sort through the styles and store in separate lists
  1838. for style in styleDict[neighborhood].getList():
  1839. heightType = string.strip(string.split(style.name, ':')[1])
  1840. styleLists[heightType].append(style)
  1841. # Now put these lists in appropriate neighborhood attribute
  1842. for type in BUILDING_TYPES:
  1843. attribute = LevelAttribute('building_style_' + type)
  1844. attribute.setList(styleLists[type])
  1845. # Store them according to neighborhood
  1846. attrDict[type][neighborhood] = attribute
  1847. def createBuildingStyleDictionary(self, neighborhood):
  1848. """
  1849. Create a dictionary of wall styles for a neighborhood
  1850. """
  1851. filename = neighborhood + '_building_styles.txt'
  1852. print 'Loading building styles from: ' + filename
  1853. styleData = self.getStyleFileData(filename)
  1854. return self.initializeBuildingStyleDictionary(styleData, neighborhood)
  1855. def initializeBuildingStyleDictionary(self, styleData, neighborhood):
  1856. """
  1857. Fill in the building style dictionary with data from the style file
  1858. """
  1859. # Create a dictionary of all building styles, this will later be
  1860. # split out into a separate dictionary for each building type
  1861. # e.g. 20, 10-10, 10-20....
  1862. bldgStyleDictionary = {}
  1863. styleCount = 0
  1864. code = NEIGHBORHOOD_CODES[neighborhood]
  1865. while styleData:
  1866. # Pull out first line
  1867. l = styleData[0]
  1868. if l[:13] == 'buildingStyle':
  1869. # Start with empty style list
  1870. bldgStyle = DNAFlatBuildingStyle(styleList = [])
  1871. # Extract height information found at end of line
  1872. heightCode = string.strip(string.split(l, ':')[1])
  1873. heightList = map(string.atof, string.split(heightCode, '_'))
  1874. # Construct name for building style. Tack on height code
  1875. # to be used later to split styles by heightCode
  1876. bldgStyle.name = (
  1877. code + '_building_style_' + `styleCount` +
  1878. ':' + heightCode)
  1879. # Increment counter
  1880. styleCount = styleCount + 1
  1881. # Reset wall counter to zero
  1882. wallCount = 0
  1883. elif l == 'endBuildingStyle':
  1884. # Done, add new style to dictionary
  1885. bldgStyleDictionary[bldgStyle.name] = bldgStyle
  1886. elif l[:9] == 'wallStyle':
  1887. # Beginning of next wall style
  1888. wallStyle, styleData = self.extractWallStyle(styleData)
  1889. wallStyle.name = bldgStyle.name + '_wall_' + `wallCount`
  1890. try:
  1891. height = heightList[wallCount]
  1892. except IndexError:
  1893. height = 10.0
  1894. # Add wall style to building style
  1895. bldgStyle.add(wallStyle, height)
  1896. # Increment wall counter
  1897. wallCount = wallCount + 1
  1898. # Move to next line
  1899. styleData = styleData[1:]
  1900. return bldgStyleDictionary
  1901. def createBuildingStyleMenu(self, neighborhood, dictionary):
  1902. """
  1903. Create a wall style pie menu
  1904. """
  1905. numItems = len(dictionary)
  1906. newStyleMenu = hidden.attachNewNode(
  1907. NamedNode(neighborhood + '_style_menu'))
  1908. radius = 0.7
  1909. angle = deg2Rad(360.0/numItems)
  1910. keys = dictionary.keys()
  1911. keys.sort()
  1912. styles = map(lambda x, d = dictionary: d[x], keys)
  1913. sf = 0.02
  1914. aspectRatio = (direct.chan.width/float(direct.chan.height))
  1915. for i in range(numItems):
  1916. # Get the node
  1917. node = self.createBuildingStyleSample(styles[i])
  1918. bounds = node.getBounds()
  1919. center = bounds.getCenter()
  1920. center = center * sf
  1921. # Reposition it
  1922. node.setPos((radius * math.cos(i * angle)) - center[0],
  1923. 0.0,
  1924. ((radius * aspectRatio * math.sin(i * angle)) -
  1925. center[2]))
  1926. # Scale it
  1927. node.setScale(sf)
  1928. # Add it to the styleMenu
  1929. node.reparentTo(newStyleMenu)
  1930. # Scale the whole shebang down by 0.5
  1931. newStyleMenu.setScale(0.5)
  1932. # Create and return a pie menu
  1933. return PieMenu(newStyleMenu, styles)
  1934. def createBuildingStyleSample(self, bldgStyle):
  1935. """
  1936. Create a style sample using the DNA info in the style
  1937. """
  1938. bldg = DNAFlatBuilding()
  1939. self.setDNAFlatBuildingStyle(bldg, bldgStyle, width = 10.0,
  1940. name = 'building_style_sample')
  1941. return bldg.traverse(hidden, DNASTORE, 1)
  1942. def setDNAFlatBuildingStyle(self, fb, bldgStyle, width = 10.0,
  1943. name = 'building'):
  1944. """ Set DNAFlatBuilding style. """
  1945. # Remove flat building's children
  1946. DNARemoveChildren(fb)
  1947. # Update the name
  1948. fb.setName(name)
  1949. # Create the walls
  1950. styleList = bldgStyle.styleList
  1951. heightList = bldgStyle.heightList
  1952. for i in range(len(styleList)):
  1953. wallStyle = styleList[i]
  1954. # Get Style
  1955. if not wallStyle:
  1956. # If set to None use default style
  1957. wallStyle = DNAWallStyle()
  1958. # Try to get height
  1959. try:
  1960. wallHeight = heightList[i]
  1961. except IndexError:
  1962. wallHeight = 10.0
  1963. # Create wall accordingly
  1964. wall = DNAWall()
  1965. self.setDNAWallStyle(wall, wallStyle, wallHeight)
  1966. # Add it to building DNA
  1967. fb.add(wall)
  1968. # Set the buildings width
  1969. fb.setWidth(width)
  1970. def setDNAWallStyle(self, wall, style, height = 10.0):
  1971. """ Set DNAWall to input style. """
  1972. # Remove wall's children
  1973. DNARemoveChildren(wall)
  1974. # Update wall attributes
  1975. wall.setCode(style['wall_texture'])
  1976. wall.setColor(style['wall_color'])
  1977. wall.setHeight(height)
  1978. # Add windows if necessary
  1979. if style['window_texture']:
  1980. windows = DNAWindows()
  1981. windows.setWindowCount(style['window_count'])
  1982. # Set window's attributes
  1983. windows.setCode(style['window_texture'])
  1984. windows.setColor(style['window_color'])
  1985. # Add windows to the wall
  1986. wall.add(windows)
  1987. # Add a window awning if necessary
  1988. if style['window_awning_texture']:
  1989. awning = DNAProp()
  1990. # Update awning's attributes
  1991. awning.setCode(style['window_awning_texture'])
  1992. awning.setColor(style['window_awning_color'])
  1993. # Add awning to window
  1994. windows.add(awning)
  1995. # Add a door if necessary
  1996. if style['door_texture']:
  1997. door = DNADoor()
  1998. # Set the door's attributes
  1999. door.setCode(style['door_texture'])
  2000. door.setColor(style['door_color'])
  2001. # Add door to wall
  2002. wall.add(door)
  2003. # Add a door awning if necessary
  2004. if style['door_awning_texture']:
  2005. awning = DNAProp()
  2006. awning.setCode(style['door_awning_texture'])
  2007. awning.setColor(style['door_awning_color'])
  2008. door.add(awning)
  2009. # And a cornice if necessary
  2010. if style['cornice_texture']:
  2011. cornice = DNACornice()
  2012. # Set the cornice's attributes
  2013. cornice.setCode(style['cornice_texture'])
  2014. cornice.setColor(style['cornice_color'])
  2015. # Add cornice to wall
  2016. wall.add(cornice)
  2017. def printFlatBuildingStyle(self, building):
  2018. for i in range(building.getNumChildren()):
  2019. child = building.at(i)
  2020. if DNAClassEqual(child, DNA_WALL):
  2021. self.printWallStyle(child)
  2022. def printWallStyle(self, wall):
  2023. print 'wall_texture: ' + wall.getCode()
  2024. color = wall.getColor()
  2025. print ('wall_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
  2026. (color[0], color[1], color[2]))
  2027. for i in range(wall.getNumChildren()):
  2028. child = wall.at(i)
  2029. if DNAClassEqual(child, DNA_WINDOWS):
  2030. print 'window_texture: ' + child.getCode()
  2031. color = child.getColor()
  2032. print ('window_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
  2033. (color[0], color[1], color[2]))
  2034. # MRM: Check for awnings here
  2035. elif DNAClassEqual(child, DNA_DOOR):
  2036. print 'door_texture: ' + child.getCode()
  2037. color = child.getColor()
  2038. print ('door_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
  2039. (color[0], color[1], color[2]))
  2040. # MRM: Check for awnings here
  2041. elif DNAClassEqual(child, DNA_CORNICE):
  2042. print 'cornice_texture: ' + child.getCode()
  2043. color = child.getColor()
  2044. print ('cornice_color: Vec4(%.3f, %.3f, %.3f, 1.0)' %
  2045. (color[0], color[1], color[2]))
  2046. # COLOR PALETTE FUNCTIONS
  2047. def createColorAttributes(self):
  2048. # First compile color information for each neighborhood
  2049. colorDictionary = {}
  2050. colorMenuDictionary = {}
  2051. for neighborhood in NEIGHBORHOODS:
  2052. colorDictionary[neighborhood] = (
  2053. # Create a wall style dictionary for each neighborhood
  2054. self.createColorDictionary(neighborhood))
  2055. # Using this dictionary, create color pie menus
  2056. colorMenuDictionary[neighborhood] = (
  2057. self.createColorMenus(
  2058. neighborhood, colorDictionary[neighborhood]))
  2059. # Now store this info in the appropriate place in the attribute dict
  2060. for colorType in COLOR_TYPES:
  2061. neighborhoodDict = self.attributeDictionary[colorType] = {}
  2062. for neighborhood in NEIGHBORHOODS:
  2063. attribute = LevelAttribute(colorType)
  2064. dict = {}
  2065. # Add colors to attribute dictionary
  2066. colorList = colorDictionary[neighborhood][colorType]
  2067. for i in range(len(colorList)):
  2068. dict[i] = colorList[i]
  2069. attribute.setDict(dict)
  2070. attribute.setMenu(
  2071. colorMenuDictionary[neighborhood][colorType])
  2072. neighborhoodDict[neighborhood] = attribute
  2073. def createColorDictionary(self, neighborhood):
  2074. filename = neighborhood + '_colors.txt'
  2075. print 'Loading Color Palettes from: ' + filename
  2076. colorData = self.getStyleFileData(filename)
  2077. return self.getColorDictionary(colorData)
  2078. def getColorDictionary(self, colorData):
  2079. # Initialze neighborhod color dictionary
  2080. dict = {}
  2081. for colorType in COLOR_TYPES:
  2082. dict[colorType] = DEFAULT_COLORS[:]
  2083. # Add color information to appropriate sub-list
  2084. for line in colorData:
  2085. pair = map(string.strip, line.split(':'))
  2086. key = pair[0]
  2087. if dict.has_key(key):
  2088. dict[key].append(eval(pair[1]))
  2089. else:
  2090. print 'LevelStyleManager.getColorDictionary key not found'
  2091. return dict
  2092. def createColorMenus(self, neighborhood, dictionary):
  2093. menuDict = {}
  2094. keys = dictionary.keys()
  2095. for key in keys:
  2096. menuDict[key] = (
  2097. self.createColorMenu(neighborhood + key, dictionary[key]))
  2098. return menuDict
  2099. def createColorMenu(self, menuName, colorList, radius = 0.7, sf = 2.0):
  2100. # Create color chips for each color
  2101. numItems = len(colorList)
  2102. # Attach it to hidden for now
  2103. newColorMenu = hidden.attachNewNode(NamedNode(menuName + 'Menu'))
  2104. # Compute the angle per item
  2105. angle = deg2Rad(360.0/float(numItems))
  2106. aspectRatio = (direct.chan.width / float(direct.chan.height))
  2107. # Attach the color chips to the new menu and adjust sizes
  2108. for i in range (numItems):
  2109. # Create the node and set its color
  2110. node = OnscreenText(' ', 0.0, 0.0)
  2111. node.setColor(colorList[i])
  2112. bounds = node.getBounds()
  2113. center = bounds.getCenter()
  2114. center = center * (sf * node.getScale()[0])
  2115. # Reposition it
  2116. node.setXY((radius * math.cos(i * angle)) - center[0],
  2117. (radius * aspectRatio * math.sin(i * angle)) -
  2118. center[2])
  2119. node.setScale(node.getScale() * sf)
  2120. # Add it to the wallColorMenu
  2121. node.reparentTo(newColorMenu)
  2122. # Scale the whole shebang down by 0.5
  2123. newColorMenu.setScale(0.5)
  2124. # Create and return resulting pie menu
  2125. return PieMenu(newColorMenu, colorList)
  2126. # DNA ATTRIBUTES
  2127. def createDNAAttributes(self):
  2128. # Create the DNA Attribute entries
  2129. # Most objects are oriented with graphical menu items
  2130. # Street and props aren't oiented and use text menus
  2131. for dnaType in DNA_TYPES:
  2132. # Create a dictionary of dna types
  2133. dict = {}
  2134. if ((dnaType == 'street') | (dnaType == 'prop') |
  2135. (dnaType == 'toon_landmark')):
  2136. dnaList = self.getCatalogCodes(dnaType)
  2137. else:
  2138. dnaList = [None] + self.getCatalogCodesSuffix(dnaType, '_ur')
  2139. # Add dnaCodes to attribute dictionary
  2140. for i in range(len(dnaList)):
  2141. dict[i] = dnaList[i]
  2142. # Create a LevelAttribute
  2143. attribute = LevelAttribute(dnaType + '_texture')
  2144. attribute.setDict(dict)
  2145. # Prepend None to allow option of no item
  2146. if ((dnaType == 'street') | (dnaType == 'prop') |
  2147. (dnaType == 'toon_landmark')):
  2148. attribute.setMenu(self.createTextPieMenu(dnaType, dnaList))
  2149. elif (dnaType == 'wall'):
  2150. attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
  2151. sf = 0.25))
  2152. elif (dnaType == 'door'):
  2153. attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
  2154. sf = 0.035))
  2155. elif (dnaType == 'cornice'):
  2156. attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
  2157. sf = 0.5))
  2158. elif (dnaType == 'window'):
  2159. attribute.setMenu(self.createDNAPieMenu(dnaType, dnaList,
  2160. sf = 0.125))
  2161. else:
  2162. print 'unknown attribute'
  2163. # Add it to the attributeDictionary
  2164. self.attributeDictionary[dnaType + '_texture'] = attribute
  2165. def createDNAPieMenu(self, dnaType, dnaList, radius = 0.7, sf = 1.0):
  2166. # Get the currently available window options
  2167. numItems = len(dnaList)
  2168. # Create a top level node to hold the menu
  2169. newMenu = hidden.attachNewNode(NamedNode(dnaType + 'Menu'))
  2170. # Compute angle increment per item
  2171. angle = deg2Rad(360.0/numItems)
  2172. aspectRatio = direct.chan.width /float(direct.chan.height)
  2173. # Add items
  2174. for i in range(0, numItems):
  2175. if dnaList[i]:
  2176. # Get the node
  2177. node = DNASTORE.findNode(dnaList[i])
  2178. else:
  2179. node = None
  2180. if node:
  2181. # Add it to the window menu
  2182. path = node.instanceTo(newMenu)
  2183. # Place menu nodes in a circle, offset each in X and Z
  2184. # by half node width/height
  2185. bounds = path.getBounds()
  2186. center = bounds.getCenter()
  2187. center = center * sf
  2188. path.setPos((radius * math.cos(i * angle)) - center[0],
  2189. 0.0,
  2190. ((radius * aspectRatio * math.sin(i * angle)) -
  2191. center[2]))
  2192. path.setScale(sf)
  2193. # Scale the whole shebang down by 0.5
  2194. newMenu.setScale(0.5)
  2195. # Create and return a pie menu
  2196. return PieMenu(newMenu, dnaList)
  2197. def createTextPieMenu(self, dnaType, textList, radius = 0.7, sf = 1.0):
  2198. numItems = len(textList)
  2199. # Create top level node for new menu
  2200. newMenu = hidden.attachNewNode(NamedNode(dnaType + 'Menu'))
  2201. # Compute angle per item
  2202. angle = deg2Rad(360.0/numItems)
  2203. aspectRatio = direct.chan.width/float(direct.chan.height)
  2204. # Add items
  2205. for i in range (numItems):
  2206. # Create onscreen text node for each item
  2207. if (textList[i] != None):
  2208. node = OnscreenText(str(textList[i]),0,0)
  2209. else:
  2210. node = None
  2211. if node:
  2212. # Reposition it
  2213. bounds = node.getBounds()
  2214. center = bounds.getCenter()
  2215. center = center * (sf * node.getScale()[0])
  2216. node.setXY(radius * math.cos(i * angle) - center[0],
  2217. ((radius * aspectRatio * math.sin(i * angle)) -
  2218. center[2]))
  2219. node.setScale(node.getScale() * sf)
  2220. # Add it to the newMenu
  2221. node.reparentTo(newMenu)
  2222. # Scale the whole shebang down by 0.5
  2223. newMenu.setScale(0.5)
  2224. # Create and return a pie menu
  2225. return PieMenu(newMenu, textList)
  2226. # MISCELLANEOUS MENUS
  2227. def createMiscAttributes(self):
  2228. # Num windows menu
  2229. self.createMiscAttribute('window_count',[0,1,2,3,4])
  2230. # Building width menu
  2231. self.createMiscAttribute('building_width',[5,10,15,15.6,20,20.7,25])
  2232. # Building types
  2233. self.createMiscAttribute('building_type', BUILDING_TYPES)
  2234. # MRM: Need offset on these menus
  2235. # Wall orientation menu
  2236. self.createMiscAttribute('wall_orientation', ['ur','ul','dl','dr'])
  2237. # Wall height menu
  2238. self.createMiscAttribute('wall_height', [10, 20])
  2239. # Window orientation menu
  2240. self.createMiscAttribute('window_orientation', ['ur','ul',None,None])
  2241. # Door orientation menu
  2242. self.createMiscAttribute('door_orientation', ['ur','ul',None,None])
  2243. # Cornice orientation menu
  2244. self.createMiscAttribute('cornice_orientation', ['ur','ul',None,None])
  2245. def createMiscAttribute(self, miscType, miscList, sf = 3.0):
  2246. # Create a dictionary from miscList
  2247. dict = {}
  2248. # Add items to attribute dictionary
  2249. for i in range(len(miscList)):
  2250. dict[i] = miscList[i]
  2251. # Create the miscellaneous Attribute entries
  2252. attribute = LevelAttribute(miscType)
  2253. attribute.setDict(dict)
  2254. # Now create a pie menu
  2255. attribute.setMenu(self.createTextPieMenu(miscType, miscList,
  2256. sf = sf))
  2257. # Add it to the attributeDictionary
  2258. self.attributeDictionary[miscType] = attribute
  2259. # GENERAL FUNCTIONS
  2260. def getStyleFileData(self, filename):
  2261. """
  2262. Open the specified file and strip out unwanted whitespace and
  2263. empty lines. Return file as list, one file line per element.
  2264. """
  2265. fname = Filename(self.stylePathPrefix +
  2266. '/alpha/DIRECT/LevelEditor/StyleFiles/' +
  2267. filename)
  2268. f = open(fname.toOsSpecific(), 'r')
  2269. rawData = f.readlines()
  2270. f.close()
  2271. styleData = []
  2272. for line in rawData:
  2273. l = string.strip(line)
  2274. if l:
  2275. styleData.append(l)
  2276. return styleData
  2277. # UTILITY FUNCTIONS
  2278. def getAttribute(self, attribute):
  2279. """ Return specified attribute for current neighborhood """
  2280. levelAttribute = self.attributeDictionary[attribute]
  2281. # Get attribute for current neighborhood
  2282. if (type(levelAttribute) == types.DictionaryType):
  2283. levelAttribute = levelAttribute[self.getEditMode()]
  2284. return levelAttribute
  2285. def getCatalogCode(self, category, i):
  2286. return DNASTORE.getCatalogCode(category, i)
  2287. def getCatalogCodes(self, category):
  2288. numCodes = DNASTORE.getNumCatalogCodes(category)
  2289. codes = []
  2290. for i in range(numCodes):
  2291. codes.append(DNASTORE.getCatalogCode(category, i))
  2292. return codes
  2293. def getCatalogCodesSuffix(self, category, suffix):
  2294. codes = self.getCatalogCodes(category)
  2295. orientedCodes = []
  2296. for code in codes:
  2297. if code[-3:] == suffix:
  2298. orientedCodes.append(code)
  2299. return orientedCodes
  2300. def setEditMode(self, mode):
  2301. """Set current neighborhood editing mode"""
  2302. self._editMode = mode
  2303. def getEditMode(self):
  2304. """Get current neighborhood editing mode"""
  2305. return self._editMode
  2306. class LevelAttribute:
  2307. """Class which encapsulates a pie menu and a set of items"""
  2308. def __init__(self, name):
  2309. # Record name
  2310. self.name = name
  2311. # Pie menu used to pick an option
  2312. self._menu = None
  2313. # Dictionary of available options
  2314. self._dict = None
  2315. self._list = []
  2316. # Currently selected option
  2317. self._current = None
  2318. def setCurrent(self, newValue, fEvent = 1):
  2319. self._current = newValue
  2320. # Send event if specified
  2321. if fEvent:
  2322. messenger.send('select_' + self.name, [self._current])
  2323. def setMenu(self,menu):
  2324. self._menu = menu
  2325. self._menu.action = self.setCurrent
  2326. def setDict(self,dict):
  2327. self._dict = dict
  2328. # Create a list from the dictionary
  2329. self._list = dict.values()
  2330. # Initialize current to first item
  2331. if (len(self._list) > 0):
  2332. self._current = self._list[0]
  2333. def setList(self,list):
  2334. self._list = list
  2335. # Create a dictionary from the list
  2336. self._dict = {}
  2337. count = 0
  2338. for item in list:
  2339. self._dict[count] = item
  2340. count = count + 1
  2341. # Initialize current to first item
  2342. if (len(self._list) > 0):
  2343. self._current = self._list[0]
  2344. def getCurrent(self):
  2345. return self._current
  2346. def getMenu(self):
  2347. return self._menu
  2348. def getDict(self):
  2349. return self._dict
  2350. def getList(self):
  2351. return self._list
  2352. class DNAFlatBuildingStyle:
  2353. """Class to hold attributes of a building style"""
  2354. def __init__(self, building = None, styleList = None, name = 'bldg_style'):
  2355. self.name = name
  2356. if building:
  2357. # Passed in a building
  2358. self.copy(building)
  2359. elif styleList != None:
  2360. # Passed in a list of style-height pairs
  2361. self.styleList = []
  2362. self.heightList = []
  2363. for pair in styleList:
  2364. self.add(pair[0], pair[1])
  2365. else:
  2366. # Use default style/height
  2367. self.styleList = [DNAWallStyle()]
  2368. self.heightList = [10]
  2369. def add(self, style, height):
  2370. self.styleList.append(style)
  2371. self.heightList.append(height)
  2372. def copy(self, building):
  2373. self.styleList = []
  2374. self.heightList = DNAGetWallHeights(building)[0]
  2375. for i in range(building.getNumChildren()):
  2376. child = building.at(i)
  2377. if DNAClassEqual(child, DNA_WALL):
  2378. wallStyle = DNAWallStyle(wall = child)
  2379. self.styleList.append(wallStyle)
  2380. def output(self, file = sys.stdout):
  2381. def createHeightCode(s = self):
  2382. def joinHeights(h1,h2):
  2383. return '%s_%s' % (h1, h2)
  2384. hl = map(ROUND_INT, s.heightList)
  2385. return reduce(joinHeights, hl)
  2386. file.write('buildingStyle: %s\n' % createHeightCode())
  2387. for style in self.styleList:
  2388. style.output(file)
  2389. file.write('endBuildingStyle\n')
  2390. class DNAWallStyle:
  2391. """Class to hold attributes of a wall style (textures, colors, etc)"""
  2392. def __init__(self, wall = None, name = 'wall_style'):
  2393. # First initialize everything
  2394. self.name = name
  2395. self.wall_texture = 'wall_md_blank_ur'
  2396. self.wall_color = Vec4(1.0)
  2397. self.window_count = 2
  2398. self.window_texture = None
  2399. self.window_color = Vec4(1.0)
  2400. self.window_awning_texture = None
  2401. self.window_awning_color = Vec4(1.0)
  2402. self.door_texture = None
  2403. self.door_color = Vec4(1.0)
  2404. self.door_awning_texture = None
  2405. self.door_awning_color = Vec4(1.0)
  2406. self.cornice_texture = None
  2407. self.cornice_color = Vec4(1.0)
  2408. # Then copy the specifics for the wall if input
  2409. if wall:
  2410. self.copy(wall)
  2411. def copy(self, wall):
  2412. self.wall_texture = wall.getCode()
  2413. self.wall_color = wall.getColor()
  2414. for i in range(wall.getNumChildren()):
  2415. child = wall.at(i)
  2416. if DNAClassEqual(child, DNA_WINDOWS):
  2417. self.window_count = child.getWindowCount()
  2418. self.window_texture = child.getCode()
  2419. self.window_color = child.getColor()
  2420. # MRM: Check for awnings here
  2421. elif DNAClassEqual(child, DNA_DOOR):
  2422. self.door_texture = child.getCode()
  2423. self.door_color = child.getColor()
  2424. # MRM: Check for awnings here
  2425. elif DNAClassEqual(child, DNA_CORNICE):
  2426. self.cornice_texture = child.getCode()
  2427. self.cornice_color = child.getColor()
  2428. def output(self, file = sys.stdout):
  2429. """ Output style description to a file """
  2430. def writeAttributes(f, type, s = self):
  2431. color = s[type + '_color']
  2432. f.write('%s_texture: %s\n' % (type, s[type + '_texture']))
  2433. f.write('%s_color: Vec4(%.2f, %.2f, %.2f, 1.0)\n' %
  2434. (type, color[0], color[1], color[2]))
  2435. file.write('wallStyle\n')
  2436. writeAttributes(file, 'wall')
  2437. if self['window_texture']:
  2438. writeAttributes(file, 'window')
  2439. file.write('window_count: %s\n' % self['window_count'])
  2440. if self['window_awning_texture']:
  2441. writeAttributes(file, 'window_awning')
  2442. if self['door_texture']:
  2443. writeAttributes(file, 'door')
  2444. if self['door_awning_texture']:
  2445. writeAttributes(file, 'door_awning')
  2446. if self['cornice_texture']:
  2447. writeAttributes(file, 'cornice')
  2448. file.write('endWallStyle\n')
  2449. # Convience functions to facilitate class use
  2450. def __setitem__(self, index, item):
  2451. self.__dict__[index] = item
  2452. def __getitem__(self, index):
  2453. return self.__dict__[index]
  2454. def __repr__(self):
  2455. return(
  2456. 'Name: %s\n' % self.name +
  2457. 'Wall Texture: %s\n' % self.wall_texture +
  2458. 'Wall Color: %s\n' % self.wall_color +
  2459. 'Window Texture: %s\n' % self.window_texture +
  2460. 'Window Color: %s\n' % self.window_color +
  2461. 'Window Awning Texture: %s\n' % self.window_awning_texture +
  2462. 'Window Awning Color: %s\n' % self.window_awning_color +
  2463. 'Door Texture: %s\n' % self.door_texture +
  2464. 'Door Color: %s\n' % self.door_color +
  2465. 'Door Awning Texture: %s\n' % self.door_awning_texture +
  2466. 'Door Awning Color: %s\n' % self.door_awning_color +
  2467. 'Cornice Texture: %s\n' % self.cornice_texture +
  2468. 'Cornice Color: %s\n' % self.cornice_color
  2469. )
  2470. class OldLevelEditor(NodePath, PandaObject):
  2471. pass
  2472. class LevelEditorPanel(Pmw.MegaToplevel):
  2473. def __init__(self, levelEditor, parent = None, **kw):
  2474. INITOPT = Pmw.INITOPT
  2475. optiondefs = (
  2476. ('title', 'Toontown Level Editor', None),
  2477. )
  2478. self.defineoptions(kw, optiondefs)
  2479. Pmw.MegaToplevel.__init__(self, parent, title = self['title'])
  2480. self.levelEditor = levelEditor
  2481. self.styleManager = self.levelEditor.styleManager
  2482. self.fUpdateSelected = 1
  2483. # Handle to the toplevels hull
  2484. hull = self.component('hull')
  2485. balloon = self.balloon = Pmw.Balloon(hull)
  2486. # Start with balloon help disabled
  2487. self.balloon.configure(state = 'none')
  2488. menuFrame = Frame(hull, relief = GROOVE, bd = 2)
  2489. menuFrame.pack(fill = X, expand = 1)
  2490. menuBar = Pmw.MenuBar(menuFrame, hotkeys = 1, balloon = balloon)
  2491. menuBar.pack(side = LEFT, expand = 1, fill = X)
  2492. menuBar.addmenu('Level Editor', 'Level Editor Operations')
  2493. menuBar.addmenuitem('Level Editor', 'command',
  2494. 'Load DNA from specified file',
  2495. label = 'Load DNA...',
  2496. command = self.levelEditor.loadSpecifiedDNAFile)
  2497. menuBar.addmenuitem('Level Editor', 'command',
  2498. 'Save DNA data to specified file',
  2499. label = 'Save DNA As...',
  2500. command = self.levelEditor.saveToSpecifiedDNAFile)
  2501. menuBar.addmenuitem('Level Editor', 'command',
  2502. 'Save DNA File',
  2503. label = 'Save DNA',
  2504. command = self.levelEditor.outputDNADefaultFile)
  2505. menuBar.addmenuitem('Level Editor', 'command',
  2506. 'Edit Visibility Groups',
  2507. label = 'Edit Vis Groups',
  2508. command = self.levelEditor.editDNAVisGroups)
  2509. menuBar.addmenuitem('Level Editor', 'command',
  2510. 'Reset level',
  2511. label = 'Reset level',
  2512. command = self.levelEditor.reset)
  2513. menuBar.addmenuitem('Level Editor', 'command',
  2514. 'Exit Level Editor Panel',
  2515. label = 'Exit',
  2516. command = self.levelEditor.destroy)
  2517. menuBar.addmenu('Style', 'Style Operations')
  2518. menuBar.addmenuitem('Style', 'command',
  2519. "Save Selected Object's Color",
  2520. label = 'Save Color',
  2521. command = self.levelEditor.saveColor)
  2522. menuBar.addmenuitem('Style', 'command',
  2523. "Save Selected Wall's Style",
  2524. label = 'Save Wall Style',
  2525. command = self.levelEditor.saveWallStyle)
  2526. menuBar.addmenuitem('Style', 'command',
  2527. "Save Selected Buildings's Style",
  2528. label = 'Save Bldg Style',
  2529. command = self.levelEditor.saveBuildingStyle)
  2530. menuBar.addmenuitem('Style', 'command',
  2531. 'Reload Color Palettes',
  2532. label = 'Reload Colors',
  2533. command = self.styleManager.createColorAttributes)
  2534. menuBar.addmenuitem('Style', 'command',
  2535. 'Reload Wall Style Palettes',
  2536. label = 'Reload Wall Styles',
  2537. command = self.styleManager.createWallStyleAttributes)
  2538. menuBar.addmenuitem('Style', 'command',
  2539. 'Reload Building Style Palettes',
  2540. label = 'Reload Bldg Styles',
  2541. command = self.styleManager.createBuildingStyleAttributes)
  2542. menuBar.addmenu('Help', 'Level Editor Help Operations')
  2543. self.toggleBalloonVar = IntVar()
  2544. self.toggleBalloonVar.set(0)
  2545. menuBar.addmenuitem('Help', 'checkbutton',
  2546. 'Toggle balloon help',
  2547. label = 'Balloon Help',
  2548. variable = self.toggleBalloonVar,
  2549. command = self.toggleBalloon)
  2550. self.editMenu = Pmw.ComboBox(
  2551. menuFrame, labelpos = W,
  2552. label_text = 'Edit Mode:', entry_width = 12,
  2553. selectioncommand = self.levelEditor.setEditMode, history = 0,
  2554. scrolledlist_items = NEIGHBORHOODS)
  2555. self.editMenu.selectitem(NEIGHBORHOODS[0])
  2556. self.editMenu.pack(side = 'left', expand = 0)
  2557. # Create the notebook pages
  2558. notebook = Pmw.NoteBook(hull)
  2559. notebook.pack(fill = BOTH, expand = 1)
  2560. streetsPage = notebook.add('Streets')
  2561. toonBuildingsPage = notebook.add('Toon Bldgs')
  2562. landmarkBuildingsPage = notebook.add('Landmark Bldgs')
  2563. # suitBuildingsPage = notebook.add('Suit Buildings')
  2564. propsPage = notebook.add('Props')
  2565. sceneGraphPage = notebook.add('SceneGraph')
  2566. self.addStreetButton = Button(
  2567. streetsPage,
  2568. text = 'ADD STREET',
  2569. command = self.addStreet)
  2570. self.addStreetButton.pack(fill = 'x')
  2571. self.streetSelector = Pmw.ComboBox(
  2572. streetsPage,
  2573. dropdown = 0,
  2574. listheight = 200,
  2575. labelpos = W,
  2576. label_text = 'Street type:',
  2577. label_width = 12,
  2578. label_anchor = W,
  2579. entry_width = 30,
  2580. selectioncommand = self.setStreetModuleType,
  2581. scrolledlist_items = map(lambda s: s[7:],
  2582. self.styleManager.getCatalogCodes(
  2583. 'street'))
  2584. )
  2585. self.streetModuleType = self.styleManager.getCatalogCode('street',0)
  2586. self.streetSelector.selectitem(self.streetModuleType[7:])
  2587. self.streetSelector.pack(expand = 1, fill = 'both')
  2588. self.addToonBuildingButton = Button(
  2589. toonBuildingsPage,
  2590. text = 'ADD TOON BUILDING',
  2591. command = self.addFlatBuilding)
  2592. self.addToonBuildingButton.pack(fill = 'x')
  2593. self.toonBuildingSelector = Pmw.ComboBox(
  2594. toonBuildingsPage,
  2595. dropdown = 0,
  2596. listheight = 200,
  2597. labelpos = W,
  2598. label_width = 12,
  2599. label_anchor = W,
  2600. label_text = 'Toon bldg type:',
  2601. entry_width = 30,
  2602. selectioncommand = self.setFlatBuildingType,
  2603. scrolledlist_items = ['random20', 'random30'] + BUILDING_TYPES
  2604. )
  2605. self.toonBuildingType = 'random20'
  2606. self.toonBuildingSelector.selectitem(self.toonBuildingType)
  2607. self.toonBuildingSelector.pack(expand = 1, fill = 'both')
  2608. #self.toonBuildingWidthScale = EntryScale.EntryScale(
  2609. #toonBuildingsPage, min = 1.0, max = 30.0,
  2610. # resolution = 0.01, text = 'Wall Width',
  2611. # command = self.updateSelectedWallWidth)
  2612. #self.toonBuildingWidthScale.pack(fill = 'x')
  2613. self.addLandmarkBuildingButton = Button(
  2614. landmarkBuildingsPage,
  2615. text = 'ADD LANDMARK BUILDING',
  2616. command = self.addLandmark)
  2617. self.addLandmarkBuildingButton.pack(fill = 'x')
  2618. self.landmarkBuildingSelector = Pmw.ComboBox(
  2619. landmarkBuildingsPage,
  2620. dropdown = 0,
  2621. listheight = 200,
  2622. labelpos = W,
  2623. label_width = 12,
  2624. label_anchor = W,
  2625. label_text = 'Landmark Building type:',
  2626. entry_width = 30,
  2627. selectioncommand = self.setLandmarkType,
  2628. scrolledlist_items = map(lambda s: s[14:],
  2629. self.styleManager.getCatalogCodes(
  2630. 'toon_landmark'))
  2631. )
  2632. self.landmarkType = self.styleManager.getCatalogCode(
  2633. 'toon_landmark',0)
  2634. self.landmarkBuildingSelector.selectitem(
  2635. self.styleManager.getCatalogCode('toon_landmark',0)[14:])
  2636. self.landmarkBuildingSelector.pack(expand = 1, fill = 'both')
  2637. self.addPropsButton = Button(
  2638. propsPage,
  2639. text = 'ADD PROP',
  2640. command = self.addProp)
  2641. self.addPropsButton.pack(fill = 'x')
  2642. self.propSelector = Pmw.ComboBox(
  2643. propsPage,
  2644. dropdown = 0,
  2645. listheight = 200,
  2646. labelpos = W,
  2647. label_width = 12,
  2648. label_anchor = W,
  2649. label_text = 'Prop type:',
  2650. entry_width = 30,
  2651. selectioncommand = self.setPropType,
  2652. scrolledlist_items = map(lambda s: s[5:],
  2653. self.styleManager.getCatalogCodes('prop'))
  2654. )
  2655. self.propType = self.styleManager.getCatalogCode('prop',0)
  2656. self.propSelector.selectitem(
  2657. self.styleManager.getCatalogCode('prop',0)[5:])
  2658. self.propSelector.pack(expand = 1, fill = 'both')
  2659. # Compact down notebook
  2660. notebook.setnaturalsize()
  2661. self.colorEntry = VectorWidgets.ColorEntry(
  2662. hull, text = 'Select Color',
  2663. command = self.updateSelectedObjColor)
  2664. self.colorEntry.menu.add_command(
  2665. label = 'Save Color', command = self.levelEditor.saveColor)
  2666. self.colorEntry.pack(fill = 'x')
  2667. buttonFrame = Frame(hull)
  2668. self.fMapViz = IntVar()
  2669. self.fMapViz.set(0)
  2670. self.mapSnapButton = Checkbutton(buttonFrame,
  2671. text = 'Map Viz',
  2672. width = 6,
  2673. variable = self.fMapViz,
  2674. command = self.toggleMapViz)
  2675. self.mapSnapButton.pack(side = 'left', expand = 1, fill = 'x')
  2676. self.fXyzSnap = IntVar()
  2677. self.fXyzSnap.set(1)
  2678. self.xyzSnapButton = Checkbutton(buttonFrame,
  2679. text = 'XyzSnap',
  2680. width = 6,
  2681. variable = self.fXyzSnap,
  2682. command = self.toggleXyzSnap)
  2683. self.xyzSnapButton.pack(side = 'left', expand = 1, fill = 'x')
  2684. self.fHprSnap = IntVar()
  2685. self.fHprSnap.set(1)
  2686. self.hprSnapButton = Checkbutton(buttonFrame,
  2687. text = 'HprSnap',
  2688. width = 6,
  2689. variable = self.fHprSnap,
  2690. command = self.toggleHprSnap)
  2691. self.hprSnapButton.pack(side = 'left', expand = 1, fill = 'x')
  2692. self.fGrid = IntVar()
  2693. self.fGrid.set(0)
  2694. direct.gridButton = Checkbutton(buttonFrame,
  2695. text = 'Show Grid',
  2696. width = 6,
  2697. variable = self.fGrid,
  2698. command = self.toggleGrid)
  2699. direct.gridButton.pack(side = 'left', expand = 1, fill = 'x')
  2700. buttonFrame.pack(expand = 1, fill = 'x')
  2701. buttonFrame2 = Frame(hull)
  2702. self.groupButton = Button(
  2703. buttonFrame2,
  2704. text = 'New Group',
  2705. command = self.levelEditor.createNewGroup)
  2706. self.groupButton.pack(side = 'left', expand = 1, fill = 'x')
  2707. self.visGroupButton = Button(
  2708. buttonFrame2,
  2709. text = 'New Vis Group',
  2710. command = self.createNewVisGroup)
  2711. self.groupButton.pack(side = 'left', expand = 1, fill = 'x')
  2712. self.setParentButton = Button(
  2713. buttonFrame2,
  2714. text = 'Set Parent',
  2715. command = self.levelEditor.setActiveParent)
  2716. self.setParentButton.pack(side = 'left', expand = 1, fill = 'x')
  2717. self.reparentButton = Button(
  2718. buttonFrame2,
  2719. text = 'Re Parent',
  2720. command = self.levelEditor.reparentSelected)
  2721. self.reparentButton.pack(side = 'left', expand = 1, fill = 'x')
  2722. self.isolateButton = Button(
  2723. buttonFrame2,
  2724. text = 'Isolate Selected',
  2725. command = self.levelEditor.isolate)
  2726. self.isolateButton.pack(side = 'left', expand = 1, fill = 'x')
  2727. self.showAllButton = Button(
  2728. buttonFrame2,
  2729. text = 'Show All',
  2730. command = self.levelEditor.showAll)
  2731. self.showAllButton.pack(side = 'left', expand = 1, fill = 'x')
  2732. buttonFrame2.pack(fill = 'x')
  2733. buttonFrame3 = Frame(hull)
  2734. self.driveMode = IntVar()
  2735. self.driveMode.set(1)
  2736. self.driveModeButton = Radiobutton(
  2737. buttonFrame3,
  2738. text = 'Drive Mode',
  2739. value = 0,
  2740. variable = self.driveMode,
  2741. command = self.levelEditor.useDriveMode)
  2742. self.driveModeButton.pack(side = 'left', expand = 1, fill = 'x')
  2743. directModeButton = Radiobutton(
  2744. buttonFrame3,
  2745. text = 'DIRECT Fly',
  2746. value = 1,
  2747. variable = self.driveMode,
  2748. command = self.levelEditor.useDirectFly)
  2749. directModeButton.pack(side = 'left', expand = 1, fill = 'x')
  2750. buttonFrame3.pack(fill = 'x')
  2751. self.sceneGraphExplorer = SceneGraphExplorer(
  2752. parent = sceneGraphPage,
  2753. root = self.levelEditor,
  2754. menuItems = ['Select', 'Isolate', 'Flash', 'Toggle Viz',
  2755. 'Set Parent', 'Reparent', 'Add Group',
  2756. 'Set Name'])
  2757. self.sceneGraphExplorer.pack(expand = 1, fill = 'both')
  2758. # Make sure input variables processed
  2759. self.initialiseoptions(LevelEditorPanel)
  2760. def toggleGrid(self):
  2761. if self.fGrid.get():
  2762. direct.grid.enable()
  2763. else:
  2764. direct.grid.disable()
  2765. def toggleXyzSnap(self):
  2766. direct.grid.setXyzSnap(self.fXyzSnap.get())
  2767. def toggleHprSnap(self):
  2768. direct.grid.setHprSnap(self.fXyzSnap.get())
  2769. def toggleMapViz(self):
  2770. self.levelEditor.toggleMapViz(self.fMapViz.get())
  2771. def createNewVisGroup(self):
  2772. self.levelEditor.createNewGroup(type = 'vis')
  2773. def setStreetModuleType(self,name):
  2774. self.streetModuleType = 'street_' + name
  2775. self.levelEditor.setCurrent('street_texture', self.streetModuleType)
  2776. def addStreet(self):
  2777. self.levelEditor.addStreet(self.streetModuleType)
  2778. def setFlatBuildingType(self,name):
  2779. self.toonBuildingType = name
  2780. self.levelEditor.setCurrent('building_type', self.toonBuildingType)
  2781. def addFlatBuilding(self):
  2782. self.levelEditor.addFlatBuilding(self.toonBuildingType)
  2783. def setLandmarkType(self,name):
  2784. self.landmarkType = 'toon_landmark_' + name
  2785. self.levelEditor.setCurrent('toon_landmark_texture', self.landmarkType)
  2786. def addLandmark(self):
  2787. self.levelEditor.addLandmark(self.landmarkType)
  2788. def setPropType(self,name):
  2789. self.propType = 'prop_' + name
  2790. self.levelEditor.setCurrent('prop_texture', self.propType)
  2791. def addProp(self):
  2792. self.levelEditor.addProp(self.propType)
  2793. def updateSelectedWallWidth(self, strVal):
  2794. self.levelEditor.updateSelectedWallWidth(string.atof(strVal))
  2795. def setCurrentColor(self, colorVec, fUpdate = 0):
  2796. # Turn on/off update of selected before updating entry
  2797. self.fUpdateSelected = fUpdate
  2798. self.colorEntry.set([int(colorVec[0] * 255.0),
  2799. int(colorVec[1] * 255.0),
  2800. int(colorVec[2] * 255.0),
  2801. 255])
  2802. def setResetColor(self, colorVec):
  2803. self.colorEntry['resetValue'] = (
  2804. [int(colorVec[0] * 255.0),
  2805. int(colorVec[1] * 255.0),
  2806. int(colorVec[2] * 255.0),
  2807. 255])
  2808. def updateSelectedObjColor(self, color):
  2809. try:
  2810. obj = self.levelEditor.DNATarget
  2811. if self.fUpdateSelected & (obj != None):
  2812. objClass = DNAGetClassType(obj)
  2813. if ((objClass.eq(DNA_WALL)) |
  2814. (objClass.eq(DNA_WINDOWS)) |
  2815. (objClass.eq(DNA_DOOR)) |
  2816. (objClass.eq(DNA_CORNICE)) |
  2817. (objClass.eq(DNA_PROP))
  2818. ):
  2819. self.levelEditor.setDNATargetColor(
  2820. VBase4((color[0]/255.0),
  2821. (color[1]/255.0),
  2822. (color[2]/255.0),
  2823. 1.0))
  2824. except AttributeError:
  2825. pass
  2826. # Default is to update selected
  2827. self.fUpdateSelected = 1
  2828. def toggleBalloon(self):
  2829. if self.toggleBalloonVar.get():
  2830. self.balloon.configure(state = 'balloon')
  2831. else:
  2832. self.balloon.configure(state = 'none')
  2833. class VisGroupsEditor(Pmw.MegaToplevel):
  2834. def __init__(self, levelEditor, visGroups = ['None'],
  2835. parent = None, **kw):
  2836. INITOPT = Pmw.INITOPT
  2837. optiondefs = (
  2838. ('title', 'Visability Groups Editor', None),
  2839. )
  2840. self.defineoptions(kw, optiondefs)
  2841. Pmw.MegaToplevel.__init__(self, parent, title = self['title'])
  2842. self.levelEditor = levelEditor
  2843. self.visGroups = visGroups
  2844. self.visGroupNames = map(lambda pair: pair[1].getName(),
  2845. self.visGroups)
  2846. # Initialize dictionary of visibility relationships
  2847. self.visDict = {}
  2848. # Group we are currently setting visGroups for
  2849. self.target = None
  2850. # Flag to enable/disable toggleVisGroup command
  2851. self.fCommand = 1
  2852. # Handle to the toplevels hull
  2853. hull = self.component('hull')
  2854. balloon = self.balloon = Pmw.Balloon(hull)
  2855. # Start with balloon help disabled
  2856. self.balloon.configure(state = 'none')
  2857. menuFrame = Frame(hull, relief = GROOVE, bd = 2)
  2858. menuFrame.pack(fill = X, expand = 1)
  2859. menuBar = Pmw.MenuBar(menuFrame, hotkeys = 1, balloon = balloon)
  2860. menuBar.pack(side = LEFT, expand = 1, fill = X)
  2861. menuBar.addmenu('Vis Groups Editor',
  2862. 'Visability Groups Editor Operations')
  2863. menuBar.addmenuitem('Vis Groups Editor', 'command',
  2864. 'Exit Visability Groups Editor',
  2865. label = 'Exit',
  2866. command = self.preDestroy)
  2867. menuBar.addmenu('Help', 'Visability Groups Editor Help Operations')
  2868. self.toggleBalloonVar = IntVar()
  2869. self.toggleBalloonVar.set(0)
  2870. menuBar.addmenuitem('Help', 'checkbutton',
  2871. 'Toggle balloon help',
  2872. label = 'Balloon Help',
  2873. variable = self.toggleBalloonVar,
  2874. command = self.toggleBalloon)
  2875. # Create a combo box to choose target vis group
  2876. self.targetSelector = Pmw.ComboBox(
  2877. hull, labelpos = W, label_text = 'Target Vis Group:',
  2878. entry_width = 12, selectioncommand = self.selectVisGroup,
  2879. scrolledlist_items = self.visGroupNames)
  2880. self.targetSelector.selectitem(self.visGroupNames[0])
  2881. self.targetSelector.pack(expand = 1, fill = X)
  2882. # Scrolled frame to hold radio selector
  2883. sf = Pmw.ScrolledFrame(hull, horizflex = 'elastic',
  2884. usehullsize = 1, hull_width = 200,
  2885. hull_height = 400)
  2886. frame = sf.interior()
  2887. sf.pack(padx=5, pady=3, fill = 'both', expand = 1)
  2888. # Add vis groups selector
  2889. self.selected = Pmw.RadioSelect(frame, selectmode=MULTIPLE,
  2890. orient = VERTICAL,
  2891. pady = 0,
  2892. command = self.toggleVisGroup)
  2893. for groupInfo in self.visGroups:
  2894. nodePath = groupInfo[0]
  2895. group = groupInfo[1]
  2896. name = group.getName()
  2897. self.selected.add(name, width = 12)
  2898. # Assemble list of groups visible from this group
  2899. visible = []
  2900. for i in range(group.getNumVisibles()):
  2901. visible.append(group.getVisibleName(i))
  2902. visible.sort()
  2903. self.visDict[name] = [nodePath, group, visible]
  2904. # Pack the widget
  2905. self.selected.pack(expand = 1, fill = X)
  2906. # And make sure scrolled frame is happy
  2907. sf.reposition()
  2908. buttonFrame = Frame(hull)
  2909. buttonFrame.pack(fill='x', expand = 1)
  2910. self.showMode = IntVar()
  2911. self.showMode.set(0)
  2912. self.showAllButton = Radiobutton(buttonFrame, text = 'Show All',
  2913. value = 0, indicatoron = 1,
  2914. variable = self.showMode,
  2915. command = self.refreshVisibility)
  2916. self.showAllButton.pack(side = LEFT, fill = 'x', expand = 1)
  2917. self.showActiveButton = Radiobutton(buttonFrame, text = 'Show Target',
  2918. value = 1, indicatoron = 1,
  2919. variable = self.showMode,
  2920. command = self.refreshVisibility)
  2921. self.showActiveButton.pack(side = LEFT, fill = 'x', expand = 1)
  2922. # Make sure input variables processed
  2923. self.initialiseoptions(VisGroupsEditor)
  2924. # Switch to current target's list
  2925. self.selectVisGroup(self.visGroupNames[0])
  2926. def selectVisGroup(self, target):
  2927. print 'Setting vis options for group:', target
  2928. # Record current target
  2929. oldTarget = self.target
  2930. # Record new target
  2931. self.target = target
  2932. # Deselect buttons from old target (first deactivating command)
  2933. self.fCommand = 0
  2934. if oldTarget:
  2935. visList = self.visDict[oldTarget][2]
  2936. for group in visList:
  2937. self.selected.invoke(self.selected.index(group))
  2938. # Now set buttons to reflect state of new target
  2939. visList = self.visDict[target][2]
  2940. for group in visList:
  2941. self.selected.invoke(self.selected.index(group))
  2942. # Reactivate command
  2943. self.fCommand = 1
  2944. # Update scene
  2945. self.refreshVisibility()
  2946. def toggleVisGroup(self, groupName, state):
  2947. if self.fCommand:
  2948. targetInfo = self.visDict[self.target]
  2949. target = targetInfo[1]
  2950. visList = targetInfo[2]
  2951. groupNP = self.visDict[groupName][0]
  2952. group = self.visDict[groupName][1]
  2953. # MRM: Add change in visibility here
  2954. # Show all vs. show active
  2955. if state == 1:
  2956. print 'Vis Group:', self.target, 'adding group:', groupName
  2957. if groupName not in visList:
  2958. visList.append(groupName)
  2959. target.addVisible(groupName)
  2960. # Update viz and color
  2961. groupNP.show()
  2962. groupNP.setColor(1,0,0,1)
  2963. else:
  2964. print 'Vis Group:', self.target, 'removing group:', groupName
  2965. if groupName in visList:
  2966. visList.remove(groupName)
  2967. target.removeVisible(groupName)
  2968. # Update viz and color
  2969. if self.showMode.get() == 1:
  2970. groupNP.hide()
  2971. groupNP.clearColor()
  2972. def refreshVisibility(self):
  2973. # Get current visibility list for target
  2974. targetInfo = self.visDict[self.target]
  2975. visList = targetInfo[2]
  2976. for key in self.visDict.keys():
  2977. groupNP = self.visDict[key][0]
  2978. if key in visList:
  2979. groupNP.show()
  2980. if key == self.target:
  2981. groupNP.setColor(0,1,0,1)
  2982. else:
  2983. groupNP.setColor(1,0,0,1)
  2984. else:
  2985. if self.showMode.get() == 0:
  2986. groupNP.show()
  2987. else:
  2988. groupNP.hide()
  2989. groupNP.clearColor()
  2990. def preDestroy(self):
  2991. # First clear level editor variable
  2992. self.levelEditor.vgpanel = None
  2993. self.destroy()
  2994. def toggleBalloon(self):
  2995. if self.toggleBalloonVar.get():
  2996. self.balloon.configure(state = 'balloon')
  2997. else:
  2998. self.balloon.configure(state = 'none')