add tiled importer addon for level design
This commit is contained in:
parent
e4ba3f9907
commit
4c1b5b083d
8
addons/vnen.tiled_importer/plugin.cfg
Normal file
8
addons/vnen.tiled_importer/plugin.cfg
Normal file
@ -0,0 +1,8 @@
|
||||
config_version=3
|
||||
[plugin]
|
||||
|
||||
name="Tiled Map Importer"
|
||||
description="Importer for TileMaps and TileSets made on Tiled Map Editor"
|
||||
version="2.4"
|
||||
author="George Marques"
|
||||
script="vnen.tiled_importer.gd"
|
67
addons/vnen.tiled_importer/polygon_sorter.gd
Normal file
67
addons/vnen.tiled_importer/polygon_sorter.gd
Normal file
@ -0,0 +1,67 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018 George Marques
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
# Sorter for polygon vertices
|
||||
tool
|
||||
extends Reference
|
||||
|
||||
var center
|
||||
|
||||
# Sort the vertices of a convex polygon to clockwise order
|
||||
# Receives a PoolVector2Array and returns a new one
|
||||
func sort_polygon(vertices):
|
||||
vertices = Array(vertices)
|
||||
|
||||
var centroid = Vector2()
|
||||
var size = vertices.size()
|
||||
|
||||
for i in range(0, size):
|
||||
centroid += vertices[i]
|
||||
|
||||
centroid /= size
|
||||
|
||||
center = centroid
|
||||
vertices.sort_custom(self, "is_less")
|
||||
|
||||
return PoolVector2Array(vertices)
|
||||
|
||||
# Sorter function, determines which of the poins should come first
|
||||
func is_less(a, b):
|
||||
if a.x - center.x >= 0 and b.x - center.x < 0:
|
||||
return false
|
||||
elif a.x - center.x < 0 and b.x - center.x >= 0:
|
||||
return true
|
||||
elif a.x - center.x == 0 and b.x - center.x == 0:
|
||||
if a.y - center.y >= 0 or b.y - center.y >= 0:
|
||||
return a.y < b.y
|
||||
return a.y > b.y
|
||||
|
||||
var det = (a.x - center.x) * (b.y - center.y) - (b.x - center.x) * (a.y - center.y)
|
||||
if det > 0:
|
||||
return true
|
||||
elif det < 0:
|
||||
return false
|
||||
|
||||
var d1 = (a - center).length_squared()
|
||||
var d2 = (b - center).length_squared()
|
||||
|
||||
return d1 < d2
|
BIN
addons/vnen.tiled_importer/tiled.png
Normal file
BIN
addons/vnen.tiled_importer/tiled.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 757 B |
35
addons/vnen.tiled_importer/tiled.png.import
Normal file
35
addons/vnen.tiled_importer/tiled.png.import
Normal file
@ -0,0 +1,35 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="StreamTexture"
|
||||
path="res://.import/tiled.png-dbbabe58e4f927769540a522a2073689.stex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/vnen.tiled_importer/tiled.png"
|
||||
dest_files=[ "res://.import/tiled.png-dbbabe58e4f927769540a522a2073689.stex" ]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_mode=0
|
||||
compress/bptc_ldr=0
|
||||
compress/normal_map=0
|
||||
flags/repeat=0
|
||||
flags/filter=false
|
||||
flags/mipmaps=false
|
||||
flags/anisotropic=false
|
||||
flags/srgb=2
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/HDR_as_SRGB=false
|
||||
process/invert_color=false
|
||||
process/normal_map_invert_y=false
|
||||
stream=false
|
||||
size_limit=0
|
||||
detect_3d=false
|
||||
svg/scale=1.0
|
148
addons/vnen.tiled_importer/tiled_import_plugin.gd
Normal file
148
addons/vnen.tiled_importer/tiled_import_plugin.gd
Normal file
@ -0,0 +1,148 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018 George Marques
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
enum { PRESET_DEFAULT, PRESET_PIXEL_ART }
|
||||
|
||||
const TiledMapReader = preload("tiled_map_reader.gd")
|
||||
|
||||
func get_importer_name():
|
||||
return "vnen.tiled_importer"
|
||||
|
||||
func get_visible_name():
|
||||
return "Scene from Tiled"
|
||||
|
||||
func get_recognized_extensions():
|
||||
if ProjectSettings.get_setting("tiled_importer/enable_json_format"):
|
||||
return ["json", "tmx"]
|
||||
else:
|
||||
return ["tmx"]
|
||||
|
||||
func get_save_extension():
|
||||
return "scn"
|
||||
|
||||
func get_priority():
|
||||
return 1
|
||||
|
||||
func get_import_order():
|
||||
return 101
|
||||
|
||||
func get_resource_type():
|
||||
return "PackedScene"
|
||||
|
||||
func get_preset_count():
|
||||
return 2
|
||||
|
||||
func get_preset_name(preset):
|
||||
match preset:
|
||||
PRESET_DEFAULT: return "Default"
|
||||
PRESET_PIXEL_ART: return "Pixel Art"
|
||||
|
||||
func get_import_options(preset):
|
||||
return [
|
||||
{
|
||||
"name": "custom_properties",
|
||||
"default_value": true
|
||||
},
|
||||
{
|
||||
"name": "tile_metadata",
|
||||
"default_value": false
|
||||
},
|
||||
{
|
||||
"name": "uv_clip",
|
||||
"default_value": true
|
||||
},
|
||||
{
|
||||
"name": "y_sort",
|
||||
"default_value": true
|
||||
},
|
||||
{
|
||||
"name": "image_flags",
|
||||
"default_value": 0 if preset == PRESET_PIXEL_ART else Texture.FLAGS_DEFAULT,
|
||||
"property_hint": PROPERTY_HINT_FLAGS,
|
||||
"hint_string": "Mipmaps,Repeat,Filter,Anisotropic,sRGB,Mirrored Repeat"
|
||||
},
|
||||
{
|
||||
"name": "collision_layer",
|
||||
"default_value": 1,
|
||||
"property_hint": PROPERTY_HINT_LAYERS_2D_PHYSICS
|
||||
},
|
||||
{
|
||||
"name": "collision_mask",
|
||||
"default_value": 1,
|
||||
"property_hint": PROPERTY_HINT_LAYERS_2D_PHYSICS
|
||||
},
|
||||
{
|
||||
"name": "embed_internal_images",
|
||||
"default_value": true if preset == PRESET_PIXEL_ART else false
|
||||
},
|
||||
{
|
||||
"name": "save_tiled_properties",
|
||||
"default_value": false
|
||||
},
|
||||
{
|
||||
"name": "add_background",
|
||||
"default_value": true
|
||||
},
|
||||
{
|
||||
"name": "post_import_script",
|
||||
"default_value": "",
|
||||
"property_hint": PROPERTY_HINT_FILE,
|
||||
"hint_string": "*.gd;GDScript"
|
||||
}
|
||||
]
|
||||
|
||||
func get_option_visibility(option, options):
|
||||
return true
|
||||
|
||||
func import(source_file, save_path, options, r_platform_variants, r_gen_files):
|
||||
var map_reader = TiledMapReader.new()
|
||||
|
||||
var scene = map_reader.build(source_file, options)
|
||||
|
||||
if typeof(scene) != TYPE_OBJECT:
|
||||
# Error happened
|
||||
return scene
|
||||
|
||||
# Post imports script
|
||||
if not options.post_import_script.empty():
|
||||
var script = load(options.post_import_script)
|
||||
if not script or not script is GDScript:
|
||||
printerr("Post import script is not a GDScript.")
|
||||
return ERR_INVALID_PARAMETER
|
||||
|
||||
script = script.new()
|
||||
if not script.has_method("post_import"):
|
||||
printerr("Post import script does not have a 'post_import' method.")
|
||||
return ERR_INVALID_PARAMETER
|
||||
|
||||
scene = script.post_import(scene)
|
||||
|
||||
if not scene or not scene is Node2D:
|
||||
printerr("Invalid scene returned from post import script.")
|
||||
return ERR_INVALID_DATA
|
||||
|
||||
var packed_scene = PackedScene.new()
|
||||
packed_scene.pack(scene)
|
||||
return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], packed_scene)
|
1412
addons/vnen.tiled_importer/tiled_map_reader.gd
Normal file
1412
addons/vnen.tiled_importer/tiled_map_reader.gd
Normal file
File diff suppressed because it is too large
Load Diff
121
addons/vnen.tiled_importer/tiled_tileset_import_plugin.gd
Normal file
121
addons/vnen.tiled_importer/tiled_tileset_import_plugin.gd
Normal file
@ -0,0 +1,121 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018 George Marques
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
enum { PRESET_DEFAULT, PRESET_PIXEL_ART }
|
||||
|
||||
const TiledMapReader = preload("tiled_map_reader.gd")
|
||||
|
||||
func get_importer_name():
|
||||
return "vnen.tiled_tileset_importer"
|
||||
|
||||
func get_visible_name():
|
||||
return "TileSet from Tiled"
|
||||
|
||||
func get_recognized_extensions():
|
||||
if ProjectSettings.get_setting("tiled_importer/enable_json_format"):
|
||||
return ["json", "tsx"]
|
||||
else:
|
||||
return ["tsx"]
|
||||
|
||||
func get_save_extension():
|
||||
return "res"
|
||||
|
||||
func get_import_order():
|
||||
return 100
|
||||
|
||||
func get_resource_type():
|
||||
return "TileSet"
|
||||
|
||||
func get_preset_count():
|
||||
return 2
|
||||
|
||||
func get_preset_name(preset):
|
||||
match preset:
|
||||
PRESET_DEFAULT: return "Default"
|
||||
PRESET_PIXEL_ART: return "Pixel Art"
|
||||
|
||||
func get_import_options(preset):
|
||||
return [
|
||||
{
|
||||
"name": "custom_properties",
|
||||
"default_value": true
|
||||
},
|
||||
{
|
||||
"name": "tile_metadata",
|
||||
"default_value": false
|
||||
},
|
||||
{
|
||||
"name": "image_flags",
|
||||
"default_value": 0 if preset == PRESET_PIXEL_ART else Texture.FLAGS_DEFAULT,
|
||||
"property_hint": PROPERTY_HINT_FLAGS,
|
||||
"hint_string": "Mipmaps,Repeat,Filter,Anisotropic,sRGB,Mirrored Repeat"
|
||||
},
|
||||
{
|
||||
"name": "embed_internal_images",
|
||||
"default_value": true if preset == PRESET_PIXEL_ART else false
|
||||
},
|
||||
{
|
||||
"name": "save_tiled_properties",
|
||||
"default_value": false
|
||||
},
|
||||
{
|
||||
"name": "post_import_script",
|
||||
"default_value": "",
|
||||
"property_hint": PROPERTY_HINT_FILE,
|
||||
"hint_string": "*.gd;GDScript"
|
||||
}
|
||||
]
|
||||
|
||||
func get_option_visibility(option, options):
|
||||
return true
|
||||
|
||||
func import(source_file, save_path, options, r_platform_variants, r_gen_files):
|
||||
var map_reader = TiledMapReader.new()
|
||||
|
||||
var tileset = map_reader.build_tileset(source_file, options)
|
||||
|
||||
if typeof(tileset) != TYPE_OBJECT:
|
||||
# Error happened
|
||||
return tileset
|
||||
|
||||
# Post imports script
|
||||
if not options.post_import_script.empty():
|
||||
var script = load(options.post_import_script)
|
||||
if not script or not script is GDScript:
|
||||
printerr("Post import script is not a GDScript.")
|
||||
return ERR_INVALID_PARAMETER
|
||||
|
||||
script = script.new()
|
||||
if not script.has_method("post_import"):
|
||||
printerr("Post import script does not have a 'post_import' method.")
|
||||
return ERR_INVALID_PARAMETER
|
||||
|
||||
tileset = script.post_import(tileset)
|
||||
|
||||
if not tileset or not tileset is TileSet:
|
||||
printerr("Invalid TileSet returned from post import script.")
|
||||
return ERR_INVALID_DATA
|
||||
|
||||
return ResourceSaver.save("%s.%s" % [save_path, get_save_extension()], tileset)
|
571
addons/vnen.tiled_importer/tiled_xml_to_dict.gd
Normal file
571
addons/vnen.tiled_importer/tiled_xml_to_dict.gd
Normal file
@ -0,0 +1,571 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018 George Marques
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
tool
|
||||
extends Reference
|
||||
|
||||
# Reads a TMX file from a path and return a Dictionary with the same structure
|
||||
# as the JSON map format
|
||||
# Returns an error code if failed
|
||||
func read_tmx(path):
|
||||
var parser = XMLParser.new()
|
||||
var err = parser.open(path)
|
||||
if err != OK:
|
||||
printerr("Error opening TMX file '%s'." % [path])
|
||||
return err
|
||||
|
||||
while parser.get_node_type() != XMLParser.NODE_ELEMENT:
|
||||
err = parser.read()
|
||||
if err != OK:
|
||||
printerr("Error parsing TMX file '%s' (around line %d)." % [path, parser.get_current_line()])
|
||||
return err
|
||||
|
||||
if parser.get_node_name().to_lower() != "map":
|
||||
printerr("Error parsing TMX file '%s'. Expected 'map' element.")
|
||||
return ERR_INVALID_DATA
|
||||
|
||||
var data = attributes_to_dict(parser)
|
||||
if not "infinite" in data:
|
||||
data.infinite = false
|
||||
data.type = "map"
|
||||
data.tilesets = []
|
||||
data.layers = []
|
||||
|
||||
err = parser.read()
|
||||
if err != OK:
|
||||
printerr("Error parsing TMX file '%s' (around line %d)." % [path, parser.get_current_line()])
|
||||
return err
|
||||
|
||||
while err == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name() == "map":
|
||||
break
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "tileset":
|
||||
# Empty element means external tileset
|
||||
if not parser.is_empty():
|
||||
var tileset = parse_tileset(parser)
|
||||
if typeof(tileset) != TYPE_DICTIONARY:
|
||||
# Error happened
|
||||
return err
|
||||
data.tilesets.push_back(tileset)
|
||||
else:
|
||||
var tileset_data = attributes_to_dict(parser)
|
||||
if not "source" in tileset_data:
|
||||
printerr("Error parsing TMX file '%s'. Missing tileset source (around line %d)." % [path, parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
data.tilesets.push_back(tileset_data)
|
||||
|
||||
elif parser.get_node_name() == "layer":
|
||||
var layer = parse_tile_layer(parser, data.infinite)
|
||||
if typeof(layer) != TYPE_DICTIONARY:
|
||||
printerr("Error parsing TMX file '%s'. Invalid tile layer data (around line %d)." % [path, parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
data.layers.push_back(layer)
|
||||
|
||||
elif parser.get_node_name() == "imagelayer":
|
||||
var layer = parse_image_layer(parser)
|
||||
if typeof(layer) != TYPE_DICTIONARY:
|
||||
printerr("Error parsing TMX file '%s'. Invalid image layer data (around line %d)." % [path, parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
data.layers.push_back(layer)
|
||||
|
||||
elif parser.get_node_name() == "objectgroup":
|
||||
var layer = parse_object_layer(parser)
|
||||
if typeof(layer) != TYPE_DICTIONARY:
|
||||
printerr("Error parsing TMX file '%s'. Invalid object layer data (around line %d)." % [path, parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
data.layers.push_back(layer)
|
||||
|
||||
elif parser.get_node_name() == "group":
|
||||
var layer = parse_group_layer(parser, data.infinite)
|
||||
if typeof(layer) != TYPE_DICTIONARY:
|
||||
printerr("Error parsing TMX file '%s'. Invalid group layer data (around line %d)." % [path, parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
data.layers.push_back(layer)
|
||||
|
||||
elif parser.get_node_name() == "properties":
|
||||
var prop_data = parse_properties(parser)
|
||||
if typeof(prop_data) == TYPE_STRING:
|
||||
return prop_data
|
||||
|
||||
data.properties = prop_data.properties
|
||||
data.propertytypes = prop_data.propertytypes
|
||||
|
||||
err = parser.read()
|
||||
|
||||
return data
|
||||
|
||||
# Reads a TSX and return a tileset dictionary
|
||||
# Returns an error code if fails
|
||||
func read_tsx(path):
|
||||
var parser = XMLParser.new()
|
||||
var err = parser.open(path)
|
||||
if err != OK:
|
||||
printerr("Error opening TSX file '%s'." % [path])
|
||||
return err
|
||||
|
||||
while parser.get_node_type() != XMLParser.NODE_ELEMENT:
|
||||
err = parser.read()
|
||||
if err != OK:
|
||||
printerr("Error parsing TSX file '%s' (around line %d)." % [path, parser.get_current_line()])
|
||||
return err
|
||||
|
||||
if parser.get_node_name().to_lower() != "tileset":
|
||||
printerr("Error parsing TMX file '%s'. Expected 'map' element.")
|
||||
return ERR_INVALID_DATA
|
||||
|
||||
var tileset = parse_tileset(parser)
|
||||
|
||||
return tileset
|
||||
|
||||
# Parses a tileset element from the XML and return a dictionary
|
||||
# Return an error code if fails
|
||||
func parse_tileset(parser):
|
||||
var err = OK
|
||||
var data = attributes_to_dict(parser)
|
||||
data.tiles = {}
|
||||
|
||||
err = parser.read()
|
||||
while err == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name() == "tileset":
|
||||
break
|
||||
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "tile":
|
||||
var attr = attributes_to_dict(parser)
|
||||
var tile_data = parse_tile_data(parser)
|
||||
if typeof(tile_data) != TYPE_DICTIONARY:
|
||||
# Error happened
|
||||
return tile_data
|
||||
if "properties" in tile_data and "propertytypes" in tile_data:
|
||||
if not "tileproperties" in data:
|
||||
data.tileproperties = {}
|
||||
data.tilepropertytypes = {}
|
||||
data.tileproperties[str(attr.id)] = tile_data.properties
|
||||
data.tilepropertytypes[str(attr.id)] = tile_data.propertytypes
|
||||
tile_data.erase("tileproperties")
|
||||
tile_data.erase("tilepropertytypes")
|
||||
data.tiles[str(attr.id)] = tile_data
|
||||
|
||||
elif parser.get_node_name() == "image":
|
||||
var attr = attributes_to_dict(parser)
|
||||
if not "source" in attr:
|
||||
printerr("Error loading image tag. No source attribute found (around line %d)." % [parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
data.image = attr.source
|
||||
if "width" in attr:
|
||||
data.imagewidth = attr.width
|
||||
if "height" in attr:
|
||||
data.imageheight = attr.height
|
||||
|
||||
elif parser.get_node_name() == "properties":
|
||||
var prop_data = parse_properties(parser)
|
||||
if typeof(prop_data) != TYPE_DICTIONARY:
|
||||
# Error happened
|
||||
return prop_data
|
||||
|
||||
data.properties = prop_data.properties
|
||||
data.propertytypes = prop_data.propertytypes
|
||||
|
||||
err = parser.read()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# Parses the data of a single tile from the XML and return a dictionary
|
||||
# Returns an error code if fails
|
||||
func parse_tile_data(parser):
|
||||
var err = OK
|
||||
var data = {}
|
||||
var obj_group = {}
|
||||
if parser.is_empty():
|
||||
return data
|
||||
|
||||
err = parser.read()
|
||||
while err == OK:
|
||||
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name() == "tile":
|
||||
return data
|
||||
elif parser.get_node_name() == "objectgroup":
|
||||
data.objectgroup = obj_group
|
||||
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "image":
|
||||
# If there are multiple images in one tile we only use the last one.
|
||||
var attr = attributes_to_dict(parser)
|
||||
if not "source" in attr:
|
||||
printerr("Error loading image tag. No source attribute found (around line %d)." % [parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
data.image = attr.source
|
||||
data.imagewidth = attr.width
|
||||
data.imageheight = attr.height
|
||||
|
||||
elif parser.get_node_name() == "objectgroup":
|
||||
obj_group = attributes_to_dict(parser)
|
||||
for attr in ["width", "height", "offsetx", "offsety"]:
|
||||
if not attr in obj_group:
|
||||
data[attr] = 0
|
||||
if not "opacity" in data:
|
||||
data.opacity = 1
|
||||
if not "visible" in data:
|
||||
data.visible = true
|
||||
if parser.is_empty():
|
||||
data.objectgroup = obj_group
|
||||
|
||||
elif parser.get_node_name() == "object":
|
||||
if not "objects" in obj_group:
|
||||
obj_group.objects = []
|
||||
var obj = parse_object(parser)
|
||||
if typeof(obj) != TYPE_DICTIONARY:
|
||||
# Error happened
|
||||
return obj
|
||||
obj_group.objects.push_back(obj)
|
||||
|
||||
elif parser.get_node_name() == "properties":
|
||||
var prop_data = parse_properties(parser)
|
||||
data["properties"] = prop_data.properties
|
||||
data["propertytypes"] = prop_data.propertytypes
|
||||
|
||||
elif parser.get_node_name() == "animation":
|
||||
var frame_list = []
|
||||
var err2 = parser.read()
|
||||
while err2 == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "frame":
|
||||
var frame = {"tileid": 0, "duration": 0}
|
||||
for i in parser.get_attribute_count():
|
||||
if parser.get_attribute_name(i) == "tileid":
|
||||
frame["tileid"] = parser.get_attribute_value(i)
|
||||
if parser.get_attribute_name(i) == "duration":
|
||||
frame["duration"] = parser.get_attribute_value(i)
|
||||
frame_list.push_back(frame)
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name() == "animation":
|
||||
break
|
||||
err2 = parser.read()
|
||||
|
||||
data["animation"] = frame_list
|
||||
|
||||
err = parser.read()
|
||||
|
||||
return data
|
||||
|
||||
# Parses the data of a single object from the XML and return a dictionary
|
||||
# Returns an error code if fails
|
||||
static func parse_object(parser):
|
||||
var err = OK
|
||||
var data = attributes_to_dict(parser)
|
||||
|
||||
if not parser.is_empty():
|
||||
err = parser.read()
|
||||
while err == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name() == "object":
|
||||
break
|
||||
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "properties":
|
||||
var prop_data = parse_properties(parser)
|
||||
data["properties"] = prop_data.properties
|
||||
data["propertytypes"] = prop_data.propertytypes
|
||||
|
||||
elif parser.get_node_name() == "point":
|
||||
data.point = true
|
||||
|
||||
elif parser.get_node_name() == "ellipse":
|
||||
data.ellipse = true
|
||||
|
||||
elif parser.get_node_name() == "polygon" or parser.get_node_name() == "polyline":
|
||||
var points = []
|
||||
var points_raw = parser.get_named_attribute_value("points").split(" ", false, 0)
|
||||
|
||||
for pr in points_raw:
|
||||
points.push_back({
|
||||
"x": float(pr.split(",")[0]),
|
||||
"y": float(pr.split(",")[1]),
|
||||
})
|
||||
|
||||
data[parser.get_node_name()] = points
|
||||
|
||||
err = parser.read()
|
||||
|
||||
return data
|
||||
|
||||
|
||||
# Parses a tile layer from the XML and return a dictionary
|
||||
# Returns an error code if fails
|
||||
func parse_tile_layer(parser, infinite):
|
||||
var err = OK
|
||||
var data = attributes_to_dict(parser)
|
||||
data.type = "tilelayer"
|
||||
if not "x" in data:
|
||||
data.x = 0
|
||||
if not "y" in data:
|
||||
data.y = 0
|
||||
if infinite:
|
||||
data.chunks = []
|
||||
else:
|
||||
data.data = []
|
||||
|
||||
var current_chunk = null
|
||||
var encoding = ""
|
||||
|
||||
if not parser.is_empty():
|
||||
err = parser.read()
|
||||
|
||||
while err == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name() == "layer":
|
||||
break
|
||||
elif parser.get_node_name() == "chunk":
|
||||
data.chunks.push_back(current_chunk)
|
||||
current_chunk = null
|
||||
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "data":
|
||||
var attr = attributes_to_dict(parser)
|
||||
|
||||
if "compression" in attr:
|
||||
data.compression = attr.compression
|
||||
|
||||
if "encoding" in attr:
|
||||
encoding = attr.encoding
|
||||
if attr.encoding != "csv":
|
||||
data.encoding = attr.encoding
|
||||
|
||||
if not infinite:
|
||||
err = parser.read()
|
||||
if err != OK:
|
||||
return err
|
||||
|
||||
if attr.encoding != "csv":
|
||||
data.data = parser.get_node_data().strip_edges()
|
||||
else:
|
||||
var csv = parser.get_node_data().split(",", false)
|
||||
|
||||
for v in csv:
|
||||
data.data.push_back(int(v.strip_edges()))
|
||||
|
||||
elif parser.get_node_name() == "tile":
|
||||
var gid = int(parser.get_named_attribute_value_safe("gid"))
|
||||
if infinite:
|
||||
current_chunk.data.push_back(gid)
|
||||
else:
|
||||
data.data.push_back(gid)
|
||||
|
||||
elif parser.get_node_name() == "chunk":
|
||||
current_chunk = attributes_to_dict(parser)
|
||||
current_chunk.data = []
|
||||
if encoding != "":
|
||||
err = parser.read()
|
||||
if err != OK:
|
||||
return err
|
||||
if encoding != "csv":
|
||||
current_chunk.data = parser.get_node_data().strip_edges()
|
||||
else:
|
||||
var csv = parser.get_node_data().split(",", false)
|
||||
for v in csv:
|
||||
current_chunk.data.push_back(int(v.strip_edges()))
|
||||
|
||||
elif parser.get_node_name() == "properties":
|
||||
var prop_data = parse_properties(parser)
|
||||
if typeof(prop_data) == TYPE_STRING:
|
||||
return prop_data
|
||||
|
||||
data.properties = prop_data.properties
|
||||
data.propertytypes = prop_data.propertytypes
|
||||
|
||||
err = parser.read()
|
||||
|
||||
return data
|
||||
|
||||
# Parses an object layer from the XML and return a dictionary
|
||||
# Returns an error code if fails
|
||||
func parse_object_layer(parser):
|
||||
var err = OK
|
||||
var data = attributes_to_dict(parser)
|
||||
data.type = "objectgroup"
|
||||
data.objects = []
|
||||
|
||||
if not parser.is_empty():
|
||||
err = parser.read()
|
||||
while err == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name() == "objectgroup":
|
||||
break
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "object":
|
||||
data.objects.push_back(parse_object(parser))
|
||||
elif parser.get_node_name() == "properties":
|
||||
var prop_data = parse_properties(parser)
|
||||
if typeof(prop_data) != TYPE_DICTIONARY:
|
||||
# Error happened
|
||||
return prop_data
|
||||
data.properties = prop_data.properties
|
||||
data.propertytypes = prop_data.propertytypes
|
||||
|
||||
err = parser.read()
|
||||
|
||||
return data
|
||||
|
||||
# Parses an image layer from the XML and return a dictionary
|
||||
# Returns an error code if fails
|
||||
func parse_image_layer(parser):
|
||||
var err = OK
|
||||
var data = attributes_to_dict(parser)
|
||||
data.type = "imagelayer"
|
||||
data.image = ""
|
||||
|
||||
if not parser.is_empty():
|
||||
err = parser.read()
|
||||
|
||||
while err == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name().to_lower() == "imagelayer":
|
||||
break
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name().to_lower() == "image":
|
||||
var image = attributes_to_dict(parser)
|
||||
if not image.has("source"):
|
||||
printerr("Missing source attribute in imagelayer (around line %d)." % [parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
data.image = image.source
|
||||
|
||||
elif parser.get_node_name() == "properties":
|
||||
var prop_data = parse_properties(parser)
|
||||
if typeof(prop_data) != TYPE_DICTIONARY:
|
||||
# Error happened
|
||||
return prop_data
|
||||
data.properties = prop_data.properties
|
||||
data.propertytypes = prop_data.propertytypes
|
||||
|
||||
err = parser.read()
|
||||
|
||||
return data
|
||||
|
||||
# Parses a group layer from the XML and return a dictionary
|
||||
# Returns an error code if fails
|
||||
func parse_group_layer(parser, infinite):
|
||||
var err = OK
|
||||
var result = attributes_to_dict(parser)
|
||||
result.type = "group"
|
||||
result.layers = []
|
||||
|
||||
if not parser.is_empty():
|
||||
err = parser.read()
|
||||
|
||||
while err == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name().to_lower() == "group":
|
||||
break
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "layer":
|
||||
var layer = parse_tile_layer(parser, infinite)
|
||||
if typeof(layer) != TYPE_DICTIONARY:
|
||||
printerr("Error parsing TMX file. Invalid tile layer data (around line %d)." % [parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
result.layers.push_back(layer)
|
||||
|
||||
elif parser.get_node_name() == "imagelayer":
|
||||
var layer = parse_image_layer(parser)
|
||||
if typeof(layer) != TYPE_DICTIONARY:
|
||||
printerr("Error parsing TMX file. Invalid image layer data (around line %d)." % [parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
result.layers.push_back(layer)
|
||||
|
||||
elif parser.get_node_name() == "objectgroup":
|
||||
var layer = parse_object_layer(parser)
|
||||
if typeof(layer) != TYPE_DICTIONARY:
|
||||
printerr("Error parsing TMX file. Invalid object layer data (around line %d)." % [parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
result.layers.push_back(layer)
|
||||
|
||||
elif parser.get_node_name() == "group":
|
||||
var layer = parse_group_layer(parser, infinite)
|
||||
if typeof(layer) != TYPE_DICTIONARY:
|
||||
printerr("Error parsing TMX file. Invalid group layer data (around line %d)." % [parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
result.layers.push_back(layer)
|
||||
|
||||
elif parser.get_node_name() == "properties":
|
||||
var prop_data = parse_properties(parser)
|
||||
if typeof(prop_data) == TYPE_STRING:
|
||||
return prop_data
|
||||
|
||||
result.properties = prop_data.properties
|
||||
result.propertytypes = prop_data.propertytypes
|
||||
|
||||
err = parser.read()
|
||||
return result
|
||||
|
||||
# Parses properties data from the XML and return a dictionary
|
||||
# Returns an error code if fails
|
||||
static func parse_properties(parser):
|
||||
var err = OK
|
||||
var data = {
|
||||
"properties": {},
|
||||
"propertytypes": {},
|
||||
}
|
||||
|
||||
if not parser.is_empty():
|
||||
err = parser.read()
|
||||
|
||||
while err == OK:
|
||||
if parser.get_node_type() == XMLParser.NODE_ELEMENT_END:
|
||||
if parser.get_node_name() == "properties":
|
||||
break
|
||||
elif parser.get_node_type() == XMLParser.NODE_ELEMENT:
|
||||
if parser.get_node_name() == "property":
|
||||
var prop_data = attributes_to_dict(parser)
|
||||
if not (prop_data.has("name") and prop_data.has("value")):
|
||||
printerr("Missing information in custom properties (around line %d)." % [parser.get_current_line()])
|
||||
return ERR_INVALID_DATA
|
||||
|
||||
data.properties[prop_data.name] = prop_data.value
|
||||
if prop_data.has("type"):
|
||||
data.propertytypes[prop_data.name] = prop_data.type
|
||||
else:
|
||||
data.propertytypes[prop_data.name] = "string"
|
||||
|
||||
err = parser.read()
|
||||
|
||||
return data
|
||||
|
||||
# Reads the attributes of the current element and return them as a dictionary
|
||||
static func attributes_to_dict(parser):
|
||||
var data = {}
|
||||
for i in range(parser.get_attribute_count()):
|
||||
var attr = parser.get_attribute_name(i)
|
||||
var val = parser.get_attribute_value(i)
|
||||
if val.is_valid_integer():
|
||||
val = int(val)
|
||||
elif val.is_valid_float():
|
||||
val = float(val)
|
||||
elif val == "true":
|
||||
val = true
|
||||
elif val == "false":
|
||||
val = false
|
||||
data[attr] = val
|
||||
return data
|
45
addons/vnen.tiled_importer/vnen.tiled_importer.gd
Normal file
45
addons/vnen.tiled_importer/vnen.tiled_importer.gd
Normal file
@ -0,0 +1,45 @@
|
||||
# The MIT License (MIT)
|
||||
#
|
||||
# Copyright (c) 2018 George Marques
|
||||
#
|
||||
# Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
# of this software and associated documentation files (the "Software"), to deal
|
||||
# in the Software without restriction, including without limitation the rights
|
||||
# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
# copies of the Software, and to permit persons to whom the Software is
|
||||
# furnished to do so, subject to the following conditions:
|
||||
#
|
||||
# The above copyright notice and this permission notice shall be included in all
|
||||
# copies or substantial portions of the Software.
|
||||
#
|
||||
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
# SOFTWARE.
|
||||
|
||||
tool
|
||||
extends EditorPlugin
|
||||
|
||||
var import_plugin = null
|
||||
var tileset_import_plugin = null
|
||||
|
||||
func get_name():
|
||||
return "Tiled Map Importer"
|
||||
|
||||
func _enter_tree():
|
||||
if not ProjectSettings.has_setting("tiled_importer/enable_json_format"):
|
||||
ProjectSettings.set_setting("tiled_importer/enable_json_format", true)
|
||||
|
||||
import_plugin = preload("tiled_import_plugin.gd").new()
|
||||
tileset_import_plugin = preload("tiled_tileset_import_plugin.gd").new()
|
||||
add_import_plugin(import_plugin)
|
||||
add_import_plugin(tileset_import_plugin)
|
||||
|
||||
func _exit_tree():
|
||||
remove_import_plugin(import_plugin)
|
||||
remove_import_plugin(tileset_import_plugin)
|
||||
import_plugin = null
|
||||
tileset_import_plugin = null
|
Loading…
Reference in New Issue
Block a user