summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Resources/WorldChange.pngbin0 -> 2800 bytes
-rw-r--r--src/editorui/paths.py39
-rw-r--r--src/exporter.py31
-rw-r--r--src/mapdata.py28
-rw-r--r--src/ui.py7
-rw-r--r--src/worldeditor.py163
6 files changed, 246 insertions, 22 deletions
diff --git a/Resources/WorldChange.png b/Resources/WorldChange.png
new file mode 100644
index 0000000..a244752
--- /dev/null
+++ b/Resources/WorldChange.png
Binary files differ
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()
@@ -254,6 +265,12 @@ class KPEditorNode(KPEditorItem):
@QtCore.pyqtSlot(int)
+ def worldDefIDChange(self, ID):
+ node = self._nodeRef()
+ node.worldDefID = ID
+
+
+ @QtCore.pyqtSlot(int)
def transitionChange(self, index):
node = self._nodeRef()
@@ -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())
+