Guest User

Untitled

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