From 64df3b0e7503b2da43d5448f788bb560e5a6881c Mon Sep 17 00:00:00 2001 From: Treeki Date: Wed, 26 Sep 2012 05:38:44 +0200 Subject: added world change nodes. I hope this works. --- src/editorui/paths.py | 39 ++++++++++-- src/exporter.py | 31 +++++++++- src/mapdata.py | 28 +++++---- src/ui.py | 7 +++ src/worldeditor.py | 163 ++++++++++++++++++++++++++++++++++++++++++++++++-- 5 files changed, 246 insertions(+), 22 deletions(-) (limited to 'src') diff --git a/src/editorui/paths.py b/src/editorui/paths.py index 2ddea4c..69a08e6 100644 --- a/src/editorui/paths.py +++ b/src/editorui/paths.py @@ -16,9 +16,7 @@ class KPEditorNode(KPEditorItem): self.setIconSize(QtCore.QSize(24, 24)) self.setFixedSize(24, 24) - self.iconList = [QtGui.QIcon("Resources/Through.png"), - QtGui.QIcon("Resources/Level.png"), - QtGui.QIcon("Resources/Exit.png")] + self.iconList = [KP.icon('Through'), KP.icon('Level'), KP.icon('Exit'), KP.icon('WorldChange')] self.state = 0 @@ -32,7 +30,7 @@ class KPEditorNode(KPEditorItem): def toggle(self): self.state += 1 - if self.state == 3: + if self.state == 4: self.state = 0 self.stateToggled.emit(self.state) @@ -121,6 +119,7 @@ class KPEditorNode(KPEditorItem): self._boundingRect = QtCore.QRectF(-24, -24, 48, 48) self._levelRect = self._boundingRect self._stopRect = QtCore.QRectF(-12, -12, 24, 24) + self._worldChangeRect = QtCore.QRectF(-16, -16, 32, 32) self._tinyRect = QtCore.QRectF(-6, -6, 12, 12) self.isLayerSelected = False @@ -165,12 +164,20 @@ class KPEditorNode(KPEditorItem): self.transitionProxy = self.HiddenProxy(self.transition, self, -102, 24) self.transition.currentIndexChanged.connect(self.transitionChange) + self.worldDefID = self.LevelSlotSpinner() + self.worldDefID.setRange(0,255) + self.worldDefIDProxy = self.HiddenProxy(self.worldDefID, self, 60, 24) + self.worldDefID.valueChanged.connect(self.worldDefIDChange) + if node.foreignID != None: self.foreignID.setValue(node.foreignID) if node.mapChange != None: self.mapChange.setText(node.mapChange) + if node.worldDefID != None: + self.worldDefID.setValue(node.worldDefID) + self.transition.setCurrentIndex(node.transition) @@ -187,6 +194,7 @@ class KPEditorNode(KPEditorItem): node.mapID = None node.foreignID = None node.level = None + node.worldDefID = None if state == 1: node.level = [1, 1] @@ -212,6 +220,9 @@ class KPEditorNode(KPEditorItem): self.mapChange.setText('None.arc') self.transition.setCurrentIndex(0) + elif state == 3: + node.worldDefID = 0 + self.update() KP.mainWindow.pathNodeList.update() @@ -253,6 +264,12 @@ class KPEditorNode(KPEditorItem): KP.mainWindow.pathNodeList.update() + @QtCore.pyqtSlot(int) + def worldDefIDChange(self, ID): + node = self._nodeRef() + node.worldDefID = ID + + @QtCore.pyqtSlot(int) def transitionChange(self, index): @@ -326,6 +343,12 @@ class KPEditorNode(KPEditorItem): selectionRect = self._boundingRect.adjusted(1,5,-1,-5) + elif node.worldDefID != None: + painter.setBrush(QtGui.QColor(0, 0, 0, 0)) + painter.setPen(QtGui.QColor(0, 0, 0, 0)) + painter.drawPixmap(self._worldChangeRect.topLeft(), QtGui.QPixmap("Resources/WorldChange.png")) + selectionRect = self._worldChangeRect + elif len(node.exits) != 2: if self.isLayerSelected: brush = QtGui.QBrush(QtGui.QColor(255, 40, 40)) @@ -352,6 +375,8 @@ class KPEditorNode(KPEditorItem): painter.setBrush(QtGui.QColor(0,0,0,0)) painter.drawEllipse(selectionRect) + # WHAT THE FUCK SINCE WHEN DO YOU SHOW/HIDE WIDGETS IN A PAINT EVENT + # oh well, I don't feel like refactoring this self.buttonProxy.show() if node.level: @@ -374,6 +399,11 @@ class KPEditorNode(KPEditorItem): self.transition.hide() self.mapChange.hide() + if node.worldDefID != None: + self.worldDefID.show() + else: + self.worldDefID.hide() + else: self.buttonProxy.hide() self.worldProxy.hide() @@ -382,6 +412,7 @@ class KPEditorNode(KPEditorItem): self.foreignID.hide() self.transition.hide() self.mapChange.hide() + self.worldDefID.hide() def remove(self, withItem=False): diff --git a/src/exporter.py b/src/exporter.py index ad352d3..eeca252 100644 --- a/src/exporter.py +++ b/src/exporter.py @@ -193,11 +193,12 @@ class KPMapExporter: # now that we've got that, we can pack the first part of the file version = 1 - headerSize = 0x24 + headerSize = 0x2C tsInfoOffsetInHeader = 0x10 - data = bytearray(struct.pack('>4sIIIIIIII', 'KP_m', version, len(self.layers), headerSize + len(sectorData), 0, 0, 0, headerSize, 0)) + data = bytearray(struct.pack('>4sIIIIIIIIII', 'KP_m', version, len(self.layers), headerSize + len(sectorData), 0, 0, 0, headerSize, 0, 0, len(self.map.worlds))) requiredFixUps.append((0x18, 'UnlockBytecode')) requiredFixUps.append((0x20, self.map.bgName)) + requiredFixUps.append((0x24, '_WorldDefList')) stringsToAdd.add(self.map.bgName) # list of layer pointers goes here.. or will, later @@ -395,6 +396,32 @@ class KPMapExporter: data += struct.pack('>bbbbfi', available, 0, 0, 0, path.movementSpeed, path.animation) + # align it to 4 bytes before we write the world defs + padding = ((len(data) + 4) & ~4) - len(data) + data += ('\0' * padding) + + offsets['_WorldDefList'] = len(data) + for world in self.map.worlds: + requiredFixUps.append((len(data), world.name)) + stringsToAdd.add(world.name) + data += zero32 + + fst1,fst2 = world.fsTextColours + fsh1,fsh2 = world.fsHintColours + ht1,ht2 = world.hudTextColours + htf = world.hudHintTransform + + data += struct.pack('>BBBB BBBB BBBB BBBB BBBB BBBB hhh BB', + fst1[0],fst1[1],fst1[2],fst1[3], + fst2[0],fst2[1],fst2[2],fst2[3], + fsh1[0],fsh1[1],fsh1[2],fsh1[3], + fsh2[0],fsh2[1],fsh2[2],fsh2[3], + ht1[0],ht1[1],ht1[2],ht1[3], + ht2[0],ht2[1],ht2[2],ht2[3], + htf[0],htf[1],htf[2], + world.uniqueKey, world.musicTrackID + ) + # now that we're almost done... pack the strings for string in stringsToAdd: offsets[string] = len(data) diff --git a/src/mapdata.py b/src/mapdata.py index 2c88f9b..5bfe9d5 100644 --- a/src/mapdata.py +++ b/src/mapdata.py @@ -427,7 +427,7 @@ class KPDoodadLayer(KPLayer): class KPNode(object): __dump_attribs__ = ( 'position', 'actions', 'level', 'hasSecret', 'mapChange', - 'transition', 'mapID', 'foreignID') + 'transition', 'mapID', 'foreignID', 'worldDefID') def _dump(self, mapObj, dest): dest['exitIDs'] = map(mapObj.refPath, self.exits) @@ -447,6 +447,7 @@ class KPNode(object): self.transition = 0 self.mapID = None self.foreignID = None + self.worldDefID = None def isStop(self): return True if (self.level or self.mapChange or len(self.exits) != 2) else False @@ -548,18 +549,19 @@ class KPPathLayer(KPLayer): @mapfile.dumpable('world_definition') class KPWorldDef(object): - __dump_attribs__ = ('name', 'fs_hint_colours', 'fs_text_colours', 'hud_hint_colours', 'hud_text_colours', 'music_track_id') + __dump_attribs__ = ('uniqueKey', 'name', 'fsHintColours', 'fsTextColours', 'hudHintTransform', 'hudTextColours', 'musicTrackID') def __init__(self): + self.uniqueKey = -1 self.name = 'Untitled World' - self.fs_hint_colours = (0x000000FF,0x000000FF) - self.fs_text_colours = (0xFFFFFFFF,0xFFFFFFFF) + self.fsHintColours = ((0,0,0,255),(0,0,0,255)) + self.fsTextColours = ((255,255,255,255),(255,255,255,255)) - self.hud_hint_transform = (0,0,0) - self.hud_text_colours = (0xFFFFFFFF,0xFFFFFFFF) + self.hudHintTransform = (0,0,0) + self.hudTextColours = ((255,255,255,255),(255,255,255,255)) - self.music_track_id = 0 + self.musicTrackID = 0 @mapfile.dumpable('map_root') @@ -581,11 +583,6 @@ class KPMap(object): else: self.version = 1 - if 'bgName' in source: - self.bgName = source['bgName'] - else: - self.bgName = '/Maps/Water.brres' - def _dump(self, mapObj, dest): dest['version'] = self.version dest['bgName'] = self.bgName @@ -624,7 +621,14 @@ class KPMap(object): self.doodadModel = KPMap.DoodadModel(self.doodadDefinitions) self.worlds = [] + self.nextWorldKey = 1 + + self.bgName = '/Maps/Water.brres' + def allocateWorldDefKey(self): + key = self.nextWorldKey + self.nextWorldKey += 1 + return key # LAYERS class LayerModel(QtCore.QAbstractListModel): diff --git a/src/ui.py b/src/ui.py index 05923e3..c154225 100644 --- a/src/ui.py +++ b/src/ui.py @@ -1140,6 +1140,7 @@ class KPMainWindow(QtGui.QMainWindow): m = mb.addMenu('Map') self.ma = m.addAction('Set Background...', self.setMapBackground) + self.ma = m.addAction('World Editor...', self.showWorldEditor) w = mb.addMenu('Window') self.wa = w.addAction('Show Grid', self.showGrid, QKeySequence("Ctrl+G")) @@ -1635,6 +1636,12 @@ class KPMainWindow(QtGui.QMainWindow): if newBG is not None: KP.map.bgName = newBG + @QtCore.pyqtSlot() + def showWorldEditor(self): + from worldeditor import KPWorldEditor + dlg = KPWorldEditor(KP.map, self) + dlg.show() + # Window ######################## @QtCore.pyqtSlot() diff --git a/src/worldeditor.py b/src/worldeditor.py index a2262ae..35e42a6 100644 --- a/src/worldeditor.py +++ b/src/worldeditor.py @@ -1,17 +1,172 @@ from common import * +import re + +def editableColourStr(array): + return '#%02X%02X%02X (%d)' % tuple(array) + +NICE_STR_RE = re.compile('^#([0-9a-fA-F]{2})([0-9a-fA-F]{2})([0-9a-fA-F]{2})\s*(?:\((\d+)\))?$') +def colourFromNiceStr(thing): + match = NICE_STR_RE.match(thing) + try: + if match: + r,g,b,a = match.groups() + return (int(r,16), int(g,16), int(b,16), int(a) if a != None else 255) + except: + pass + return None + +class KPWorldTableModel(QtCore.QAbstractTableModel): + FIELDS = ('Name', 'Track ID', + 'FS Text 1', 'FS Text 2', + 'FS Hint 1', 'FS Hint 2', + 'HUD Text 1', 'HUD Text 2', + 'HUD Hue', 'HUD Saturation', 'HUD Lightness') + + def __init__(self, kpmap, parent=None): + QtCore.QAbstractTableModel.__init__(self, parent) + + self.currentMap = kpmap + self.worlds = kpmap.worlds + + def columnCount(self, parent): + return len(self.FIELDS) + def headerData(self, section, orientation, role): + if orientation == Qt.Horizontal: + if role == Qt.DisplayRole: + return self.FIELDS[section] + else: + if role == Qt.DisplayRole: + return str(self.worlds[section].uniqueKey) + + return QtCore.QVariant() + + def rowCount(self, parent): + if parent.isValid(): + return 0 + else: + return len(self.worlds) + + def data(self, index, role): + if index.isValid(): + entry = self.worlds[index.row()] + col = index.column() + + if role == Qt.DisplayRole or role == Qt.EditRole: + if col == 0: + return entry.name + elif col == 1: + return entry.musicTrackID + elif col == 2 or col == 3: + return editableColourStr(entry.fsTextColours[col - 2]) + elif col == 4 or col == 5: + return editableColourStr(entry.fsHintColours[col - 4]) + elif col == 6 or col == 7: + return editableColourStr(entry.hudTextColours[col - 6]) + elif col >= 8 and col <= 10: + return entry.hudHintTransform[col - 8] + + if role == Qt.DecorationRole: + if col == 2 or col == 3: + return QtGui.QColor(*entry.fsTextColours[col - 2]) + elif col == 4 or col == 5: + return QtGui.QColor(*entry.fsHintColours[col - 4]) + elif col == 6 or col == 7: + return QtGui.QColor(*entry.hudTextColours[col - 6]) + + return QtCore.QVariant() + + def flags(self, index): + return Qt.ItemIsEnabled | Qt.ItemIsSelectable | Qt.ItemIsEditable + + def setData(self, index, value, role): + if index.isValid(): + if role == Qt.EditRole: + success = False + + entry = self.worlds[index.row()] + col = index.column() + + if col == 0: + entry.name = str(value.toString()) + success = True + elif col == 1: + v,ok = value.toInt() + if ok: + entry.musicTrackID = v + success = True + elif col >= 2 and col <= 7: + newCol = colourFromNiceStr(str(value.toString())) + if newCol: + success = True + if col == 2: + entry.fsTextColours = (newCol, entry.fsTextColours[1]) + elif col == 3: + entry.fsTextColours = (entry.fsTextColours[0], newCol) + elif col == 4: + entry.fsHintColours = (newCol, entry.fsHintColours[1]) + elif col == 5: + entry.fsHintColours = (entry.fsHintColours[0], newCol) + elif col == 6: + entry.hudTextColours = (newCol, entry.hudTextColours[1]) + elif col == 7: + entry.hudTextColours = (entry.hudTextColours[0], newCol) + elif col >= 8 and col <= 10: + v,ok = value.toInt() + if ok: + new = list(entry.hudHintTransform) + new[col - 8] = v + entry.hudHintTransform = new + success = True + + if success: + self.dataChanged.emit(index, index) + return success + + return False + + + def addEntryToEnd(self): + self.beginInsertRows(QtCore.QModelIndex(), len(self.worlds), len(self.worlds)) + entry = KPWorldDef() + entry.uniqueKey = self.currentMap.allocateWorldDefKey() + self.worlds.append(entry) + self.endInsertRows() + + def removeRows(self, row, count, parent): + if not parent.isValid(): + if row >= 0 and (row + count) <= len(self.worlds): + self.beginRemoveRows(parent, row, row+count-1) + for i in xrange(count): + del self.worlds[row] + self.endRemoveRows() + + class KPWorldEditor(QtGui.QWidget): - def __init__(self, parent=None): - QtGui.QWidget.__init__(self, parent) + def __init__(self, kpmap, parent=None): + QtGui.QWidget.__init__(self, parent, Qt.Window) self.setWindowTitle('World Editor') self.dataView = QtGui.QTableView(self) + self.addButton = QtGui.QPushButton('Add', self) - self.removedButton = QtGui.QPushButton('Remove', self) + self.removeButton = QtGui.QPushButton('Remove', self) layout = QtGui.QGridLayout(self) layout.addWidget(self.dataView, 0, 0, 1, 2) layout.addWidget(self.addButton, 1, 0, 1, 1) layout.addWidget(self.removeButton, 1, 1, 1, 1) - self.currentWorlds = None + self.model = KPWorldTableModel(kpmap, self) + self.dataView.setModel(self.model) + + self.addButton.clicked.connect(self.model.addEntryToEnd) + self.removeButton.clicked.connect(self.removeCurrentEntry) + + def removeCurrentEntry(self): + what = self.dataView.selectionModel().currentIndex() + if what.isValid(): + what = what.row() + key = self.model.worlds[what].uniqueKey + self.model.removeRows(what, 1, QtCore.QModelIndex()) + -- cgit v1.2.3