Map saving first pass
This commit is contained in:
@@ -7,4 +7,5 @@ add_subdirectory(palette)# Palette asset needs to be added before any images.
|
|||||||
|
|
||||||
add_subdirectory(config)
|
add_subdirectory(config)
|
||||||
add_subdirectory(entity)
|
add_subdirectory(entity)
|
||||||
|
add_subdirectory(map)
|
||||||
add_subdirectory(ui)
|
add_subdirectory(ui)
|
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<tileset version="1.10" tiledversion="1.11.1" name="entities" tilewidth="16" tileheight="16" tilecount="64" columns="8">
|
<tileset version="1.10" tiledversion="1.11.2" name="entities" tilewidth="16" tileheight="16" tilecount="64" columns="8">
|
||||||
<image source="entities.png" width="128" height="128"/>
|
<image source="entities.png" width="128" height="128"/>
|
||||||
</tileset>
|
</tileset>
|
||||||
|
6
assets/map/CMakeLists.txt
Normal file
6
assets/map/CMakeLists.txt
Normal file
@@ -0,0 +1,6 @@
|
|||||||
|
# Copyright (c) 2025 Dominic Masters
|
||||||
|
#
|
||||||
|
# This software is released under the MIT License.
|
||||||
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
|
add_asset(MAP untitled.tmx)
|
28
assets/map/untitled.tmx
Normal file
28
assets/map/untitled.tmx
Normal file
@@ -0,0 +1,28 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<map version="1.10" tiledversion="1.11.2" orientation="orthogonal" renderorder="right-down" width="30" height="20" tilewidth="16" tileheight="16" infinite="0" nextlayerid="2" nextobjectid="1">
|
||||||
|
<tileset firstgid="1" source="../tileset/prarie.tsx"/>
|
||||||
|
<layer id="1" name="Tile Layer 1" width="30" height="20">
|
||||||
|
<data encoding="csv">
|
||||||
|
2,3,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
9,10,11,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
16,17,18,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,
|
||||||
|
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
|
||||||
|
</data>
|
||||||
|
</layer>
|
||||||
|
</map>
|
@@ -3,4 +3,4 @@
|
|||||||
# This software is released under the MIT License.
|
# This software is released under the MIT License.
|
||||||
# https://opensource.org/licenses/MIT
|
# https://opensource.org/licenses/MIT
|
||||||
|
|
||||||
add_asset(PALETTE pallet0.png)
|
add_asset(PALETTE palette0.png)
|
BIN
assets/palette/palette0.png
Normal file
BIN
assets/palette/palette0.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 241 B |
BIN
assets/palette/palette0.pxo
Normal file
BIN
assets/palette/palette0.pxo
Normal file
Binary file not shown.
Binary file not shown.
Before Width: | Height: | Size: 268 B |
BIN
assets/tileset/prarie.png
Normal file
BIN
assets/tileset/prarie.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 758 B |
BIN
assets/tileset/prarie.pxo
Normal file
BIN
assets/tileset/prarie.pxo
Normal file
Binary file not shown.
4
assets/tileset/prarie.tsx
Normal file
4
assets/tileset/prarie.tsx
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<tileset version="1.10" tiledversion="1.11.2" name="prarie" tilewidth="16" tileheight="16" tilecount="21" columns="7">
|
||||||
|
<image source="prarie.png" width="112" height="48"/>
|
||||||
|
</tileset>
|
14
assets/untitled.tiled-project
Normal file
14
assets/untitled.tiled-project
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
{
|
||||||
|
"automappingRulesFile": "",
|
||||||
|
"commands": [
|
||||||
|
],
|
||||||
|
"compatibilityVersion": 1100,
|
||||||
|
"extensionsPath": "extensions",
|
||||||
|
"folders": [
|
||||||
|
"."
|
||||||
|
],
|
||||||
|
"properties": [
|
||||||
|
],
|
||||||
|
"propertyTypes": [
|
||||||
|
]
|
||||||
|
}
|
56
assets/untitled.tiled-session
Normal file
56
assets/untitled.tiled-session
Normal file
@@ -0,0 +1,56 @@
|
|||||||
|
{
|
||||||
|
"Map/SizeTest": {
|
||||||
|
"height": 4300,
|
||||||
|
"width": 2
|
||||||
|
},
|
||||||
|
"activeFile": "map/untitled.tmx",
|
||||||
|
"expandedProjectPaths": [
|
||||||
|
"map",
|
||||||
|
".",
|
||||||
|
"tileset"
|
||||||
|
],
|
||||||
|
"file.lastUsedOpenFilter": "All Files (*)",
|
||||||
|
"fileStates": {
|
||||||
|
"entity/entities.tsx": {
|
||||||
|
"scaleInDock": 1,
|
||||||
|
"scaleInEditor": 1
|
||||||
|
},
|
||||||
|
"map/prarie.tsx": {
|
||||||
|
"scaleInDock": 1,
|
||||||
|
"scaleInEditor": 11
|
||||||
|
},
|
||||||
|
"map/untitled.tmx": {
|
||||||
|
"scale": 3.8326562499999994,
|
||||||
|
"selectedLayer": 0,
|
||||||
|
"viewCenter": {
|
||||||
|
"x": 169.5951730604591,
|
||||||
|
"y": 108.41045293326268
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"tileset/prarie.tsx": {
|
||||||
|
"scaleInDock": 1,
|
||||||
|
"scaleInEditor": 1
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"last.imagePath": "/home/yourwishes/htdocs/dusk/assets/tileset",
|
||||||
|
"map.lastUsedFormat": "tmx",
|
||||||
|
"map.tileHeight": 16,
|
||||||
|
"map.tileWidth": 16,
|
||||||
|
"openFiles": [
|
||||||
|
"entity/entities.tsx",
|
||||||
|
"map/untitled.tmx",
|
||||||
|
"tileset/prarie.tsx"
|
||||||
|
],
|
||||||
|
"project": "untitled.tiled-project",
|
||||||
|
"recentFiles": [
|
||||||
|
"entity/entities.tsx",
|
||||||
|
"tileset/prarie.tsx",
|
||||||
|
"map/untitled.tmx",
|
||||||
|
"map/prarie.tsx"
|
||||||
|
],
|
||||||
|
"tileset.lastUsedFormat": "tsx",
|
||||||
|
"tileset.tileSize": {
|
||||||
|
"height": 16,
|
||||||
|
"width": 16
|
||||||
|
}
|
||||||
|
}
|
@@ -1,10 +1,26 @@
|
|||||||
{
|
{
|
||||||
"activeFile": "map.tmj",
|
"Map/SizeTest": {
|
||||||
|
"height": 4300,
|
||||||
|
"width": 2
|
||||||
|
},
|
||||||
|
"activeFile": "/home/yourwishes/htdocs/dusk/assets/map/untitled.tmx",
|
||||||
"expandedProjectPaths": [
|
"expandedProjectPaths": [
|
||||||
".",
|
"templates",
|
||||||
"templates"
|
"."
|
||||||
],
|
],
|
||||||
"fileStates": {
|
"fileStates": {
|
||||||
|
"/home/yourwishes/htdocs/dusk/assets/entity/entities.tsx": {
|
||||||
|
"scaleInDock": 1,
|
||||||
|
"scaleInEditor": 1
|
||||||
|
},
|
||||||
|
"/home/yourwishes/htdocs/dusk/assets/map/untitled.tmx": {
|
||||||
|
"scale": 1.9163281249999997,
|
||||||
|
"selectedLayer": 0,
|
||||||
|
"viewCenter": {
|
||||||
|
"x": 546.8792042072649,
|
||||||
|
"y": 320.14350360797425
|
||||||
|
}
|
||||||
|
},
|
||||||
":/automap-tiles.tsx": {
|
":/automap-tiles.tsx": {
|
||||||
"scaleInDock": 1
|
"scaleInDock": 1
|
||||||
},
|
},
|
||||||
@@ -16,8 +32,8 @@
|
|||||||
"scale": 3,
|
"scale": 3,
|
||||||
"selectedLayer": 2,
|
"selectedLayer": 2,
|
||||||
"viewCenter": {
|
"viewCenter": {
|
||||||
"x": 6603.333333333333,
|
"x": 6912,
|
||||||
"y": 6846.5
|
"y": 6911.833333333333
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"minogram.tsx": {
|
"minogram.tsx": {
|
||||||
@@ -32,17 +48,20 @@
|
|||||||
"last.externalTilesetPath": "/home/yourwishes/htdocs/dusk/data",
|
"last.externalTilesetPath": "/home/yourwishes/htdocs/dusk/data",
|
||||||
"last.imagePath": "/home/yourwishes/htdocs/dusk/data",
|
"last.imagePath": "/home/yourwishes/htdocs/dusk/data",
|
||||||
"last.objectTemplatePath": "/home/yourwishes/htdocs/dusk/data/templates",
|
"last.objectTemplatePath": "/home/yourwishes/htdocs/dusk/data/templates",
|
||||||
|
"map.lastUsedFormat": "tmx",
|
||||||
"openFiles": [
|
"openFiles": [
|
||||||
"map.tmj",
|
"/home/yourwishes/htdocs/dusk/assets/map/untitled.tmx",
|
||||||
"overworld.tsx"
|
"/home/yourwishes/htdocs/dusk/assets/entity/entities.tsx"
|
||||||
],
|
],
|
||||||
"project": "map project.tiled-project",
|
"project": "map project.tiled-project",
|
||||||
"property.type": "string",
|
"property.type": "string",
|
||||||
"recentFiles": [
|
"recentFiles": [
|
||||||
"overworld.tsx",
|
"/home/yourwishes/htdocs/dusk/assets/entity/entities.tsx",
|
||||||
|
"/home/yourwishes/htdocs/dusk/assets/map/untitled.tmx",
|
||||||
"map.tmj",
|
"map.tmj",
|
||||||
"minogram.tsx",
|
"overworld.tsx",
|
||||||
"entities.tsx"
|
"entities.tsx",
|
||||||
|
"minogram.tsx"
|
||||||
],
|
],
|
||||||
"tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)",
|
"tileset.lastUsedFilter": "Tiled tileset files (*.tsx *.xml)",
|
||||||
"tileset.lastUsedFormat": "tsx",
|
"tileset.lastUsedFormat": "tsx",
|
||||||
|
@@ -455,7 +455,7 @@
|
|||||||
"nextobjectid":14,
|
"nextobjectid":14,
|
||||||
"orientation":"orthogonal",
|
"orientation":"orthogonal",
|
||||||
"renderorder":"right-down",
|
"renderorder":"right-down",
|
||||||
"tiledversion":"1.11.1",
|
"tiledversion":"1.11.2",
|
||||||
"tileheight":16,
|
"tileheight":16,
|
||||||
"tilesets":[
|
"tilesets":[
|
||||||
{
|
{
|
||||||
|
12
tools/assetstool/assetcache.py
Normal file
12
tools/assetstool/assetcache.py
Normal file
@@ -0,0 +1,12 @@
|
|||||||
|
processedAssets = {}
|
||||||
|
|
||||||
|
def assetGetCache(assetPath):
|
||||||
|
if assetPath in processedAssets:
|
||||||
|
return processedAssets[assetPath]
|
||||||
|
return None
|
||||||
|
|
||||||
|
def assetCache(assetPath, processedData):
|
||||||
|
if assetPath in processedAssets:
|
||||||
|
return processedAssets[assetPath]
|
||||||
|
processedAssets[assetPath] = processedData
|
||||||
|
return processedData
|
@@ -4,13 +4,13 @@ from processimage import processImage
|
|||||||
from processpalette import processPalette
|
from processpalette import processPalette
|
||||||
from processconfig import processConfig
|
from processconfig import processConfig
|
||||||
from processtileset import processTileset
|
from processtileset import processTileset
|
||||||
|
from processmap import processMap
|
||||||
|
|
||||||
processedAssets = []
|
processedAssets = []
|
||||||
|
|
||||||
def processAsset(asset):
|
def processAsset(asset):
|
||||||
if asset['path'] in processedAssets:
|
if asset['path'] in processedAssets:
|
||||||
return
|
return
|
||||||
|
|
||||||
processedAssets.append(asset['path'])
|
processedAssets.append(asset['path'])
|
||||||
|
|
||||||
# Handle tiled tilesets
|
# Handle tiled tilesets
|
||||||
@@ -23,6 +23,8 @@ def processAsset(asset):
|
|||||||
return processConfig(asset)
|
return processConfig(asset)
|
||||||
elif t == 'tileset':
|
elif t == 'tileset':
|
||||||
return processTileset(asset)
|
return processTileset(asset)
|
||||||
|
elif t == 'map':
|
||||||
|
return processMap(asset)
|
||||||
else:
|
else:
|
||||||
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
|
print(f"Error: Unknown asset type '{asset['type']}' for path '{asset['path']}'")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
@@ -2,9 +2,14 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
from args import args
|
from args import args
|
||||||
from assethelpers import getAssetRelativePath
|
from assethelpers import getAssetRelativePath
|
||||||
|
from assetcache import assetGetCache, assetCache
|
||||||
|
|
||||||
def processConfig(asset):
|
def processConfig(asset):
|
||||||
assetPath = asset['path']
|
assetPath = asset['path']
|
||||||
|
cache = assetGetCache(assetPath)
|
||||||
|
if cache is not None:
|
||||||
|
return cache
|
||||||
|
|
||||||
print(f"Processing config: {assetPath}")
|
print(f"Processing config: {assetPath}")
|
||||||
|
|
||||||
# Takes each line, seperates it by either semicolon or newline,
|
# Takes each line, seperates it by either semicolon or newline,
|
||||||
@@ -41,4 +46,4 @@ def processConfig(asset):
|
|||||||
outConfig = {
|
outConfig = {
|
||||||
"files": [ outputFilePath ],
|
"files": [ outputFilePath ],
|
||||||
}
|
}
|
||||||
return outConfig
|
return assetCache(assetPath, outConfig)
|
@@ -4,24 +4,32 @@ from PIL import Image
|
|||||||
from processpalette import extractPaletteFromImage, palettes
|
from processpalette import extractPaletteFromImage, palettes
|
||||||
from args import args
|
from args import args
|
||||||
from assethelpers import getAssetRelativePath
|
from assethelpers import getAssetRelativePath
|
||||||
|
from assetcache import assetGetCache, assetCache
|
||||||
|
|
||||||
images = []
|
images = []
|
||||||
|
|
||||||
def processImage(asset):
|
def processImage(asset):
|
||||||
|
cache = assetGetCache(asset['path'])
|
||||||
|
if cache is not None:
|
||||||
|
return cache
|
||||||
|
|
||||||
type = None
|
type = None
|
||||||
if 'type' in asset['options']:
|
if 'type' in asset['options']:
|
||||||
type = asset['options'].get('type', 'PALETTIZED').upper()
|
type = asset['options'].get('type', 'PALETTIZED').upper()
|
||||||
|
|
||||||
if type == 'PALETTIZED' or type is None:
|
if type == 'PALETTIZED' or type is None:
|
||||||
return processPalettizedImage(asset)
|
return assetCache(asset['path'], processPalettizedImage(asset))
|
||||||
elif type == 'ALPHA':
|
elif type == 'ALPHA':
|
||||||
return processAlphaImage(asset)
|
return assetCache(asset['path'], processAlphaImage(asset))
|
||||||
else:
|
else:
|
||||||
print(f"Error: Unknown image type {type} for asset {asset['path']}")
|
print(f"Error: Unknown image type {type} for asset {asset['path']}")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
|
|
||||||
def processPalettizedImage(asset):
|
def processPalettizedImage(asset):
|
||||||
assetPath = asset['path']
|
assetPath = asset['path']
|
||||||
|
cache = assetGetCache(assetPath)
|
||||||
|
if cache is not None:
|
||||||
|
return cache
|
||||||
|
|
||||||
image = Image.open(assetPath)
|
image = Image.open(assetPath)
|
||||||
imagePalette = extractPaletteFromImage(image)
|
imagePalette = extractPaletteFromImage(image)
|
||||||
@@ -72,10 +80,14 @@ def processPalettizedImage(asset):
|
|||||||
'width': image.width,
|
'width': image.width,
|
||||||
'height': image.height,
|
'height': image.height,
|
||||||
}
|
}
|
||||||
return outImage
|
return assetCache(assetPath, outImage)
|
||||||
|
|
||||||
def processAlphaImage(asset):
|
def processAlphaImage(asset):
|
||||||
assetPath = asset['path']
|
assetPath = asset['path']
|
||||||
|
cache = assetGetCache(assetPath)
|
||||||
|
if cache is not None:
|
||||||
|
return cache
|
||||||
|
|
||||||
print(f"Processing alpha image: {assetPath}")
|
print(f"Processing alpha image: {assetPath}")
|
||||||
|
|
||||||
data = bytearray()
|
data = bytearray()
|
||||||
@@ -101,4 +113,4 @@ def processAlphaImage(asset):
|
|||||||
'width': image.width,
|
'width': image.width,
|
||||||
'height': image.height,
|
'height': image.height,
|
||||||
}
|
}
|
||||||
return outImage
|
return assetCache(assetPath, outImage)
|
164
tools/assetstool/processmap.py
Normal file
164
tools/assetstool/processmap.py
Normal file
@@ -0,0 +1,164 @@
|
|||||||
|
import sys
|
||||||
|
import os
|
||||||
|
from args import args
|
||||||
|
from xml.etree import ElementTree as ET
|
||||||
|
from processtileset import processTileset
|
||||||
|
from assetcache import assetCache, assetGetCache
|
||||||
|
from assethelpers import getAssetRelativePath
|
||||||
|
|
||||||
|
def processMap(asset):
|
||||||
|
cache = assetGetCache(asset['path'])
|
||||||
|
if cache is not None:
|
||||||
|
return cache
|
||||||
|
|
||||||
|
# Load the TMX file
|
||||||
|
tree = ET.parse(asset['path'])
|
||||||
|
root = tree.getroot()
|
||||||
|
|
||||||
|
# Root needs to be "map" element.
|
||||||
|
if root.tag != 'map':
|
||||||
|
print(f"Error: TMX file {asset['path']} does not have a <map> root element")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Root needs to be orientation="orthogonal"
|
||||||
|
if 'orientation' not in root.attrib or root.attrib['orientation'] != 'orthogonal':
|
||||||
|
print(f"Error: TMX file {asset['path']} does not have orientation='orthogonal'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Extract width, height, tilewidth, tileheight attributes
|
||||||
|
if 'width' not in root.attrib or 'height' not in root.attrib or 'tilewidth' not in root.attrib or 'tileheight' not in root.attrib:
|
||||||
|
print(f"Error: TMX file {asset['path']} is missing required attributes (width, height, tilewidth, tileheight)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
mapWidth = int(root.attrib['width'])
|
||||||
|
mapHeight = int(root.attrib['height'])
|
||||||
|
tileWidth = int(root.attrib['tilewidth'])
|
||||||
|
tileHeight = int(root.attrib['tileheight'])
|
||||||
|
|
||||||
|
# Find all tileset elements
|
||||||
|
tilesets = []
|
||||||
|
for tilesetElement in root.findall('tileset'):
|
||||||
|
# Tileset must have a source attribute
|
||||||
|
if 'source' not in tilesetElement.attrib:
|
||||||
|
print(f"Error: <tileset> element in {asset['path']} is missing a source attribute")
|
||||||
|
sys.exit(1)
|
||||||
|
# Must have a firstgid attribute
|
||||||
|
if 'firstgid' not in tilesetElement.attrib:
|
||||||
|
print(f"Error: <tileset> element in {asset['path']} is missing a firstgid attribute")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
firstGid = int(tilesetElement.attrib['firstgid'])
|
||||||
|
source = tilesetElement.attrib['source']
|
||||||
|
|
||||||
|
# Get source path relative to the tmx file's working directory.
|
||||||
|
# Needs normalizing also since ".." is often used.
|
||||||
|
source = os.path.normpath(os.path.join(os.path.dirname(asset['path']), source))
|
||||||
|
tileset = processTileset({ 'path': source, 'type': 'tileset', 'options': {} })
|
||||||
|
|
||||||
|
tilesets.append({
|
||||||
|
'firstGid': firstGid,
|
||||||
|
'source': source,
|
||||||
|
'tileset': tileset
|
||||||
|
})
|
||||||
|
|
||||||
|
# Sort tilesets by firstGid, highest first
|
||||||
|
tilesets.sort(key=lambda x: x['firstGid'], reverse=True)
|
||||||
|
|
||||||
|
# Layer types
|
||||||
|
# objectLayers = [] # Not implemented
|
||||||
|
tileLayers = []
|
||||||
|
for layerElement in root.findall('layer'):
|
||||||
|
# Assume tile layer for now
|
||||||
|
# Must have id, name, width, height attributes
|
||||||
|
if 'id' not in layerElement.attrib or 'name' not in layerElement.attrib or 'width' not in layerElement.attrib or 'height' not in layerElement.attrib:
|
||||||
|
print(f"Error: <layer> element in {asset['path']} is missing required attributes (id, name, width, height)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
id = int(layerElement.attrib['id'])
|
||||||
|
name = layerElement.attrib['name']
|
||||||
|
width = int(layerElement.attrib['width'])
|
||||||
|
height = int(layerElement.attrib['height'])
|
||||||
|
|
||||||
|
# Need exactly one data element
|
||||||
|
dataElements = layerElement.findall('data')
|
||||||
|
if len(dataElements) != 1:
|
||||||
|
print(f"Error: <layer> element in {asset['path']} must have exactly one <data> child element")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# Get text, remove whitespace, split by comman and convert to int
|
||||||
|
dataElement = dataElements[0]
|
||||||
|
if dataElement.attrib.get('encoding', '') != 'csv':
|
||||||
|
print(f"Error: <data> element in {asset['path']} must have encoding='csv'")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
dataText = dataElement.text.strip()
|
||||||
|
data = [int(gid) for gid in dataText.split(',') if gid.strip().isdigit()]
|
||||||
|
|
||||||
|
# Should be exactly width * height entries
|
||||||
|
if len(data) != width * height:
|
||||||
|
print(f"Error: <data> element in {asset['path']} has {len(data)} entries but expected {width * height} (width * height)")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
tileLayers.append({
|
||||||
|
'id': id,
|
||||||
|
'name': name,
|
||||||
|
'width': width,
|
||||||
|
'height': height,
|
||||||
|
'data': data,
|
||||||
|
})
|
||||||
|
|
||||||
|
# Now we have our layers all parsed out.
|
||||||
|
data = bytearray()
|
||||||
|
data += b'drm' # Dusk RPG Map
|
||||||
|
data += mapWidth.to_bytes(4, 'little') # Map width in tiles
|
||||||
|
data += mapHeight.to_bytes(4, 'little') # Map height in tiles
|
||||||
|
data += len(tilesets).to_bytes(4, 'little') # Number of tilesets
|
||||||
|
data += len(tileLayers).to_bytes(4, 'little') # Number of layers
|
||||||
|
|
||||||
|
# For each tileset
|
||||||
|
for tileset in tilesets:
|
||||||
|
data += tileset['firstGid'].to_bytes(4, 'little') # First GID
|
||||||
|
|
||||||
|
# For each layer...
|
||||||
|
for layer in tileLayers:
|
||||||
|
for gid in layer['data']:
|
||||||
|
# Get tileset for this gid, since the tilesets are already sorted we can
|
||||||
|
# simply find the first one that has firstGid <= gid
|
||||||
|
if gid == 0:
|
||||||
|
# Empty tile
|
||||||
|
localIndex = 0xFF
|
||||||
|
tilesetIndex = 0xFFFFFFFF
|
||||||
|
else:
|
||||||
|
for tileset in tilesets:
|
||||||
|
if gid >= tileset['firstGid']:
|
||||||
|
tilesetIndex = tilesets.index(tileset)
|
||||||
|
localIndex = gid - tileset['firstGid']
|
||||||
|
break
|
||||||
|
else:
|
||||||
|
# If no tileset was found, this is an invalid gid
|
||||||
|
print(f"Error: Invalid tile GID {gid} in {asset['path']}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
if localIndex > 255:
|
||||||
|
print(f"Error: Local tile index {localIndex} exceeds 255 in {asset['path']}")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
data += tilesetIndex.to_bytes(4, 'little') # Tileset index
|
||||||
|
data += localIndex.to_bytes(1, 'little') # Local tile index
|
||||||
|
|
||||||
|
relative = getAssetRelativePath(asset['path'])
|
||||||
|
fileNameWithoutExt = os.path.splitext(os.path.basename(asset['path']))[0]
|
||||||
|
outputFileRelative = os.path.join(os.path.dirname(relative), f"{fileNameWithoutExt}.drm")
|
||||||
|
outputFilePath = os.path.join(args.output_assets, outputFileRelative)
|
||||||
|
os.makedirs(os.path.dirname(outputFilePath), exist_ok=True)
|
||||||
|
with open(outputFilePath, "wb") as f:
|
||||||
|
f.write(data)
|
||||||
|
|
||||||
|
outMap = {
|
||||||
|
'mapPath': outputFileRelative,
|
||||||
|
'files': [ outputFilePath ],
|
||||||
|
'width': mapWidth,
|
||||||
|
'height': mapHeight,
|
||||||
|
}
|
||||||
|
|
||||||
|
return assetCache(asset['path'], outMap)
|
@@ -3,6 +3,7 @@ from PIL import Image
|
|||||||
from args import args
|
from args import args
|
||||||
import sys
|
import sys
|
||||||
import datetime
|
import datetime
|
||||||
|
from assetcache import assetCache, assetGetCache
|
||||||
|
|
||||||
palettes = []
|
palettes = []
|
||||||
|
|
||||||
@@ -22,6 +23,9 @@ def extractPaletteFromImage(image):
|
|||||||
|
|
||||||
def processPalette(asset):
|
def processPalette(asset):
|
||||||
print(f"Processing palette: {asset['path']}")
|
print(f"Processing palette: {asset['path']}")
|
||||||
|
cache = assetGetCache(asset['path'])
|
||||||
|
if cache is not None:
|
||||||
|
return cache
|
||||||
|
|
||||||
paletteIndex = len(palettes)
|
paletteIndex = len(palettes)
|
||||||
image = Image.open(asset['path'])
|
image = Image.open(asset['path'])
|
||||||
@@ -72,7 +76,7 @@ def processPalette(asset):
|
|||||||
}
|
}
|
||||||
|
|
||||||
palettes.append(palette)
|
palettes.append(palette)
|
||||||
return palette
|
return assetCache(asset['path'], palette)
|
||||||
|
|
||||||
def processPaletteList():
|
def processPaletteList():
|
||||||
data = f"// Auto-generated palette list\n"
|
data = f"// Auto-generated palette list\n"
|
||||||
|
@@ -6,6 +6,7 @@ import os
|
|||||||
import datetime
|
import datetime
|
||||||
from args import args
|
from args import args
|
||||||
from xml.etree import ElementTree
|
from xml.etree import ElementTree
|
||||||
|
from assetcache import assetGetCache, assetCache
|
||||||
|
|
||||||
def loadTilesetFromTSX(asset):
|
def loadTilesetFromTSX(asset):
|
||||||
# Load the TSX file
|
# Load the TSX file
|
||||||
@@ -99,8 +100,11 @@ def loadTilesetFromArgs(asset):
|
|||||||
}
|
}
|
||||||
|
|
||||||
def processTileset(asset):
|
def processTileset(asset):
|
||||||
print(f"Processing tileset: {asset['path']}")
|
cache = assetGetCache(asset['path'])
|
||||||
|
if cache is not None:
|
||||||
|
return cache
|
||||||
|
|
||||||
|
print(f"Processing tileset: {asset['path']}")
|
||||||
tilesetData = None
|
tilesetData = None
|
||||||
if asset['path'].endswith('.tsx'):
|
if asset['path'].endswith('.tsx'):
|
||||||
tilesetData = loadTilesetFromTSX(asset)
|
tilesetData = loadTilesetFromTSX(asset)
|
||||||
@@ -135,9 +139,9 @@ def processTileset(asset):
|
|||||||
with open(outputFile, 'w') as f:
|
with open(outputFile, 'w') as f:
|
||||||
f.write(data)
|
f.write(data)
|
||||||
|
|
||||||
outTileset = {
|
return assetCache(asset['path'], {
|
||||||
|
"files": [],
|
||||||
"image": tilesetData['image'],
|
"image": tilesetData['image'],
|
||||||
"headerFile": outputFile,
|
"headerFile": outputFile,
|
||||||
"files": tilesetData['image']['files'],
|
"files": tilesetData['image']['files'],
|
||||||
}
|
})
|
||||||
return outTileset
|
|
Reference in New Issue
Block a user