diff options
Diffstat (limited to '')
| -rw-r--r-- | Sample/YetAnotherTest.kpmap | 1 | ||||
| -rw-r--r-- | src/exporter.py | 192 | ||||
| -rw-r--r-- | src/ui.py | 4 | 
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  				) @@ -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() | 
