summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--Sample/YetAnotherTest.kpmap1
-rw-r--r--src/exporter.py192
-rw-r--r--src/ui.py4
3 files changed, 146 insertions, 51 deletions
diff --git a/Sample/YetAnotherTest.kpmap b/Sample/YetAnotherTest.kpmap
new file mode 100644
index 0000000..137f8d0
--- /dev/null
+++ b/Sample/YetAnotherTest.kpmap
@@ -0,0 +1 @@
+{"layers": [{"paths": [{"endNodeLink": 1, "unlocks": 0, "_t": "path", "secret": 0, "animation": 0, "startNodeLink": 0, "movementSpeed": 1.0}, {"endNodeLink": 2, "unlocks": 0, "_t": "path", "secret": 0, "animation": 0, "startNodeLink": 0, "movementSpeed": 1.0}, {"endNodeLink": 3, "unlocks": 0, "_t": "path", "secret": 0, "animation": 0, "startNodeLink": 0, "movementSpeed": 1.0}, {"endNodeLink": 4, "unlocks": 0, "_t": "path", "secret": 0, "animation": 0, "startNodeLink": 3, "movementSpeed": 1.0}, {"endNodeLink": 5, "unlocks": 0, "_t": "path", "secret": 0, "animation": 0, "startNodeLink": 3, "movementSpeed": 1.0}, {"endNodeLink": 1, "unlocks": 0, "_t": "path", "secret": 0, "animation": 0, "startNodeLink": 4, "movementSpeed": 1.0}], "nodes": [{"_t": "node", "level": null, "transition": 0, "mapID": null, "actions": [], "exitIDs": [0, 1, 2], "position": [168, 108], "foreignID": null, "mapChange": null}, {"_t": "node", "level": [1, 1], "transition": 0, "mapID": null, "actions": [], "exitIDs": [0, 5], "position": [252, 108], "foreignID": null, "mapChange": null}, {"_t": "node", "level": [1, 2], "transition": 0, "mapID": null, "actions": [], "exitIDs": [1], "position": [84, 108], "foreignID": null, "mapChange": null}, {"_t": "node", "level": null, "transition": 0, "mapID": null, "actions": [], "exitIDs": [2, 3, 4], "position": [168, 204], "foreignID": null, "mapChange": null}, {"_t": "node", "level": [2, 1], "transition": 0, "mapID": null, "actions": [], "exitIDs": [3, 5], "position": [252, 204], "foreignID": null, "mapChange": null}, {"_t": "node", "level": [2, 2], "transition": 0, "mapID": null, "actions": [], "exitIDs": [4], "position": [84, 204], "foreignID": null, "mapChange": null}], "_t": "path_layer", "name": "Paths", "_visible": true}, {"tileset": "Test3", "_t": "tile_layer", "name": "Walls", "objects": [{"position": [3, 9], "kind": 77, "_t": "object", "tileset": "Test3", "size": [2, 2]}, {"position": [10, 9], "kind": 80, "_t": "object", "tileset": "Test3", "size": [2, 2]}, {"position": [5, 10], "kind": 50, "_t": "object", "tileset": "Test3", "size": [5, 1]}, {"position": [5, 14], "kind": 52, "_t": "object", "tileset": "Test3", "size": [5, 1]}, {"position": [5, 11], "kind": 51, "_t": "object", "tileset": "Test3", "size": [5, 3]}, {"position": [10, 14], "kind": 82, "_t": "object", "tileset": "Test3", "size": [2, 1]}, {"position": [3, 14], "kind": 79, "_t": "object", "tileset": "Test3", "size": [2, 1]}, {"position": [3, 11], "kind": 78, "_t": "object", "tileset": "Test3", "size": [2, 3]}, {"position": [10, 11], "kind": 81, "_t": "object", "tileset": "Test3", "size": [2, 3]}], "_visible": true}, {"tileset": "Test3", "_t": "tile_layer", "name": "Ground", "objects": [{"position": [3, 4], "kind": 36, "_t": "object", "tileset": "Test3", "size": [2, 1]}, {"position": [5, 4], "kind": 31, "_t": "object", "tileset": "Test3", "size": [5, 1]}, {"position": [10, 4], "kind": 37, "_t": "object", "tileset": "Test3", "size": [2, 1]}, {"position": [3, 5], "kind": 24, "_t": "object", "tileset": "Test3", "size": [9, 5]}], "_visible": true}, {"_t": "doodad_layer", "name": "Doodads - Layer 2", "objects": [{"angle": 0, "_t": "doodad", "sourceRef": 0, "animations": [], "position": [328.0, 88.0], "size": [64, 64]}, {"angle": -32.07924966416843, "_t": "doodad", "sourceRef": 0, "animations": [], "position": [359.0, 234.0], "size": [64, 64]}, {"angle": 19.13230055635183, "_t": "doodad", "sourceRef": 0, "animations": [], "position": [22.0, 343.0], "size": [172.0, 106.0]}], "_visible": true}], "_t": "map_root", "pathNodeTileset": "Test3", "associateLayers": [{"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["node", 0], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["node", 1], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["path", 0], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["node", 2], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["path", 1], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["node", 3], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["path", 2], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["node", 4], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["path", 3], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["node", 5], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [], "_t": "associate_layer", "associatedRef": ["path", 4], "doodads": [], "_visible": true}, {"tileset": "Test3", "name": "", "objects": [{"position": [10, 5], "kind": 51, "_t": "object", "tileset": "Test3", "size": [2, 4]}], "_t": "associate_layer", "associatedRef": ["path", 5], "doodads": [], "_visible": true}], "nextLayerNumber": 4, "doodadDefinitions": [["W5_gake", {"_t": "pixmap", "png": "iVBORw0KGgoAAAANSUhEUgAAAEAAAABACAIAAAAlC+aJAAAAA3NCSVQICAjb4U/gAAAACXBIWXMAAA7DAAAOwwHHb6hkAAACs0lEQVRoge2ZrZakMBCFL3sQJSMjkciWI1euHDly5Mp9hJXzCCtHjhy5jzCyJRIZWfLKEWnodAMNTSeHwzm5CpLKVxSE/FXx7/0D41KBEygAgBM2AoAwhAXMhM2U4vDLaQeWsAQAJ1BBe1VNVIQB7LngPsXh3wgg9GSIaqxKFj1qSv6PGE+wpcrLKDlxPaVbfXfOPg5//1/ANV/+ytZPEbk9tlcifvHn5WdfKiKmOpjq4O/uGVhOxtoetT2S4w1T8Iu/r08ASPFGhAhohKhfjK07s5m+qK5B86EU39yjRM6t0vGLt9fzl2XnjBBfLaYWWxtbjeLVtXQNtfGtpIPe/oXj8i8CCD0xMCNhBj1Ymy8JSAIunxQi8scnsmH31MFPOWyyXBH55VT/M2G5iI4Y4P7lQ3z+kqVEj0uo1fzdT2S7D+DGWggLyqe0ZC0Uh7/7L7D7AMrD87O/ck0DP/npcExbKjHGT6u2rq+qEvGL/59vAEQMAEIBuOZIp+pCN/PrcmONWGPrAwCBAUCeCen4xef7bx+aj69voG2rzmnrZh2YyhprTVWdH8e/4yCAdPwugBNGunD7rTS0bXt/wfs4EUNubyATD5SCPx7AlY8lO8fQ/WwAEfnlZVsGDVoAEBEIRHwnvjClQkkQg+3L9Kgenz+3FiIJFQrhvD9f2IH46MnKw/wli7kAcf0yopwLPcTf/0QW3jxyQrRkRZWCv/8voPjlrwQNoN1x8XoRhjDE9VIiEb8MKmrC7/mdwOFOT/4QnKfD5JGfLxF/ZE9MVIRddX7fb9Zv9eq4/NFh1FevOL9fuC+Jyc/5ga2V8wNbK+cHuqY5P7CSn/MDnXJ+IOcH5vylVc4P7FY5P7C1dh9Azg90yvmBlfycHxjQlwQQkZ/zAzk/kPMDgXJ+YAPl/MAFd4P8wDeU2ykj9z1RngAAAABJRU5ErkJggg=="}]]} \ No newline at end of file
diff --git a/src/exporter.py b/src/exporter.py
index 4026929..0c24d50 100644
--- a/src/exporter.py
+++ b/src/exporter.py
@@ -1,8 +1,15 @@
from common import *
import array
import sys
+import math
from ctypes import create_string_buffer
+# Useful Stuff
+u32 = struct.Struct('>I')
+u16 = struct.Struct('>H')
+zero32 = u32.pack(0)
+
+
def RGB5A3Encode(tex):
tex = tex.toImage()
w, h = tex.width(), tex.height()
@@ -54,7 +61,8 @@ class KPMapExporter:
class LayerExporter:
def __init__(self, layer):
self.layer = layer
-
+
+ class TileLayerExporter(LayerExporter):
def buildSectors(self, sectors, indices):
# we'll use the cache held by the layer: why reinvent the wheel?
layer = self.layer
@@ -67,8 +75,8 @@ class KPMapExporter:
sectorLeft = layerX / 16
sectorTop = layerY / 16
- sectorRight = (layerX + layerWidth) / 16
- sectorBottom = (layerY + layerHeight) / 16
+ sectorRight = (layerX + layerWidth - 1) / 16
+ sectorBottom = (layerY + layerHeight - 1) / 16
rawSectors = []
for i in xrange(sectorBottom - sectorTop + 1):
@@ -100,7 +108,7 @@ class KPMapExporter:
# now add the created sectors to the data
count = reduce(lambda x,y: x+len(y), rawSectors, 0)
- sectorMap = [-1 for i in xrange(count)]
+ sectorMap = [0xFFFF for i in xrange(count)]
destIdx = 0
for srcRow in rawSectors:
@@ -119,23 +127,52 @@ class KPMapExporter:
destIdx += 1
self.sectorBounds = (sectorLeft, sectorTop, sectorRight, sectorBottom)
+ self.realBounds = (layerX, layerY, layerX+layerWidth-1, layerY+layerHeight-1)
self.sectorMap = sectorMap
+ class DoodadLayerExporter(LayerExporter):
+ pass
+
+ class PathLayerExporter(LayerExporter):
+ pass
+
def __init__(self, mapObj):
self.map = mapObj
- self.layers = map(KPMapExporter.LayerExporter, self.map.layers)
+ self.tileAssociates = {}
+ self.doodadAssociates = {}
- def build(self):
- u32 = struct.Struct('>I')
- u16 = struct.Struct('>H')
- zero32 = u32.pack(0)
+ output = []
+ for layer in self.map.layers:
+ if isinstance(layer, KPTileLayer) and len(layer.objects) > 0:
+ output.append(KPMapExporter.TileLayerExporter(layer))
+
+ elif isinstance(layer, KPDoodadLayer) and len(layer.objects) > 0:
+ output.append(KPMapExporter.DoodadLayerExporter(layer))
+
+ elif isinstance(layer, KPPathLayer):
+ for iLayer in self.map.associateLayers:
+ if len(iLayer.objects) > 0:
+ tl = KPMapExporter.TileLayerExporter(iLayer)
+ self.tileAssociates[iLayer.associate] = tl
+ output.append(tl)
+
+ if len(iLayer.doodads) > 0:
+ dl = KPMapExporter.DoodadLayerExporter(iLayer)
+ self.doodadAssociates[iLayer.associate] = dl
+ output.append(dl)
+
+ output.append(KPMapExporter.PathLayerExporter(layer))
+ self.layers = output
+
+ def build(self):
requiredFixUps = []
stringsToAdd = set()
textures = set()
+ tilesets = set()
offsets = {None: 0xFFFFFFFF}
# first off, build the sectors
@@ -143,7 +180,7 @@ class KPMapExporter:
sectorIndices = {}
for layer in self.layers:
- if isinstance(layer.layer, KPTileLayer):
+ if isinstance(layer, self.TileLayerExporter):
layer.buildSectors(sectors, sectorIndices)
sectorData = self._packSectorData(sectors)
@@ -158,6 +195,22 @@ class KPMapExporter:
requiredFixUps.append((len(data), layer))
data += zero32
+ # map all paths to unlock info
+ unlockInfo = {}
+
+ for node in self.map.pathLayer.nodes:
+ if not node.level: continue
+
+ checked = set()
+ affected = []
+ self._findUnlocksForNode(node, checked, affected)
+
+ level1, level2 = node.level
+
+ for item, secret in affected:
+ unlockInfo[item] = (level1, level2, secret)
+
+
# now build the layers
for eLayer in self.layers:
layer = eLayer.layer
@@ -165,32 +218,40 @@ class KPMapExporter:
offsets[eLayer] = len(data)
offsets[layer] = len(data)
- if isinstance(layer, KPTileLayer):
+ if isinstance(eLayer, self.TileLayerExporter):
data += u32.pack(0)
# tileset name
- stringsToAdd.add(layer.tileset)
- requiredFixUps.append((len(data), layer.tileset))
+ tileset = '/Maps/%s.bin' % layer.tileset
+ tilesets.add(tileset)
+ stringsToAdd.add(tileset)
+ requiredFixUps.append((len(data), ('tileset', tileset)))
data += zero32
# sector info
data += struct.pack('>IIII', *eLayer.sectorBounds)
+ data += struct.pack('>IIII', *eLayer.realBounds)
data += ''.join(map(u16.pack, eLayer.sectorMap))
pad = (4 - (len(data) & 3)) % 4
data += ('\0' * pad)
- elif isinstance(layer, KPDoodadLayer):
+ elif isinstance(eLayer, self.DoodadLayerExporter):
data += u32.pack(1)
# doodad list
- data += u32.pack(len(layer.objects))
- for doodad in layer.objects:
+ try:
+ doodadList = layer.doodads
+ except AttributeError:
+ doodadList = layer.objects
+
+ data += u32.pack(len(doodadList))
+ for doodad in doodadList:
requiredFixUps.append((len(data), doodad))
data += zero32
# now pack them ...
- for doodad in layer.objects:
+ for doodad in doodadList:
offsets[doodad] = len(data)
x, y = doodad.position
@@ -207,9 +268,9 @@ class KPMapExporter:
loopid = self.ANIM_LOOPS.index(rLoop)
curveid = self.ANIM_CURVES.index(rCurve)
typeid = self.ANIM_TYPES.index(rType)
- data += struct.pack('>iififf', loopid, curveid, rFrames, typeid, rStart, rEnd)
+ data += struct.pack('>iiiiiiii', loopid, curveid, rFrames, typeid, rStart, rEnd, 0, 0)
- elif isinstance(layer, KPPathLayer):
+ elif isinstance(eLayer, self.PathLayerExporter):
data += u32.pack(2)
# lists
@@ -232,14 +293,48 @@ class KPMapExporter:
x, y = node.position
current = len(data)
- data += struct.pack('>hhiiii', x, y, 0, 0, 0, 0)
+ data += struct.pack('>hhiiiiii', x+12, y+12, 0, 0, 0, 0, 0, 0)
+
+ # this varies
+ if node.isStop():
+ # figure out the exits by direction
+ leftExit, rightExit, upExit, downExit = None, None, None, None
+
+ for exit in node.exits:
+ start, end = exit._startNodeRef(), exit._endNodeRef()
+ opposite = end if (start == node) else start
+
+ oX, oY = opposite.position
+ deltaX, deltaY = oX-x, oY-y
+ angle = math.degrees(math.atan2(deltaX, deltaY)) % 360
+ print "Here: %d,%d Opposite %d,%d Delta: %d,%d Angle: %d" % (x,y,oX,oY,deltaX,deltaY,angle)
+
+ # Left = 270, Right = 90, Up = 180, Down = 0
+ if angle >= 225 and angle <= 315:
+ leftExit = exit
+ elif angle >= 45 and angle <= 135:
+ rightExit = exit
+ elif angle > 135 and angle < 225:
+ upExit = exit
+ elif angle > 315 or angle < 45:
+ downExit = exit
+
+ exits = [leftExit, rightExit, upExit, downExit]
+
+ else:
+ # not a stop, so just dump them in
+ exits = node.exits + [None,None,None,None]
- exits = node.exits + [None,None,None,None] # TODO
requiredFixUps.append((current+4, exits[0]))
requiredFixUps.append((current+8, exits[1]))
requiredFixUps.append((current+12, exits[2]))
requiredFixUps.append((current+16, exits[3]))
+ if node in self.tileAssociates:
+ requiredFixUps.append((current+20, self.tileAssociates[node]))
+ if node in self.doodadAssociates:
+ requiredFixUps.append((current+24, self.doodadAssociates[node]))
+
if node.isStop():
if node.level:
level1, level2 = node.level
@@ -269,29 +364,20 @@ class KPMapExporter:
requiredFixUps.append((current, start))
requiredFixUps.append((current+4, end))
- requiredFixUps.append((current+8, path.linkedLayer))
- data += (zero32 * 3)
-
- data += struct.pack('>fi', path.movementSpeed, path.animation)
-
- # unlocking paths/nodes!
- unlocks = []
- us = struct.Struct('>bbbbi')
-
- for node in self.map.pathLayer.nodes:
- if not node.level: continue
-
- checked = set()
- affected = []
- self._findUnlocksForNode(node, checked, affected, True)
+ if path in self.tileAssociates:
+ requiredFixUps.append((current+8, self.tileAssociates[path]))
+ if path in self.doodadAssociates:
+ requiredFixUps.append((current+12, self.doodadAssociates[path]))
- level1, level2 = node.level
+ data += (zero32 * 4)
- for item, secret in affected:
- unlocks.append(us.pack(secret, level1, level2, 0, offsets[item]))
+ try:
+ unlockL1, unlockL2, isSecret = unlockInfo[path]
+ unlockType = (2 if isSecret else 1)
+ except KeyError:
+ unlockL1, unlockL2, unlockType = 0, 0, 0
- struct.pack_into('>ii', data, 8, len(unlocks), len(data))
- data += ''.join(unlocks)
+ data += struct.pack('>bbbbfi', unlockType, unlockL1, unlockL2, 1, path.movementSpeed, path.animation)
# now that we're almost done... pack the strings
for string in stringsToAdd:
@@ -300,16 +386,21 @@ class KPMapExporter:
data += '\0'
# textures
- texHeaderStartOffset = len(data)
- texHeaderEndOffset = texHeaderStartOffset + (len(textures) * 0x20)
+ texPadding = ((len(data) + 0x1F) & ~0x1F) - len(data)
+ data += ('\0' * texPadding)
- texDataStartOffset = (texHeaderEndOffset + 0x1F) & ~0x1F
- texPadding = texDataStartOffset - texHeaderEndOffset
+ texHeaderStartOffset = len(data)
+ texDataStartOffset = texHeaderStartOffset + ((len(textures) + len(tilesets)) * 0x20)
currentTexOffset = texDataStartOffset
imageData = []
+ struct.pack_into('>ii', data, 8, len(textures), len(data))
+ for setname in tilesets:
+ offsets[('tileset', setname)] = len(data)
+ data += self._buildGXTexObjRGB5A3(1024, 512, offsets[setname])
+
for tex in textures:
offsets[tex] = len(data)
data += self._buildGXTexObjRGB5A3(tex.width(), tex.height(), currentTexOffset)
@@ -318,7 +409,6 @@ class KPMapExporter:
imageData.append(converted)
currentTexOffset += len(converted)
- data += ('\0' * texPadding)
for piece in imageData:
data += piece
@@ -333,7 +423,7 @@ class KPMapExporter:
ANIM_TYPES = ['X Position', 'Y Position', 'Angle', 'X Scale', 'Y Scale', 'Opacity']
- def _findUnlocksForNode(self, node, checked, affected, isFirstBranch=False, secret=None):
+ def _findUnlocksForNode(self, node, checked, affected, isFirstBranch=True, secret=None):
if node in checked: return
checked.add(node)
@@ -367,11 +457,11 @@ class KPMapExporter:
# Format: RGB5A3 (5)
# Wrap: CLAMP (0)
return struct.pack('>IIIIIIIHH',
- 0x10, 0,
- (0x20000 | ((height - 1) << 10) | (width - 1)),
- imgOffset, # (imgptr >> 5)
+ 0x90, 0,
+ (0x500000 | ((height - 1) << 10) | (width - 1)),
+ 0x10000000 + imgOffset, # (imgptr >> 5)
0, 0, 0,
- (((width + 3) / 4) * ((height + 3) / 4)) & 0x1FFFF,
+ (((width + 3) / 4) * ((height + 3) / 4)) & 0x7FFF,
0x0202
)
diff --git a/src/ui.py b/src/ui.py
index 1a654ab..56795b6 100644
--- a/src/ui.py
+++ b/src/ui.py
@@ -328,21 +328,25 @@ class KPLayerList(QtGui.QWidget):
def moveUp(self):
index = self.selectedLayerIndex()
KP.map.moveLayer(index, index - 1)
+ self.setButtonStates()
KP.mainWindow.editor.viewport().update()
def moveDown(self):
index = self.selectedLayerIndex()
KP.map.moveLayer(index, index + 2)
+ self.setButtonStates()
KP.mainWindow.editor.viewport().update()
def moveTop(self):
index = self.selectedLayerIndex()
KP.map.moveLayer(index, 0)
+ self.setButtonStates()
KP.mainWindow.editor.viewport().update()
def moveBottom(self):
index = self.selectedLayerIndex()
KP.map.moveLayer(index, len(KP.map.layers))
+ self.setButtonStates()
KP.mainWindow.editor.viewport().update()