Guest User

Untitled

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