Guest User

Untitled

a guest
Apr 27th, 2015
251
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 54.96 KB | None | 0 0
  1. ###########################################
  2. #
  3. # Binding of Isaac: Rebirth Stage Editor
  4. #
  5. #
  6. #
  7. #
  8. # UI Elements
  9. # Main Scene: Click to select, right click to paint. Auto resizes to match window zoom. Renders background.
  10. # Entity: A QGraphicsItem to be added to the scene for drawing.
  11. # Room List: Shows a list of rooms with mini-renders as icons. Needs add and remove buttons. Should drag and drop re-sort.
  12. # Entity Palette: A palette from which to choose entities to draw.
  13. # Properties: Possibly a contextual menu thing?
  14. #
  15. #
  16. #
  17. # Todo:
  18. # Idiot proof the variant numbers further
  19. # Variant number hardcoding notes:
  20. # Horsemen, shops, devil/angel trapdoor rooms, Satan, Lamb
  21. #
  22.  
  23.  
  24. from PyQt5.QtCore import *
  25. from PyQt5.QtGui import *
  26. from PyQt5.QtWidgets import *
  27.  
  28. import struct, os
  29. import xml.etree.ElementTree as ET
  30.  
  31.  
  32. ########################
  33. # XML Data #
  34. ########################
  35.  
  36. def getEntityXML():
  37. tree = ET.parse('resources/Entities.xml')
  38. root = tree.getroot()
  39.  
  40. return root
  41.  
  42. ########################
  43. # Scene/View #
  44. ########################
  45.  
  46. class RoomScene(QGraphicsScene):
  47.  
  48. def __init__(self):
  49. QGraphicsScene.__init__(self, 0,0,0,0)
  50. self.newRoomSize(13, 7)
  51.  
  52. self.BG = [ "01_basement.png", "02_cellar.png", "03_caves.png", "04_catacombs.png",
  53. "05_depths.png", "06_necropolis.png", "07_the womb.png", "08_utero.png",
  54. "09_sheol.png", "10_cathedral.png", "11_chest.png", "12_darkroom.png",
  55. "0a_library.png", "0b_shop.png", "0c_isaacsroom.png", "0d_barrenroom.png",
  56. "0e_arcade.png", "0e_diceroom.png", "0f_secretroom.png"
  57. ]
  58. self.grid = True
  59.  
  60. # Make the bitfont
  61. q = QImage()
  62. q.load('resources/UI/Bitfont.png')
  63.  
  64. self.bitfont = [QPixmap.fromImage(q.copy(i*12,0,12,12)) for i in range(10)]
  65. self.bitText = True
  66.  
  67. def newRoomSize(self, w, h):
  68. self.roomWidth = w
  69. self.roomHeight = h
  70.  
  71. self.setSceneRect(-52, -52, self.roomWidth*26+52*2, self.roomHeight*26+52*2)
  72.  
  73. def clearDoors(self):
  74. for item in self.items():
  75. if isinstance(item, Door):
  76. item.remove()
  77.  
  78. def drawForeground(self, painter, rect):
  79.  
  80. # Bitfont drawing: moved to the RoomEditorWidget.drawForeground for easier anti-aliasing
  81.  
  82. # Grey out the screen to show it's inactive if there are no rooms selected
  83. if mainWindow.roomList.selectedRoom() is None:
  84. painter.setPen(QPen(Qt.white,1,Qt.SolidLine))
  85. painter.setBrush(QBrush(QColor(100,100,100,100)))
  86.  
  87. b = QBrush(QColor(100,100,100,100))
  88. painter.fillRect(rect, b)
  89. return
  90.  
  91. # Draw me a foreground grid
  92. if not self.grid: return
  93.  
  94. painter.setRenderHint(QPainter.Antialiasing, True)
  95. painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
  96.  
  97. rect = QRectF(0,0,self.roomWidth*26,self.roomHeight*26)
  98. ts = 26
  99.  
  100. startx = rect.x()
  101. endx = startx + rect.width()
  102.  
  103. starty = rect.y()
  104. endy = starty + rect.height()
  105.  
  106. painter.setPen(QPen(QColor.fromRgb(255,255,255,100), 1, Qt.DashLine))
  107.  
  108. x = startx
  109. y1 = rect.top()
  110. y2 = rect.bottom()
  111. while x <= endx:
  112. painter.drawLine(x, starty, x, endy)
  113. x += ts
  114.  
  115. y = starty
  116. x1 = rect.left()
  117. x2 = rect.right()
  118. while y <= endy:
  119. painter.drawLine(startx, y, endx, y)
  120. y += ts
  121.  
  122. def drawBackground(self, painter, rect):
  123. roomBG = 1
  124.  
  125. if mainWindow.roomList.selectedRoom():
  126. roomBG = mainWindow.roomList.selectedRoom().roomBG
  127.  
  128. tile = QImage()
  129. tile.load('resources/Backgrounds/{0}'.format(self.BG[roomBG-1]))
  130.  
  131. corner = tile.copy( QRect(0, 0, 26*7, 26*4) )
  132. vert = tile.copy( QRect(26*7, 0, 26*2, 26*6) )
  133. horiz = tile.copy( QRect(0, 26*4, 26*9, 26*2) )
  134.  
  135. t = -52
  136. xm = 26 * (self.roomWidth+2) - 26*7
  137. ym = 26 * (self.roomHeight+2) - 26*4
  138.  
  139. # Corner Painting
  140. painter.drawPixmap (t,t, QPixmap().fromImage(corner.mirrored(False, False)))
  141. painter.drawPixmap (xm,t, QPixmap().fromImage(corner.mirrored(True, False)))
  142. painter.drawPixmap (t,ym, QPixmap().fromImage(corner.mirrored(False, True)))
  143. painter.drawPixmap (xm,ym, QPixmap().fromImage(corner.mirrored(True, True)))
  144.  
  145. # Mirrored Textures
  146. uRect = QImage(26*4, 26*6, QImage.Format_RGB32)
  147. lRect = QImage(26*9, 26*4, QImage.Format_RGB32)
  148.  
  149. uRect.fill(1)
  150. lRect.fill(1)
  151.  
  152. vp = QPainter()
  153. vp.begin(uRect)
  154. vp.drawPixmap(0, 0,QPixmap().fromImage(vert))
  155. vp.drawPixmap(52,0,QPixmap().fromImage(vert.mirrored(True, False)))
  156. vp.end()
  157.  
  158. vh = QPainter()
  159. vh.begin(lRect)
  160. vh.drawPixmap(0, 0,QPixmap().fromImage(horiz))
  161. vh.drawPixmap(0,52,QPixmap().fromImage(horiz.mirrored(False, True)))
  162. vh.end()
  163.  
  164. painter.drawTiledPixmap(26*7-52-13, -52, 26 * (self.roomWidth - 10)+26, 26*6, QPixmap().fromImage(uRect))
  165. painter.drawTiledPixmap(-52, 26*4-52-13, 26*9, 26 * (self.roomHeight - 4)+26, QPixmap().fromImage(lRect))
  166. painter.drawTiledPixmap(26*7-52-13, self.roomHeight*26-26*4, 26 * (self.roomWidth - 10)+26, 26*6, QPixmap().fromImage(uRect.mirrored(False, True)))
  167. painter.drawTiledPixmap(self.roomWidth*26-26*7, 26*4-52-13, 26*9, 26 * (self.roomHeight - 4)+26, QPixmap().fromImage(lRect.mirrored(True, False)))
  168.  
  169. if self.roomHeight == 14 and self.roomWidth == 26:
  170.  
  171. center = tile.copy( QRect(26*3, 26*3, 26*6, 26*3) )
  172.  
  173. painter.drawPixmap (26*7, 26*4, QPixmap().fromImage(center.mirrored(False, False)))
  174. painter.drawPixmap (26*13, 26*4, QPixmap().fromImage(center.mirrored(True, False)))
  175. painter.drawPixmap (26*7 , 26*7, QPixmap().fromImage(center.mirrored(False, True)))
  176. painter.drawPixmap (26*13, 26*7, QPixmap().fromImage(center.mirrored(True, True)))
  177.  
  178. class RoomEditorWidget(QGraphicsView):
  179.  
  180. def __init__(self, scene, parent=None):
  181. QGraphicsView.__init__(self, scene, parent)
  182.  
  183. self.setViewportUpdateMode(self.FullViewportUpdate)
  184. self.setDragMode(self.RubberBandDrag)
  185. self.setTransformationAnchor(QGraphicsView.AnchorViewCenter)
  186. self.setAlignment(Qt.AlignTop|Qt.AlignLeft)
  187. self.newScale = 1.0
  188.  
  189. self.assignNewScene(scene)
  190.  
  191. self._altPressed = 0
  192.  
  193. def assignNewScene(self, scene):
  194. self.setScene(scene)
  195. self.centerOn(0,0)
  196.  
  197. self.objectToPaint = None
  198. self.lastTile = None
  199.  
  200. def tryToPaint(self, event):
  201. '''Called when a paint attempt is initiated'''
  202.  
  203. paint = self.objectToPaint
  204. if paint is None: return
  205.  
  206. clicked = self.mapToScene(event.x(), event.y())
  207. x, y = clicked.x(), clicked.y()
  208. if x > self.scene().roomWidth * 26: return
  209. if y > self.scene().roomHeight * 26: return
  210.  
  211. if x < 0: x = 0
  212. if y < 0: y = 0
  213.  
  214. x = int(x / 26)
  215. y = int(y / 26)
  216.  
  217. # Don't stack multiple grid entities
  218. for i in self.scene().items():
  219. if isinstance(i, Entity):
  220. if i.entity['X'] == x and i.entity['Y'] == y:
  221. if int(i.entity['Type']) > 999 and int(self.objectToPaint.ID) > 999:
  222. return
  223.  
  224. # Make sure we're not spawning oodles
  225. if (x,y) in self.lastTile: return
  226. self.lastTile.add((x,y))
  227.  
  228. en = Entity(x,y,int(paint.ID), int(paint.variant), int(paint.subtype), 0)
  229.  
  230. self.scene().addItem(en)
  231. mainWindow.dirt()
  232.  
  233. def mousePressEvent(self, event):
  234. if event.button() == Qt.RightButton:
  235. if mainWindow.roomList.selectedRoom() is not None:
  236. self.lastTile = set()
  237. self.tryToPaint(event)
  238. event.accept()
  239. else:
  240. self.lastTile = None
  241. QGraphicsView.mousePressEvent(self, event)
  242.  
  243. def mouseMoveEvent(self, event):
  244. if self.lastTile:
  245. if mainWindow.roomList.selectedRoom() is not None:
  246. self.tryToPaint(event)
  247. event.accept()
  248. else:
  249. QGraphicsView.mouseMoveEvent(self, event)
  250.  
  251. def mouseReleaseEvent(self, event):
  252. self.lastTile = None
  253. QGraphicsView.mouseReleaseEvent(self, event)
  254.  
  255. def keyPressEvent(self, event):
  256. scene = self.scene()
  257. selection = scene.selectedItems()
  258.  
  259. if event.key() == Qt.Key_Alt:
  260. self._altPressed = 1
  261.  
  262. if event.key() == Qt.Key_Delete or event.key() == Qt.Key_Backspace:
  263. if len(selection) > 0:
  264. for obj in selection:
  265. obj.setSelected(False)
  266. obj.remove()
  267. scene.update()
  268. self.update()
  269. mainWindow.dirt()
  270. return
  271.  
  272. else:
  273. if self._altPressed == 1:
  274. if event.key() == Qt.Key_Plus:
  275. if len(selection) > 0:
  276. for obj in selection:
  277. obj.entity['Type'] = obj.entity['Type']+1
  278. elif event.key() == Qt.Key_Minus:
  279. if len(selection) > 0:
  280. for obj in selection:
  281. obj.entity['Type'] = obj.entity['Type']-1
  282. else:
  283. if event.key() == Qt.Key_Plus:
  284. if len(selection) > 0:
  285. for obj in selection:
  286. obj.entity['Variant'] = obj.entity['Variant']+1
  287. elif event.key() == Qt.Key_Minus:
  288. if len(selection) > 0:
  289. for obj in selection:
  290. obj.entity['Variant'] = obj.entity['Variant']-1
  291. else:
  292. QGraphicsView.keyPressEvent(self, event)
  293.  
  294. self.scene().update()
  295.  
  296. def keyReleaseEvent(self, event):
  297. if event.key() == Qt.Key_Alt:
  298. self._altPressed = 0
  299.  
  300. def drawBackground(self, painter, rect):
  301. painter.fillRect(rect, QColor(0, 0, 0))
  302.  
  303. QGraphicsView.drawBackground(self,painter,rect)
  304.  
  305. def resizeEvent(self, event):
  306. QGraphicsView.resizeEvent(self, event)
  307.  
  308. w = self.scene().roomWidth
  309. h = self.scene().roomHeight
  310.  
  311. xScale = event.size().width() / (w*26 + 52*2)
  312. yScale = event.size().height() / (h*26 + 52*2)
  313. newScale = min([xScale, yScale])
  314.  
  315. tr = QTransform()
  316. tr.scale(newScale, newScale)
  317. self.newScale = newScale
  318.  
  319. self.setTransform(tr)
  320.  
  321. if newScale == yScale:
  322. self.setAlignment(Qt.AlignTop|Qt.AlignHCenter)
  323. else:
  324. self.setAlignment(Qt.AlignVCenter|Qt.AlignLeft)
  325.  
  326. def drawForeground(self, painter, rect):
  327.  
  328. QGraphicsView.drawForeground(self,painter,rect)
  329.  
  330. painter.setRenderHint(QPainter.Antialiasing, True)
  331. painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
  332.  
  333. # Display the number of entities on a given tile, in bitFont or regular font
  334. tiles = [[0 for y in range(26)] for x in range(14)]
  335. for e in self.scene().items():
  336. if isinstance(e,Entity):
  337. tiles[e.entity['Y']][e.entity['X']] += 1
  338.  
  339. if not self.scene().bitText:
  340. painter.setPen(QPen(Qt.white,1,Qt.SolidLine))
  341. painter.font().setPixelSize(5)
  342.  
  343. for y in enumerate(tiles):
  344. for x in enumerate(y[1]):
  345.  
  346. if x[1] > 1:
  347.  
  348. if self.scene().bitText:
  349. c = x[1]
  350.  
  351. if x[1] >= 10:
  352. painter.drawPixmap( (x[0] + 1) * 26 - 24, (y[0] + 1) * 26 - 12, self.scene().bitfont[int(c/10)] )
  353. c = c % 10
  354.  
  355. painter.drawPixmap( (x[0] + 1) * 26 - 12, (y[0] + 1) * 26 - 12, self.scene().bitfont[c] )
  356.  
  357. else:
  358. painter.drawText( x[0] * 26, y[0] * 26, 26, 26, Qt.AlignBottom | Qt.AlignRight, str(x[1]) )
  359.  
  360. class Entity(QGraphicsItem):
  361. SNAP_TO = 26
  362.  
  363. def __init__(self, x, y, mytype, variant, subtype, weight):
  364. QGraphicsItem.__init__(self)
  365. self.setFlags(
  366. self.ItemSendsGeometryChanges |
  367. self.ItemIsSelectable |
  368. self.ItemIsMovable
  369. )
  370.  
  371. # Supplied entity info
  372. self.entity = {}
  373. self.entity['X'] = x
  374. self.entity['Y'] = y
  375. self.entity['Type'] = mytype
  376. self.entity['Variant'] = variant
  377. self.entity['Subtype'] = subtype
  378. self.entity['Weight'] = weight
  379.  
  380. # Derived Entity Info
  381. self.entity['name'] = None
  382. self.entity['baseHP'] = None
  383. self.entity['boss'] = None
  384. self.entity['champion'] = None
  385. self.entity['pixmap'] = None
  386.  
  387. self.getEntityInfo(mytype, subtype, variant)
  388.  
  389. self.updatePosition()
  390. if self.entity['Type'] < 999:
  391. self.setZValue(1)
  392. else:
  393. self.setZValue(0)
  394.  
  395. if not hasattr(Entity, 'SELECTION_PEN'):
  396. Entity.SELECTION_PEN = QPen(Qt.green, 1, Qt.DashLine)
  397.  
  398. def getEntityInfo(self, t, subtype, variant):
  399. global entityXML
  400.  
  401. while entityXML.find("entity[@ID='{0}'][@Subtype='{1}'][@Variant='{2}']".format(t, subtype, variant)) is None:
  402. variant = variant - 1
  403.  
  404. en = entityXML.find("entity[@ID='{0}'][@Subtype='{1}'][@Variant='{2}']".format(t, subtype, variant))
  405.  
  406. self.entity['name'] = en.get('Name')
  407. self.entity['baseHP'] = en.get('BaseHP')
  408. self.entity['boss'] = en.get('Boss')
  409. self.entity['champion'] = en.get('Champion')
  410.  
  411. if self.entity['Type'] is 5 and self.entity['Variant'] is 100:
  412. i = QImage()
  413. i.load('resources/Entities/5.100.0 - Collectible.png')
  414.  
  415. d = QImage()
  416. d.load(en.get('Image'))
  417.  
  418. p = QPainter(i)
  419. p.drawImage(0,0,d)
  420. p.end()
  421.  
  422. self.entity['pixmap'] = QPixmap.fromImage(i)
  423.  
  424. else:
  425. self.entity['pixmap'] = QPixmap()
  426. self.entity['pixmap'].load(en.get('Image'))
  427.  
  428. def itemChange(self, change, value):
  429.  
  430. if change == self.ItemPositionChange:
  431. currentX, currentY = self.x(), self.y()
  432.  
  433. x, y = value.x(), value.y()
  434.  
  435. try:
  436. w = self.scene().roomWidth
  437. h = self.scene().roomHeight
  438. except:
  439. w = 26
  440. h = 14
  441.  
  442. x = int((x + (self.SNAP_TO/2)) / self.SNAP_TO) * self.SNAP_TO
  443. y = int((y + (self.SNAP_TO/2)) / self.SNAP_TO) * self.SNAP_TO
  444.  
  445. if x < 0: x = 0
  446. if x >= (self.SNAP_TO * (w-1)): x = (self.SNAP_TO * (w-1))
  447. if y < 0: y = 0
  448. if y >= (self.SNAP_TO * (h-1)): y = (self.SNAP_TO * (h-1))
  449.  
  450. if x != currentX or y != currentY:
  451. self.entity['X'] = int(x/self.SNAP_TO)
  452. self.entity['Y'] = int(y/self.SNAP_TO)
  453. else:
  454. mainWindow.dirt()
  455.  
  456. value.setX(x)
  457. value.setY(y)
  458. return value
  459.  
  460. return QGraphicsItem.itemChange(self, change, value)
  461.  
  462. def boundingRect(self):
  463.  
  464. if self.entity['pixmap']:
  465. return QRectF(self.entity['pixmap'].rect())
  466. else:
  467. return QRectF(0.0,0.0,26.0,26.0)
  468.  
  469. def updatePosition(self):
  470. self.setPos(self.entity['X']*26, self.entity['Y']*26)
  471.  
  472. def paint(self, painter, option, widget):
  473.  
  474. painter.setRenderHint(QPainter.Antialiasing, True)
  475. painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
  476.  
  477. painter.setBrush(Qt.Dense5Pattern)
  478. painter.setPen(QPen(Qt.white))
  479.  
  480. if self.entity['pixmap']:
  481. x = -(self.entity['pixmap'].width() -26) / 2
  482. y = -(self.entity['pixmap'].height()-26) / 2
  483.  
  484. # Creeper special case
  485. if self.entity['Type'] in [240, 241, 242]:
  486. w = self.scene().roomWidth -1
  487. h = self.scene().roomHeight-1
  488. ex = self.entity['X']
  489. ey = self.entity['Y']
  490.  
  491. distances = [w - ex, ex, ey, h - ey]
  492. closest = min(distances)
  493. direction = distances.index(closest)
  494.  
  495. painter.setPen(QPen(QColor(220,220,180), 2, Qt.DashDotLine))
  496.  
  497. if direction == 0: # Right
  498. painter.drawLine(26,13, closest*26+26, 13)
  499.  
  500. elif direction == 1: # Left
  501. painter.drawLine(0,13, -closest*26, 13)
  502.  
  503. elif direction == 2: # Top
  504. painter.drawLine(13,0, 13, -closest*26)
  505.  
  506. elif direction == 3: # Bottom
  507. painter.drawLine(13,26, 13, closest*26+26)
  508.  
  509. painter.drawPixmap(x,y,self.entity['pixmap'])
  510.  
  511. # Most Painting
  512. else:
  513. painter.drawPixmap(x,y,self.entity['pixmap'])
  514.  
  515. else:
  516. painter.drawRect(0, 0, 26, 26)
  517. painter.drawText(4,16,str(self.entity['Type']))
  518. painter.drawText(4,32,str(self.entity['Variant']))
  519. painter.drawText(4,48,str(self.entity['Subtype']))
  520.  
  521. if self.isSelected():
  522. painter.setPen(self.SELECTION_PEN)
  523. painter.setBrush(Qt.NoBrush)
  524. painter.drawRect(x,y,self.entity['pixmap'].width(), self.entity['pixmap'].height())
  525. painter.drawText(x,y-4,"id="+str(self.entity['Type']))
  526. painter.drawText(x,y+self.entity['pixmap'].height()+10,"v="+str(self.entity['Variant']))
  527.  
  528. def mouseDoubleClickEvent(self, event):
  529. x = -(self.entity['pixmap'].width() -26) / 2
  530. y = -(self.entity['pixmap'].height()-26) / 2
  531.  
  532. # self.entity['Variant'] = self.entity['Variant']+1
  533.  
  534. event.accept()
  535. self.update()
  536. mainWindow.dirt()
  537.  
  538. def remove(self):
  539. self.scene().removeItem(self)
  540.  
  541. class Door(QGraphicsItem):
  542.  
  543. def __init__(self, doorItem):
  544. QGraphicsItem.__init__(self)
  545.  
  546. # Supplied entity info
  547. self.doorItem = doorItem
  548. self.exists = doorItem[2]
  549.  
  550. self.setPos(self.doorItem[0]*26-13, self.doorItem[1]*26-13)
  551.  
  552. tr = QTransform()
  553. if doorItem[0] == -1:
  554. tr.rotate(270)
  555. self.moveBy(-13, 0)
  556. elif doorItem[0] in [13,26]:
  557. tr.rotate(90)
  558. self.moveBy(13, 0)
  559. elif doorItem[1] in [7,14]:
  560. tr.rotate(180)
  561. self.moveBy(0, 13)
  562. else:
  563. self.moveBy(0, -13)
  564.  
  565. self.image = QImage('resources/Backgrounds/Door.png').transformed(tr)
  566. self.disabledImage = QImage('resources/Backgrounds/DisabledDoor.png').transformed(tr)
  567.  
  568. def paint(self, painter, option, widget):
  569.  
  570. painter.setRenderHint(QPainter.Antialiasing, True)
  571. painter.setRenderHint(QPainter.SmoothPixmapTransform, True)
  572.  
  573. if self.exists:
  574. painter.drawImage(0,0, self.image)
  575. else:
  576. painter.drawImage(0,0, self.disabledImage)
  577.  
  578. def boundingRect(self):
  579. return QRectF(0.0,0.0,64.0,52.0)
  580.  
  581. def mouseDoubleClickEvent(self, event):
  582.  
  583. if self.exists:
  584. self.exists = False
  585. else:
  586. self.exists = True
  587.  
  588. self.doorItem[2] = self.exists
  589.  
  590. event.accept()
  591. self.update()
  592. mainWindow.dirt()
  593.  
  594. def remove(self):
  595. self.scene().removeItem(self)
  596.  
  597.  
  598. ########################
  599. # Dock Widgets #
  600. ########################
  601.  
  602. # Room Selector
  603. ########################
  604.  
  605. class Room(QListWidgetItem):
  606.  
  607. def __init__(self, name="New Room", doors=[], spawns=[], mytype=1, variant=0, difficulty=1, weight=1.0, width=13, height=7):
  608. """Initializes the room item."""
  609.  
  610. QListWidgetItem.__init__(self)
  611.  
  612. self.setText(name)
  613.  
  614. self.roomSpawns = spawns
  615. self.roomDoors = doors
  616. self.roomType = mytype
  617. self.roomVariant = variant
  618. self.roomDifficulty = difficulty
  619. self.roomWeight = weight
  620. self.roomWidth = width
  621. self.roomHeight = height
  622.  
  623. self.roomBG = 1
  624. self.setRoomBG()
  625.  
  626. self.setFlags(self.flags() | Qt.ItemIsEditable)
  627. self.setToolTip()
  628.  
  629. if doors == []: self.makeNewDoors()
  630. self.renderDisplayIcon()
  631.  
  632. def makeNewDoors(self):
  633. if self.roomWidth == 13 and self.roomHeight == 7:
  634. self.roomDoors = [[6,-1,True],[-1,3,True],[13,3,True],[6,7,True]]
  635. elif self.roomWidth == 26 and self.roomHeight == 14:
  636. self.roomDoors = [[6,-1,True],[-1,3,True],[-1,10,True],[19,-1,True],[6,14,True],[19,14,True],[26,3,True],[26,10,True]]
  637. elif self.roomWidth == 26:
  638. self.roomDoors = [[6,-1,True],[-1,3,True],[6,7,True],[19,-1,True],[19,7,True],[26,3,True]]
  639. elif self.roomHeight == 14:
  640. self.roomDoors = [[6,-1,True],[-1,3,True],[-1,10,True],[13,3,True],[13,10,True],[6,14,True]]
  641.  
  642. def clearDoors(self):
  643. mainWindow.scene.clearDoors()
  644. for door in self.roomDoors:
  645. d = Door(door)
  646. mainWindow.scene.addItem(d)
  647.  
  648. def getSpawnCount(self):
  649. ret = 0
  650.  
  651. for x in self.roomSpawns:
  652. for y in x:
  653. if len(y) is not 0:
  654. ret += 1
  655.  
  656. return ret
  657.  
  658. def setToolTip(self):
  659. tip = "{4}x{5} - Type: {0}, Variant: {1}, Difficulty: {2}, Weight: {3}".format(self.roomType, self.roomVariant, self.roomDifficulty, self.roomWeight, self.roomWidth, self.roomHeight)
  660. QListWidgetItem.setToolTip(self, tip)
  661.  
  662. def renderDisplayIcon(self):
  663. """Renders the mini-icon for display."""
  664.  
  665. q = QImage()
  666. q.load('resources/UI/RoomIcons.png')
  667.  
  668. i = QIcon(QPixmap.fromImage(q.copy(self.roomType*16,0,16,16)))
  669.  
  670. self.setIcon(i)
  671.  
  672. def setRoomBG(self):
  673. c = self.roomType
  674.  
  675. roomType = ['basement', 'cellar', 'caves', 'catacombs', 'depths', 'necropolis', 'womb', 'utero', 'sheol', 'cathedral', 'chest', 'dark room']
  676. for t in roomType:
  677. if t in mainWindow.path:
  678. self.roomBG = roomType.index(t)+1
  679.  
  680. if c == 12:
  681. self.roomBG = 13
  682. elif c == 2:
  683. self.roomBG = 14
  684. elif c == 18:
  685. self.roomBG = 15
  686. elif c == 19:
  687. self.roomBG = 16
  688. elif c == 9:
  689. self.roomBG = 17
  690. elif c == 21:
  691. self.roomBG = 18
  692. elif c == 7:
  693. self.roomBG = 19
  694.  
  695. elif c in [10,11,13,14,17]:
  696. self.roomBG = 9
  697. elif c in [15]:
  698. self.roomBG = 10
  699. elif c in [20]:
  700. self.roomBG = 11
  701. elif c in [3]:
  702. self.roomBG = 12
  703.  
  704. elif c in [8]:
  705. if self.roomVariant in [0]:
  706. self.roomBG = 7
  707. elif self.roomVariant in [1]:
  708. self.roomBG = 10
  709. elif self.roomVariant in [2]:
  710. self.roomBG = 9
  711. elif self.roomVariant in [3]:
  712. self.roomBG = 4
  713. elif self.roomVariant in [4]:
  714. self.roomBG = 2
  715. elif self.roomVariant in [5]:
  716. self.roomBG = 1
  717. elif self.roomVariant in [6]:
  718. self.roomBG = 12
  719. elif self.roomVariant in [7]:
  720. self.roomBG = 13
  721. else:
  722. self.roomBG = 12
  723.  
  724. class RoomDelegate(QStyledItemDelegate):
  725.  
  726. def __init__(self):
  727.  
  728. self.pixmap = QPixmap('resources/UI/CurrentRoom.png')
  729. QStyledItemDelegate.__init__(self)
  730.  
  731. def paint(self, painter, option, index):
  732.  
  733. painter.fillRect(option.rect.right()-19, option.rect.top(), 17, 16, QBrush(Qt.white))
  734.  
  735. QStyledItemDelegate.paint(self, painter, option, index)
  736.  
  737. item = mainWindow.roomList.list.item(index.row())
  738. if item:
  739. if item.data(100):
  740. painter.drawPixmap(option.rect.right()-19, option.rect.top(), self.pixmap)
  741.  
  742. class FilterMenu(QMenu):
  743.  
  744. def __init__(self):
  745.  
  746. QMenu.__init__(self)
  747.  
  748. def paintEvent(self, event):
  749.  
  750. QMenu.paintEvent(self, event)
  751.  
  752. painter = QPainter(self)
  753.  
  754. for act in self.actions():
  755. rect = self.actionGeometry(act)
  756. painter.fillRect(rect.right()/2-12, rect.top()-2, 24, 24, QBrush(Qt.transparent))
  757. painter.drawPixmap(rect.right()/2-12, rect.top()-2, act.icon().pixmap(24, 24));
  758.  
  759. class RoomSelector(QWidget):
  760.  
  761. def __init__(self):
  762. """Initialises the widget."""
  763.  
  764. QWidget.__init__(self)
  765.  
  766. self.layout = QVBoxLayout()
  767. self.layout.setSpacing(0)
  768.  
  769. self.filterEntity = None
  770.  
  771. self.setupFilters()
  772. self.setupList()
  773. self.setupToolbar()
  774.  
  775. self.layout.addLayout(self.filter)
  776. self.layout.addWidget(self.list)
  777. self.layout.addWidget(self.toolbar)
  778.  
  779. self.setLayout(self.layout)
  780. self.setButtonStates()
  781.  
  782. def setupFilters(self):
  783. self.filter = QHBoxLayout()
  784. self.filter.setSpacing(4)
  785.  
  786. fq = QImage()
  787. fq.load('resources/UI/FilterIcons.png')
  788.  
  789. # Set the custom data
  790. self.filter.typeData = -1
  791. self.filter.weightData = -1
  792. self.filter.sizeData = -1
  793.  
  794. # Entity Toggle Button
  795. self.entityToggle = QToolButton()
  796. self.entityToggle.setCheckable(True)
  797. self.entityToggle.checked = False
  798. self.entityToggle.setIconSize(QSize(24, 24))
  799. self.entityToggle.toggled.connect(self.setEntityToggle)
  800. self.entityToggle.toggled.connect(self.changeFilter)
  801. self.entityToggle.setIcon(QIcon(QPixmap.fromImage(fq.copy(0,0,24,24))))
  802.  
  803. # Type Toggle Button
  804. self.typeToggle = QToolButton()
  805. self.typeToggle.setIconSize(QSize(24, 24))
  806. self.typeToggle.setPopupMode(QToolButton.InstantPopup)
  807.  
  808. typeMenu = QMenu()
  809.  
  810. q = QImage()
  811. q.load('resources/UI/RoomIcons.png')
  812.  
  813. self.typeToggle.setIcon(QIcon(QPixmap.fromImage(fq.copy(1*24+4,4,16,16))))
  814. act = typeMenu.addAction(QIcon(QPixmap.fromImage(fq.copy(1*24+4,4,16,16))), '')
  815. act.setData(-1)
  816.  
  817. for i in range(22):
  818. act = typeMenu.addAction(QIcon(QPixmap.fromImage(q.copy(i*16,0,16,16))), '')
  819. act.setData(i)
  820.  
  821. self.typeToggle.triggered.connect(self.setTypeFilter)
  822. self.typeToggle.setMenu(typeMenu)
  823.  
  824. # Weight Toggle Button
  825. self.weightToggle = QToolButton()
  826. self.weightToggle.setIconSize(QSize(24, 24))
  827. self.weightToggle.setPopupMode(QToolButton.InstantPopup)
  828.  
  829. weightMenu = FilterMenu()
  830.  
  831. q = QImage()
  832. q.load('resources/UI/WeightIcons.png')
  833.  
  834. self.weightToggle.setIcon(QIcon(QPixmap.fromImage(fq.copy(2*24,0,24,24))))
  835. act = weightMenu.addAction(QIcon(QPixmap.fromImage(fq.copy(2*24,0,24,24))), '')
  836. act.setData(-1)
  837. act.setIconVisibleInMenu(False)
  838.  
  839. w = [0.25,0.5,0.75,1.0,1.5,2.0,5.0,1000.0]
  840. for i in range(8):
  841. act = weightMenu.addAction(QIcon(QPixmap.fromImage(q.copy(i*24,0,24,24))), '')
  842. act.setData(w[i])
  843. act.setIconVisibleInMenu(False)
  844.  
  845. self.weightToggle.triggered.connect(self.setWeightFilter)
  846. self.weightToggle.setMenu(weightMenu)
  847.  
  848. # Size Toggle Button
  849. self.sizeToggle = QToolButton()
  850. self.sizeToggle.setIconSize(QSize(24, 24))
  851. self.sizeToggle.setPopupMode(QToolButton.InstantPopup)
  852.  
  853. sizeMenu = FilterMenu()
  854.  
  855. q = QImage()
  856. q.load('resources/UI/SizeIcons.png')
  857.  
  858. self.sizeToggle.setIcon(QIcon(QPixmap.fromImage(fq.copy(3*24,0,24,24))))
  859. act = sizeMenu.addAction(QIcon(QPixmap.fromImage(fq.copy(3*24,0,24,24))), '')
  860. act.setData(-1)
  861. act.setIconVisibleInMenu(False)
  862.  
  863. w = ['Small', 'Wide', 'Tall', 'Large']
  864. for i in range(4):
  865. act = sizeMenu.addAction(QIcon(QPixmap.fromImage(q.copy(i*24,0,24,24))), '')
  866. act.setData(w[i])
  867. act.setIconVisibleInMenu(False)
  868.  
  869. self.sizeToggle.triggered.connect(self.setSizeFilter)
  870. self.sizeToggle.setMenu(sizeMenu)
  871.  
  872. # Add to Layout
  873. self.filter.addStretch()
  874. self.filter.addWidget(QLabel("Filter by:"))
  875. self.filter.addWidget(self.entityToggle)
  876. self.filter.addWidget(self.typeToggle)
  877. self.filter.addWidget(self.weightToggle)
  878. self.filter.addWidget(self.sizeToggle)
  879. self.filter.setContentsMargins(4,0,0,4)
  880.  
  881. def setupList(self):
  882. self.list = QListWidget()
  883. self.list.setViewMode(self.list.ListMode)
  884. self.list.setSelectionMode(self.list.ExtendedSelection)
  885. self.list.setResizeMode(self.list.Adjust)
  886. self.list.setContextMenuPolicy(Qt.CustomContextMenu)
  887.  
  888. self.list.setAutoScroll(True)
  889. self.list.setDragEnabled(True)
  890. self.list.setDragDropMode(4)
  891.  
  892. self.list.setVerticalScrollBarPolicy(0)
  893. self.list.setHorizontalScrollBarPolicy(1)
  894.  
  895. self.list.setIconSize(QSize(52, 52))
  896. d = RoomDelegate()
  897. self.list.setItemDelegate(d)
  898.  
  899. self.list.itemSelectionChanged.connect(self.setButtonStates)
  900. self.list.doubleClicked.connect(self.activateEdit)
  901. self.list.customContextMenuRequested.connect(self.customContextMenu)
  902.  
  903. def setupToolbar(self):
  904. self.toolbar = QToolBar()
  905.  
  906. self.addRoomButton = self.toolbar.addAction(QIcon(), 'Add', self.addRoom)
  907. self.removeRoomButton = self.toolbar.addAction(QIcon(), 'Delete', self.removeRoom)
  908. self.duplicateRoomButton = self.toolbar.addAction(QIcon(), 'Duplicate', self.duplicateRoom)
  909. self.exportRoomButton = self.toolbar.addAction(QIcon(), 'Export...', self.exportRoom)
  910.  
  911. def activateEdit(self):
  912. self.list.editItem(self.selectedRoom())
  913.  
  914. @pyqtSlot(QPoint)
  915. def customContextMenu(self, pos):
  916. menu = QMenu(self.list)
  917.  
  918. # Size Changing Menu
  919. size = menu.addMenu('Size')
  920. s = size.addAction('Small')
  921. w = size.addAction('Wide')
  922. t = size.addAction('Tall')
  923. l = size.addAction('Large')
  924.  
  925. size.triggered.connect(self.changeSize)
  926. if self.selectedRoom().roomWidth == 13 and self.selectedRoom().roomHeight == 7:
  927. s.setCheckable(True)
  928. s.setChecked(True)
  929. elif self.selectedRoom().roomWidth == 26 and self.selectedRoom().roomHeight == 14:
  930. l.setCheckable(True)
  931. l.setChecked(True)
  932. elif self.selectedRoom().roomWidth == 26:
  933. w.setCheckable(True)
  934. w.setChecked(True)
  935. elif self.selectedRoom().roomHeight == 14:
  936. t.setCheckable(True)
  937. t.setChecked(True)
  938.  
  939. menu.addSeparator()
  940.  
  941. # Type
  942. Type = QWidgetAction(menu)
  943. c = QComboBox()
  944.  
  945. types= ["Null Room", "Normal Room", "Shop", "Error Room", "Treasure Room", "Boss Room",
  946. "Mini-Boss Room", "Secret Room", "Super Secret Room", "Arcade", "Curse Room", "Challenge Room",
  947. "Library", "Sacrifice Room", "Devil Room", "Angel Room", "Item Dungeon", "Boss Rush Room",
  948. "Isaac's Room", "Barren Room", "Chest Room", "Dice Room", "Black Market"]
  949.  
  950. if "00." not in mainWindow.path:
  951. types=["Null Room", "Normal Room"]
  952.  
  953. c.addItems(types)
  954. c.setCurrentIndex(self.selectedRoom().roomType)
  955. c.currentIndexChanged.connect(self.changeType)
  956. Type.setDefaultWidget(c)
  957. menu.addAction(Type)
  958.  
  959. # Difficulty
  960. diff = menu.addMenu('Difficulty')
  961.  
  962. for d in [0,1,2,5,10]:
  963. m = diff.addAction('{0}'.format(d))
  964.  
  965. if self.selectedRoom().roomDifficulty == d:
  966. m.setCheckable(True)
  967. m.setChecked(True)
  968.  
  969. diff.triggered.connect(self.changeDifficulty)
  970.  
  971. # Weight
  972. weight = menu.addMenu('Weight')
  973.  
  974. for w in [0.25,0.5,0.75,1.0,1.5,2.0,5.0,1000.0]:
  975. m = weight.addAction('{0}'.format(w))
  976.  
  977. if self.selectedRoom().roomWeight == w:
  978. m.setCheckable(True)
  979. m.setChecked(True)
  980.  
  981. weight.triggered.connect(self.changeWeight)
  982.  
  983. # Variant
  984. Variant = QWidgetAction(menu)
  985. s = QSpinBox()
  986. s.setRange(0,65534)
  987.  
  988. s.setValue(self.selectedRoom().roomVariant)
  989.  
  990. Variant.setDefaultWidget(s)
  991. s.valueChanged.connect(self.changeVariant)
  992. menu.addAction(Variant)
  993.  
  994. # End it
  995. menu.exec_(self.list.mapToGlobal(pos))
  996.  
  997. @pyqtSlot(bool)
  998. def setEntityToggle(self, checked):
  999. self.entityToggle.checked = checked
  1000.  
  1001. @pyqtSlot(QAction)
  1002. def setTypeFilter(self, action):
  1003. self.filter.typeData = action.data()
  1004. self.typeToggle.setIcon(action.icon())
  1005. self.changeFilter()
  1006.  
  1007. @pyqtSlot(QAction)
  1008. def setWeightFilter(self, action):
  1009. self.filter.weightData = action.data()
  1010. self.weightToggle.setIcon(action.icon())
  1011. self.changeFilter()
  1012.  
  1013. @pyqtSlot(QAction)
  1014. def setSizeFilter(self, action):
  1015. self.filter.sizeData = action.data()
  1016. self.sizeToggle.setIcon(action.icon())
  1017. self.changeFilter()
  1018.  
  1019. @pyqtSlot()
  1020. def changeFilter(self):
  1021.  
  1022. # Here we go
  1023. for room in self.getRooms():
  1024. entityCond = typeCond = weightCond = sizeCond = True
  1025.  
  1026. # Check if the right entity is in the room
  1027. if self.entityToggle.checked and self.filterEntity:
  1028. entityCond = False
  1029.  
  1030. for x in room.roomSpawns:
  1031. for y in x:
  1032. for e in y:
  1033. if int(self.filterEntity.ID) in e and int(self.filterEntity.subtype) in e and int(self.filterEntity.variant) in e:
  1034. entityCond = True
  1035.  
  1036. # Check if the room is the right type
  1037. if self.filter.typeData is not -1:
  1038. typeCond = self.filter.typeData == room.roomType
  1039.  
  1040. # Check if the room is the right weight
  1041. if self.filter.weightData is not -1:
  1042. weightCond = self.filter.weightData == room.roomWeight
  1043.  
  1044. # Check if the room is the right size
  1045. if self.filter.sizeData is not -1:
  1046. sizeCond = False
  1047.  
  1048. text = self.filter.sizeData
  1049. w = room.roomWidth
  1050. h = room.roomHeight
  1051.  
  1052. if w is 13 and h is 7 and text == 'Small':
  1053. sizeCond = True
  1054. if w is 26 and h is 7 and text == 'Wide':
  1055. sizeCond = True
  1056. if w is 13 and h is 14 and text == 'Tall':
  1057. sizeCond = True
  1058. if w is 26 and h is 14 and text == 'Large':
  1059. sizeCond = True
  1060.  
  1061. # Filter em' out
  1062. if entityCond and typeCond and weightCond and sizeCond:
  1063. room.setHidden(False)
  1064. else:
  1065. room.setHidden(True)
  1066.  
  1067. def setEntityFilter(self, entity):
  1068. self.filterEntity = entity
  1069. self.entityToggle.setIcon(entity.icon)
  1070. self.changeFilter()
  1071.  
  1072. @pyqtSlot(QAction)
  1073. def changeSize(self, action):
  1074.  
  1075. # Set the Size
  1076. if action.text() == "Small":
  1077. w,h = 13,7
  1078. elif action.text() == "Wide":
  1079. w,h = 26,7
  1080. elif action.text() == "Tall":
  1081. w,h = 13,14
  1082. elif action.text() == "Large":
  1083. w,h = 26,14
  1084.  
  1085. # No sense in doing work we don't have to!
  1086. if self.selectedRoom().roomWidth == w and self.selectedRoom().roomHeight == h:
  1087. return
  1088.  
  1089. # Check to see if resizing will destory any entities
  1090. warn = False
  1091. mainWindow.storeEntityList()
  1092.  
  1093. for y in enumerate(self.selectedRoom().roomSpawns):
  1094. for x in enumerate(y[1]):
  1095. for entity in x[1]:
  1096.  
  1097. if x[0] >= w or y[0] >= h:
  1098. warn = True
  1099.  
  1100. if warn:
  1101. msgBox = QMessageBox(QMessageBox.Warning,
  1102. "Resize Room?", "Resizing this room will delete entities placed outside the new size. Are you sure you want to resize this room?",
  1103. QMessageBox.NoButton, self)
  1104. msgBox.addButton("Resize", QMessageBox.AcceptRole)
  1105. msgBox.addButton("Cancel", QMessageBox.RejectRole)
  1106. if msgBox.exec_() == QMessageBox.RejectRole:
  1107. # It's time for us to go now.
  1108. return
  1109.  
  1110. # Clear the room and reset the size
  1111. mainWindow.scene.clear()
  1112. self.selectedRoom().roomWidth = w
  1113. self.selectedRoom().roomHeight = h
  1114.  
  1115. self.selectedRoom().makeNewDoors()
  1116. self.selectedRoom().clearDoors()
  1117. mainWindow.scene.newRoomSize(w, h)
  1118.  
  1119. mainWindow.editor.resizeEvent(QResizeEvent(mainWindow.editor.size(), mainWindow.editor.size()))
  1120.  
  1121. # Spawn those entities
  1122. for y in enumerate(self.selectedRoom().roomSpawns):
  1123. for x in enumerate(y[1]):
  1124. for entity in x[1]:
  1125. if x[0] >= w or y[0] >= h: continue
  1126.  
  1127. e = Entity(x[0], y[0], entity[0], entity[1], entity[2], entity[3])
  1128. mainWindow.scene.addItem(e)
  1129.  
  1130. self.selectedRoom().setToolTip()
  1131. mainWindow.dirt()
  1132.  
  1133. @pyqtSlot(int)
  1134. def changeType(self, rtype):
  1135. self.selectedRoom().roomType = rtype
  1136. self.selectedRoom().renderDisplayIcon()
  1137. self.selectedRoom().setRoomBG()
  1138.  
  1139. self.selectedRoom().setToolTip()
  1140.  
  1141. mainWindow.scene.update()
  1142. mainWindow.dirt()
  1143.  
  1144. @pyqtSlot(int)
  1145. def changeVariant(self, var):
  1146. self.selectedRoom().roomVariant = var
  1147. self.selectedRoom().setToolTip()
  1148. mainWindow.dirt()
  1149.  
  1150. @pyqtSlot(QAction)
  1151. def changeDifficulty(self, action):
  1152. self.selectedRoom().roomDifficulty = int(action.text())
  1153. self.selectedRoom().setToolTip()
  1154. mainWindow.dirt()
  1155.  
  1156. @pyqtSlot(QAction)
  1157. def changeWeight(self, action):
  1158. self.selectedRoom().roomWeight = float(action.text())
  1159. self.selectedRoom().setToolTip()
  1160. mainWindow.dirt()
  1161.  
  1162. def keyPressEvent(self, event):
  1163. self.list.keyPressEvent(event)
  1164.  
  1165. if event.key() == Qt.Key_Delete or event.key() == Qt.Key_Backspace:
  1166. self.removeRoom()
  1167.  
  1168. def addRoom(self):
  1169. """Creates a new room."""
  1170.  
  1171. r = Room()
  1172. self.list.insertItem(self.list.currentRow()+1, r)
  1173. self.list.setCurrentItem(r, QItemSelectionModel.ClearAndSelect)
  1174. mainWindow.dirt()
  1175.  
  1176. def removeRoom(self):
  1177. """Removes selected room (no takebacks)"""
  1178.  
  1179. rooms = self.selectedRooms()
  1180. if rooms is None or len(rooms) == 0:
  1181. return
  1182.  
  1183. if len(rooms) == 1:
  1184. s = "this room"
  1185. else:
  1186. s = "these rooms"
  1187.  
  1188. msgBox = QMessageBox(QMessageBox.Warning,
  1189. "Delete Room?", "Are you sure you want to delete {0}? This action cannot be undone.".format(s),
  1190. QMessageBox.NoButton, self)
  1191. msgBox.addButton("Delete", QMessageBox.AcceptRole)
  1192. msgBox.addButton("Cancel", QMessageBox.RejectRole)
  1193. if msgBox.exec_() == QMessageBox.AcceptRole:
  1194.  
  1195. self.list.clearSelection()
  1196. for item in rooms:
  1197. self.list.takeItem(self.list.row(item))
  1198.  
  1199. self.list.scrollToItem(self.list.currentItem())
  1200. self.list.setCurrentItem(self.list.currentItem(), QItemSelectionModel.Select)
  1201. mainWindow.dirt()
  1202.  
  1203. def duplicateRoom(self):
  1204. """Duplicates the selected room"""
  1205.  
  1206. rooms = self.selectedRooms()
  1207. if rooms is None or len(rooms) == 0:
  1208. return
  1209.  
  1210. mainWindow.storeEntityList()
  1211.  
  1212. rv = rooms[0].roomVariant
  1213. v = 0
  1214.  
  1215. initialPlace = self.list.currentRow()
  1216. self.selectedRoom().setData(100, False)
  1217. self.list.setCurrentItem(None, QItemSelectionModel.ClearAndSelect)
  1218.  
  1219. for room in rooms:
  1220. v += 1
  1221.  
  1222. r = Room( room.text() + ' (copy)', [list(door) for door in room.roomDoors], room.roomSpawns, room.roomType,
  1223. v+rv, room.roomDifficulty, room.roomWeight, room.roomWidth, room.roomHeight)
  1224.  
  1225. self.list.insertItem(initialPlace+v, r)
  1226. self.list.setCurrentItem(r, QItemSelectionModel.Select)
  1227.  
  1228. mainWindow.dirt()
  1229.  
  1230. def exportRoom(self):
  1231.  
  1232. # Get a new
  1233. dialogDir = '' if mainWindow.path is '' else os.path.dirname(mainWindow.path)
  1234. target = QFileDialog.getSaveFileName(self, 'Select a new name or an existing stb', dialogDir, 'Stage Bundle (*.stb)', '', QFileDialog.DontConfirmOverwrite)
  1235. mainWindow.restoreEditMenu()
  1236.  
  1237. if len(target) == 0:
  1238. return
  1239.  
  1240. path = target[0]
  1241.  
  1242. # Append these rooms onto the new stb
  1243. if os.path.exists(path):
  1244. rooms = self.selectedRooms()
  1245. oldRooms = mainWindow.open(path)
  1246.  
  1247. oldRooms.extend(rooms)
  1248.  
  1249. mainWindow.save(oldRooms, path)
  1250.  
  1251. # Make a new stb with the selected rooms
  1252. else:
  1253. mainWindow.save(self.selectedRooms(), path)
  1254.  
  1255. def setButtonStates(self):
  1256. rooms = len(self.selectedRooms()) > 0
  1257.  
  1258. self.removeRoomButton.setEnabled(rooms)
  1259. self.duplicateRoomButton.setEnabled(rooms)
  1260. self.exportRoomButton.setEnabled(rooms)
  1261.  
  1262. def selectedRoom(self):
  1263. return self.list.currentItem()
  1264.  
  1265. def selectedRooms(self):
  1266. return self.list.selectedItems()
  1267.  
  1268. def getRooms(self):
  1269. ret = []
  1270. for i in range(self.list.count()):
  1271. ret.append(self.list.item(i))
  1272.  
  1273. return ret
  1274.  
  1275. # Entity Palette
  1276. ########################
  1277.  
  1278. class EntityGroupItem(object):
  1279. """Group Item to contain Entities for sorting"""
  1280.  
  1281. def __init__(self, name):
  1282.  
  1283. self.objects = []
  1284. self.startIndex = 0
  1285. self.endIndex = 0
  1286.  
  1287. self.name = name
  1288. self.alignment = Qt.AlignCenter
  1289.  
  1290. def getItem(self, index):
  1291. ''' Retrieves an item of a specific index. The index is already checked for validity '''
  1292.  
  1293. if index == self.startIndex:
  1294. return self
  1295.  
  1296. if (index <= self.startIndex + len(self.objects)):
  1297. return self.objects[index - self.startIndex - 1]
  1298.  
  1299. def calculateIndices(self, index):
  1300. self.startIndex = index
  1301. self.endIndex = len(self.objects) + index
  1302.  
  1303. class EntityItem(object):
  1304. """A single entity, pretty much just an icon and a few params."""
  1305.  
  1306. def __init__(self, name, ID, subtype, variant, iconPath):
  1307. self.name = name
  1308. self.ID = ID
  1309. self.subtype = subtype
  1310. self.variant = variant
  1311. self.icon = QIcon(iconPath)
  1312.  
  1313. class EntityGroupModel(QAbstractListModel):
  1314. """Model containing all the grouped objects in a tileset"""
  1315.  
  1316. def __init__(self, kind):
  1317. self.groups = {}
  1318. self.kind = kind
  1319. self.view = None
  1320.  
  1321. QAbstractListModel.__init__(self)
  1322.  
  1323. global entityXML
  1324. enList = entityXML.findall("entity")
  1325.  
  1326. for en in enList:
  1327. g = en.get('Group')
  1328. k = en.get('Kind')
  1329.  
  1330. if self.kind == k:
  1331. if g not in self.groups.keys():
  1332. self.groups[g] = EntityGroupItem(g)
  1333.  
  1334. e = EntityItem(en.get('Name'), en.get('ID'), en.get('Subtype'), en.get('Variant'), en.get('Image'))
  1335.  
  1336. self.groups[g].objects.append(e)
  1337.  
  1338. i = 0
  1339. for key, group in sorted(self.groups.items()):
  1340. group.calculateIndices(i)
  1341. i = group.endIndex + 1
  1342.  
  1343. def rowCount(self, parent=None):
  1344. c = 0
  1345.  
  1346. for group in self.groups.values():
  1347. c += len(group.objects) + 1
  1348.  
  1349. return c
  1350.  
  1351. def flags(self, index):
  1352. item = self.getItem(index.row())
  1353.  
  1354. if isinstance(item, EntityGroupItem):
  1355. return Qt.NoItemFlags
  1356. else:
  1357. return Qt.ItemIsEnabled | Qt.ItemIsSelectable
  1358.  
  1359. def getItem(self, index):
  1360. for group in self.groups.values():
  1361. if (group.startIndex <= index) and (index <= group.endIndex):
  1362. return group.getItem(index)
  1363.  
  1364. def data(self, index, role=Qt.DisplayRole):
  1365. # Should return the contents of a row when asked for the index
  1366. #
  1367. # Can be optimized by only dealing with the roles we need prior
  1368. # to lookup: Role order is 13, 6, 7, 9, 10, 1, 0, 8
  1369.  
  1370. if ((role > 1) and (role < 6)):
  1371. return None
  1372.  
  1373. elif (role == Qt.ForegroundRole):
  1374. return QBrush(Qt.black)
  1375.  
  1376. elif role == Qt.TextAlignmentRole:
  1377. return Qt.AlignCenter
  1378.  
  1379.  
  1380. if not index.isValid(): return None
  1381. n = index.row()
  1382.  
  1383. if n < 0: return None
  1384. if n >= self.rowCount(): return None
  1385.  
  1386. item = self.getItem(n)
  1387.  
  1388. if role == Qt.DecorationRole:
  1389. if isinstance(item, EntityItem):
  1390. return item.icon
  1391.  
  1392. if role == Qt.ToolTipRole or role == Qt.StatusTipRole or role == Qt.WhatsThisRole:
  1393. if isinstance(item, EntityItem):
  1394. return "{0}".format(item.name)
  1395.  
  1396. elif role == Qt.DisplayRole:
  1397. if isinstance(item, EntityGroupItem):
  1398. return item.name
  1399.  
  1400. elif (role == Qt.SizeHintRole):
  1401. if isinstance(item, EntityGroupItem):
  1402. return QSize(self.view.viewport().width(), 24)
  1403.  
  1404. elif role == Qt.BackgroundRole:
  1405. if isinstance(item, EntityGroupItem):
  1406.  
  1407. colour = 165
  1408.  
  1409. if colour > 255:
  1410. colour = 255
  1411.  
  1412. brush = QBrush(QColor(colour, colour, colour), Qt.Dense4Pattern)
  1413.  
  1414. return brush
  1415.  
  1416. elif (role == Qt.FontRole):
  1417. font = QFont()
  1418. font.setPixelSize(16)
  1419. font.setBold(True)
  1420.  
  1421. return font
  1422.  
  1423. return None
  1424.  
  1425. class EntityPalette(QWidget):
  1426.  
  1427. def __init__(self):
  1428. """Initialises the widget. Remember to call setTileset() on it
  1429. whenever the layer changes."""
  1430.  
  1431. QWidget.__init__(self)
  1432.  
  1433. self.menuSetup = False
  1434.  
  1435. self.layout = QVBoxLayout()
  1436. self.layout.setSpacing(0)
  1437.  
  1438. self.tabs = QTabWidget()
  1439. self.layout.addWidget(self.tabs)
  1440.  
  1441. for group in ["Pickups", "Enemies", "Bosses", "Stage", "Collect"]:
  1442.  
  1443. listView = QListView()
  1444. listView.setFlow(QListView.LeftToRight)
  1445. listView.setLayoutMode(QListView.SinglePass)
  1446. listView.setMovement(QListView.Static)
  1447. listView.setResizeMode(QListView.Adjust)
  1448. listView.setWrapping(True)
  1449. listView.setIconSize(QSize(26,26))
  1450.  
  1451. listView.setModel(EntityGroupModel(group))
  1452. listView.model().view = listView
  1453.  
  1454. listView.clicked.connect(self.objSelected)
  1455.  
  1456. if group == "Bosses":
  1457. listView.setIconSize(QSize(52,52))
  1458.  
  1459. if group == "Collect":
  1460. listView.setIconSize(QSize(32,64))
  1461.  
  1462. self.tabs.addTab(listView, group)
  1463.  
  1464. self.setLayout(self.layout)
  1465.  
  1466. def currentSelectedObject(self):
  1467. """Returns the currently selected object reference, for painting purposes."""
  1468.  
  1469. index = self.tabs.currentWidget().currentIndex().row()
  1470. obj = self.tabs.currentWidget().model().getItem(index)
  1471.  
  1472. return obj
  1473.  
  1474. @pyqtSlot()
  1475. def objSelected(self):
  1476. """Throws a signal emitting the current object when changed"""
  1477. self.objChanged.emit(self.currentSelectedObject())
  1478.  
  1479. # Throws a signal when the selected object is used as a replacement
  1480. if QApplication.keyboardModifiers() == Qt.AltModifier:
  1481. self.objReplaced.emit(self.currentSelectedObject())
  1482.  
  1483. objChanged = pyqtSignal(EntityItem)
  1484. objReplaced = pyqtSignal(EntityItem)
  1485.  
  1486.  
  1487. ########################
  1488. # Main Window #
  1489. ########################
  1490.  
  1491. class MainWindow(QMainWindow):
  1492.  
  1493. def __init__(self):
  1494. QMainWindow.__init__(self)
  1495.  
  1496. self.setWindowTitle('Basement Renovator')
  1497. self.setWindowIcon(QIcon('Resources/Koopatlas.png'))
  1498. self.setIconSize(QSize(16, 16))
  1499.  
  1500. self.dirty = False
  1501.  
  1502. self.scene = RoomScene()
  1503. self.clipboard = None
  1504.  
  1505. self.editor = RoomEditorWidget(self.scene)
  1506. self.setCentralWidget(self.editor)
  1507.  
  1508. self.setupDocks()
  1509. self.setupMenuBar()
  1510.  
  1511. # Restore Settings
  1512. if settings.value('GridEnabled', False): self.showGrid()
  1513. if settings.value('BitfontEnabled', False): self.switchBitFont()
  1514.  
  1515. self.restoreState(settings.value('MainWindowState', self.saveState()), 0)
  1516. self.restoreGeometry(settings.value('MainWindowGeometry', self.saveGeometry()))
  1517.  
  1518. # Setup a new map
  1519. self.newMap()
  1520. self.clean()
  1521.  
  1522. def setupMenuBar(self):
  1523. mb = self.menuBar()
  1524.  
  1525. f = mb.addMenu('&File')
  1526. self.fa = f.addAction('New', self.newMap, QKeySequence("Ctrl+N"))
  1527. self.fb = f.addAction('Open...', self.openMap, QKeySequence("Ctrl+O"))
  1528. f.addSeparator()
  1529. self.fd = f.addAction('Save', self.saveMap, QKeySequence("Ctrl+S"))
  1530. self.fe = f.addAction('Save As...', self.saveMapAs, QKeySequence("Ctrl+Shift+S"))
  1531. f.addSeparator()
  1532. self.fg = f.addAction('Take Screenshot...', self.screenshot, QKeySequence("Ctrl+Alt+S"))
  1533. f.addSeparator()
  1534. # self.fi = f.addAction('Quit')
  1535.  
  1536. self.e = mb.addMenu('Edit')
  1537. self.ea = self.e.addAction('Copy', self.copy, QKeySequence.Copy)
  1538. self.eb = self.e.addAction('Cut', self.cut, QKeySequence.Cut)
  1539. self.ec = self.e.addAction('Paste', self.paste, QKeySequence.Paste)
  1540. self.ed = self.e.addAction('Select All', self.selectAll, QKeySequence.SelectAll)
  1541. self.ee = self.e.addAction('Deselect', self.deSelect, QKeySequence("Ctrl+D"))
  1542. self.e.addSeparator()
  1543.  
  1544. v = mb.addMenu('View')
  1545. self.wa = v.addAction('Hide Grid', self.showGrid, QKeySequence("Ctrl+G"))
  1546. self.wd = v.addAction('Use Aliased Counter', self.switchBitFont, QKeySequence("Ctrl+Alt+A"))
  1547. v.addSeparator()
  1548. self.wb = v.addAction('Hide Entity Painter', self.showPainter, QKeySequence("Ctrl+Alt+P"))
  1549. self.wc = v.addAction('Hide Room List', self.showRoomList, QKeySequence("Ctrl+Alt+R"))
  1550.  
  1551. h = mb.addMenu('Help')
  1552. self.ha = h.addAction('About Basement Renovator', self.aboutDialog)
  1553. self.hb = h.addAction('Basement Renovator Documentation', self.goToHelp)
  1554. # self.hc = h.addAction('Keyboard Shortcuts')
  1555.  
  1556. def setupDocks(self):
  1557. self.roomList = RoomSelector()
  1558. self.roomListDock = QDockWidget('Rooms')
  1559. self.roomListDock.setWidget(self.roomList)
  1560. self.roomListDock.visibilityChanged.connect(self.updateDockVisibility)
  1561. self.roomListDock.setObjectName("RoomListDock")
  1562.  
  1563. self.roomList.list.currentItemChanged.connect(self.handleSelectedRoomChanged)
  1564.  
  1565. self.addDockWidget(Qt.RightDockWidgetArea, self.roomListDock)
  1566.  
  1567. self.EntityPalette = EntityPalette()
  1568. self.EntityPaletteDock = QDockWidget('Entity Palette')
  1569. self.EntityPaletteDock.setWidget(self.EntityPalette)
  1570. self.EntityPaletteDock.visibilityChanged.connect(self.updateDockVisibility)
  1571. self.EntityPaletteDock.setObjectName("EntityPaletteDock")
  1572.  
  1573. self.EntityPalette.objChanged.connect(self.handleObjectChanged)
  1574. self.EntityPalette.objReplaced.connect(self.handleObjectReplaced)
  1575.  
  1576. self.addDockWidget(Qt.LeftDockWidgetArea, self.EntityPaletteDock)
  1577.  
  1578. def restoreEditMenu(self):
  1579. a = self.e.actions()
  1580. self.e.insertAction(a[1], self.ea)
  1581. self.e.insertAction(a[2], self.eb)
  1582. self.e.insertAction(a[3], self.ec)
  1583. self.e.insertAction(a[4], self.ed)
  1584. self.e.insertAction(a[5], self.ee)
  1585.  
  1586. def updateTitlebar(self):
  1587. if self.path is '':
  1588. effectiveName = 'Untitled Map'
  1589. else:
  1590. effectiveName = os.path.basename(self.path)
  1591.  
  1592. self.setWindowTitle('%s - Basement Renovator' % effectiveName)
  1593.  
  1594. def checkDirty(self):
  1595. if self.dirty is False:
  1596. return False
  1597.  
  1598. msgBox = QMessageBox(QMessageBox.Warning,
  1599. "File is not saved", "Completing this operation without saving could cause loss of data.",
  1600. QMessageBox.NoButton, self)
  1601. msgBox.addButton("Continue", QMessageBox.AcceptRole)
  1602. msgBox.addButton("Cancel", QMessageBox.RejectRole)
  1603. if msgBox.exec_() == QMessageBox.AcceptRole:
  1604. return False
  1605.  
  1606. return True
  1607.  
  1608. def dirt(self):
  1609. self.dirty = True
  1610.  
  1611. def clean(self):
  1612. self.dirty = False
  1613.  
  1614. def storeEntityList(self, room=None):
  1615. if not room:
  1616. room = self.roomList.selectedRoom()
  1617.  
  1618. eList = self.scene.items()
  1619.  
  1620. spawns = [[[] for y in range(26)] for x in range(14)]
  1621. doors = []
  1622.  
  1623. for e in eList:
  1624. if isinstance(e, Door):
  1625. doors.append(e.doorItem)
  1626.  
  1627. else:
  1628. spawns[e.entity['Y']][e.entity['X']].append([e.entity['Type'], e.entity['Variant'], e.entity['Subtype'], e.entity['Weight']])
  1629.  
  1630. room.roomSpawns = spawns
  1631. room.roomDoors = doors
  1632.  
  1633. def closeEvent(self, event):
  1634. """Handler for the main window close event"""
  1635.  
  1636. if self.checkDirty():
  1637. event.ignore()
  1638. else:
  1639. # save our state
  1640. settings.setValue('MainWindowGeometry', self.saveGeometry())
  1641. settings.setValue('MainWindowState', self.saveState(0))
  1642.  
  1643. event.accept()
  1644.  
  1645.  
  1646. #####################
  1647. # Slots for Widgets #
  1648. #####################
  1649.  
  1650. @pyqtSlot(Room, Room)
  1651. def handleSelectedRoomChanged(self, current, prev):
  1652.  
  1653. if current:
  1654.  
  1655. # Encode the current room, just in case there are changes
  1656. if prev:
  1657. self.storeEntityList(prev)
  1658.  
  1659. # Clear the current room mark
  1660. prev.setData(100, False)
  1661.  
  1662. # Clear the room and reset the size
  1663. self.scene.clear()
  1664. self.scene.newRoomSize(current.roomWidth, current.roomHeight)
  1665.  
  1666. self.editor.resizeEvent(QResizeEvent(self.editor.size(), self.editor.size()))
  1667.  
  1668. # Make some doors
  1669. current.clearDoors()
  1670.  
  1671. # Spawn those entities
  1672. for y in enumerate(current.roomSpawns):
  1673. for x in enumerate(y[1]):
  1674. for entity in x[1]:
  1675. e = Entity(x[0], y[0], entity[0], entity[1], entity[2], entity[3])
  1676. self.scene.addItem(e)
  1677.  
  1678. # Make the current Room mark for clearer multi-selection
  1679. current.setData(100, True)
  1680.  
  1681. @pyqtSlot(EntityItem)
  1682. def handleObjectChanged(self, entity):
  1683. self.editor.objectToPaint = entity
  1684. self.roomList.setEntityFilter(entity)
  1685.  
  1686. @pyqtSlot(EntityItem)
  1687. def handleObjectReplaced(self, entity):
  1688. for item in self.scene.selectedItems():
  1689. item.entity['Type'] = int(entity.ID)
  1690. item.entity['Variant'] = int(entity.variant)
  1691. item.entity['Subtype'] = int(entity.subtype)
  1692.  
  1693. item.getEntityInfo(int(entity.ID), int(entity.subtype), int(entity.variant))
  1694. item.update()
  1695.  
  1696. self.dirt()
  1697.  
  1698.  
  1699. ########################
  1700. # Slots for Menu Items #
  1701. ########################
  1702.  
  1703. # File
  1704. ########################
  1705. def newMap(self):
  1706. if self.checkDirty(): return
  1707. self.roomList.list.clear()
  1708. self.scene.clear()
  1709. self.path = ''
  1710.  
  1711. self.updateTitlebar()
  1712. self.dirt()
  1713.  
  1714. def openMap(self):
  1715.  
  1716. if self.checkDirty(): return
  1717.  
  1718. target = QFileDialog.getOpenFileName(
  1719. self, 'Open Map', '', 'Stage Bundle (*.stb)')
  1720. self.restoreEditMenu()
  1721.  
  1722. # Looks like nothing was selected
  1723. if len(target[0]) == 0:
  1724. return
  1725.  
  1726. self.roomList.list.clear()
  1727. self.scene.clear()
  1728. self.path = ''
  1729.  
  1730. self.path = target[0]
  1731. self.updateTitlebar()
  1732.  
  1733. rooms = self.open()
  1734. for room in rooms:
  1735. self.roomList.list.addItem(room)
  1736.  
  1737. self.clean()
  1738.  
  1739. def open(self, path=None):
  1740.  
  1741. if not path:
  1742. path = self.path
  1743.  
  1744. # Let's read the file and parse it into our list items
  1745. stb = open(path, 'rb').read()
  1746.  
  1747. # Room count
  1748. rooms = struct.unpack_from('<I', stb, 0)[0]
  1749. off = 4
  1750. ret = []
  1751.  
  1752. for room in range(rooms):
  1753.  
  1754. # Room Type, Room Variant, Difficulty, Length of Room Name String
  1755. roomData = struct.unpack_from('<IIBH', stb, off)
  1756. off += 11
  1757.  
  1758. # Room Name
  1759. roomName = struct.unpack_from('<{0}s'.format(roomData[3]), stb, off)[0].decode()
  1760. off += roomData[3]
  1761.  
  1762. # Weight, width, height, number of doors, number of entities
  1763. entityTable = struct.unpack_from('<fBBBH', stb, off)
  1764. off += 9
  1765.  
  1766. doors = []
  1767. for door in range(entityTable[3]):
  1768. # X, Y, exists
  1769. d = struct.unpack_from('<hh?', stb, off)
  1770. doors.append([d[0], d[1], d[2]])
  1771. off += 5
  1772.  
  1773. spawns = [[[] for y in range(26)] for x in range(14)]
  1774. for entity in range(entityTable[4]):
  1775. # x, y, number of entities at this position
  1776. spawnLoc = struct.unpack_from('<hhB', stb, off)
  1777. off += 5
  1778.  
  1779. for spawn in range(spawnLoc[2]):
  1780. # type, variant, subtype, weight
  1781. t = struct.unpack_from('<HHHf', stb, off)
  1782. spawns[spawnLoc[1]][spawnLoc[0]].append([t[0], t[1], t[2], t[3]])
  1783. off += 10
  1784.  
  1785. r = Room(roomName, doors, spawns, roomData[0], roomData[1], roomData[2], entityTable[0], entityTable[1], entityTable[2])
  1786. ret.append(r)
  1787.  
  1788. return ret
  1789.  
  1790. def saveMap(self, forceNewName=False):
  1791. target = self.path
  1792.  
  1793. if target is '' or forceNewName:
  1794. dialogDir = '' if target is '' else os.path.dirname(target)
  1795. target = QFileDialog.getSaveFileName(self, 'Save Map', dialogDir, 'Stage Bundle (*.stb)')
  1796. self.restoreEditMenu()
  1797.  
  1798. if len(target) == 0:
  1799. return
  1800.  
  1801. self.path = target[0]
  1802. self.updateTitlebar()
  1803.  
  1804. self.save(self.roomList.getRooms())
  1805. self.clean()
  1806.  
  1807. def saveMapAs(self):
  1808. self.saveMap(True)
  1809.  
  1810. def save(self, rooms, path=None):
  1811. if not path:
  1812. path = self.path
  1813.  
  1814. self.storeEntityList()
  1815.  
  1816. stb = open(path, 'wb')
  1817. out = struct.pack('<I', len(rooms))
  1818.  
  1819. for room in rooms:
  1820.  
  1821. out += struct.pack('<IIBH{0}sfBB'.format(len(room.text())),
  1822. room.roomType, room.roomVariant, room.roomDifficulty, len(room.text()),
  1823. room.text().encode(), room.roomWeight, room.roomWidth, room.roomHeight)
  1824.  
  1825. # Doors and Entities
  1826. out += struct.pack('<BH', len(room.roomDoors), room.getSpawnCount())
  1827.  
  1828. for door in room.roomDoors:
  1829. out += struct.pack('<hh?', door[0], door[1], door[2])
  1830.  
  1831. for y in enumerate(room.roomSpawns):
  1832. for x in enumerate(y[1]):
  1833. if len(x[1]) == 0: continue
  1834.  
  1835. out += struct.pack('<hhB', x[0], y[0], len(x[1]))
  1836.  
  1837. for entity in x[1]:
  1838. out += struct.pack('<HHHf', entity[0], entity[1], entity[2], 2.0)
  1839.  
  1840. stb.write(out)
  1841.  
  1842. @pyqtSlot()
  1843. def screenshot(self):
  1844. fn = QFileDialog.getSaveFileName(self, 'Choose a new filename', 'untitled.png', 'Portable Network Grahics (*.png)')[0]
  1845. if fn == '': return
  1846.  
  1847. g = self.scene.grid
  1848. self.scene.grid = False
  1849.  
  1850. ScreenshotImage = QImage(self.scene.sceneRect().width(), self.scene.sceneRect().height(), QImage.Format_ARGB32)
  1851. ScreenshotImage.fill(Qt.transparent)
  1852.  
  1853. RenderPainter = QPainter(ScreenshotImage)
  1854. self.scene.render(RenderPainter, QRectF(ScreenshotImage.rect()), self.scene.sceneRect())
  1855. RenderPainter.end()
  1856.  
  1857. ScreenshotImage.save(fn, 'PNG', 50)
  1858.  
  1859. self.scene.grid = g
  1860.  
  1861. # Edit
  1862. ########################
  1863. @pyqtSlot()
  1864. def selectAll(self):
  1865.  
  1866. path = QPainterPath()
  1867. path.addRect(self.scene.sceneRect())
  1868. self.scene.setSelectionArea(path)
  1869.  
  1870. @pyqtSlot()
  1871. def deSelect(self):
  1872. self.scene.clearSelection()
  1873.  
  1874. @pyqtSlot()
  1875. def copy(self):
  1876. self.clipboard = []
  1877. for item in self.scene.selectedItems():
  1878. self.clipboard.append([item.entity['X'], item.entity['Y'], item.entity['Type'], item.entity['Variant'], item.entity['Subtype'], item.entity['Weight']])
  1879.  
  1880. @pyqtSlot()
  1881. def cut(self):
  1882. self.clipboard = []
  1883. for item in self.scene.selectedItems():
  1884. self.clipboard.append([item.entity['X'], item.entity['Y'], item.entity['Type'], item.entity['Variant'], item.entity['Subtype'], item.entity['Weight']])
  1885. item.remove()
  1886.  
  1887. @pyqtSlot()
  1888. def paste(self):
  1889. if not self.clipboard: return
  1890.  
  1891. self.scene.clearSelection()
  1892. for item in self.clipboard:
  1893. i = Entity(*item)
  1894. self.scene.addItem(i)
  1895.  
  1896. self.dirt()
  1897.  
  1898. # Miscellaneous
  1899. ########################
  1900.  
  1901. @pyqtSlot()
  1902. def showGrid(self):
  1903. """Handle toggling of the grid being showed"""
  1904. settings.setValue('GridEnabled', self.scene.grid)
  1905.  
  1906. if self.wa.text() == "Show Grid":
  1907. self.scene.grid = True
  1908. self.wa.setText("Hide Grid")
  1909. else:
  1910. self.scene.grid = False
  1911. self.wa.setText("Show Grid")
  1912.  
  1913. self.scene.update()
  1914.  
  1915. @pyqtSlot()
  1916. def switchBitFont(self):
  1917. """Handle toggling of the bitfont for entity counting"""
  1918. settings.setValue('BitfontEnabled', self.scene.bitText)
  1919.  
  1920. if self.wd.text() == "Use Aliased Counter":
  1921. self.scene.bitText = False
  1922. self.wd.setText("Use Bitfont Counter")
  1923. else:
  1924. self.scene.bitText = True
  1925. self.wd.setText("Use Aliased Counter")
  1926.  
  1927. self.scene.update()
  1928.  
  1929. @pyqtSlot()
  1930. def showPainter(self):
  1931. if self.EntityPaletteDock.isVisible():
  1932. self.EntityPaletteDock.hide()
  1933. else:
  1934. self.EntityPaletteDock.show()
  1935.  
  1936. self.updateDockVisibility()
  1937.  
  1938. @pyqtSlot()
  1939. def showRoomList(self):
  1940. if self.roomListDock.isVisible():
  1941. self.roomListDock.hide()
  1942. else:
  1943. self.roomListDock.show()
  1944.  
  1945. self.updateDockVisibility()
  1946.  
  1947. @pyqtSlot()
  1948. def updateDockVisibility(self):
  1949.  
  1950. if self.EntityPaletteDock.isVisible():
  1951. self.wb.setText('Hide Entity Painter')
  1952. else:
  1953. self.wb.setText('Show Entity Painter')
  1954.  
  1955. if self.roomListDock.isVisible():
  1956. self.wc.setText('Hide Room List')
  1957. else:
  1958. self.wc.setText('Show Room List')
  1959.  
  1960.  
  1961. # Help
  1962. ########################
  1963.  
  1964. @pyqtSlot(bool)
  1965. def aboutDialog(self):
  1966. caption = "About the Basement Renovator"
  1967.  
  1968. text = "<big><b>Basement Renovator</b></big><br><br> The Basement Renovator Editor is an editor for custom rooms, for use with the Binding of Isaac Rebirth. In order to use it, you must have unpacked the .stb files from Binding of Isaac Rebirth.<br><br> The Basement Renovator was programmed by Tempus (u/Chronometrics).<br><br> Find the source at <github link here>."
  1969.  
  1970.  
  1971. msg = QMessageBox.about(mainWindow, caption, text)
  1972.  
  1973. @pyqtSlot(bool)
  1974. def goToHelp(self):
  1975. QDesktopServices().openUrl(QUrl('http://www.reddit.com/r/themoddingofisaac'))
  1976.  
  1977.  
  1978. if __name__ == '__main__':
  1979.  
  1980. import sys
  1981.  
  1982. # XML Globals
  1983. entityXML = getEntityXML()
  1984.  
  1985. # Application
  1986. app = QApplication(sys.argv)
  1987. app.setWindowIcon(QIcon('resources/UI/BasementRenovator.png'))
  1988.  
  1989. settings = QSettings('RoomEditor', 'Binding of Isaac Rebirth: Room Editor')
  1990.  
  1991. mainWindow = MainWindow()
  1992. mainWindow.setWindowIcon(QIcon('resources/UI/BasementRenovator-Small.png'))
  1993. mainWindow.setGeometry(100, 500, 1280, 600)
  1994. mainWindow.show()
  1995.  
  1996. sys.exit(app.exec_())
Add Comment
Please, Sign In to add comment