ldtk tileset import
@ -14,12 +14,13 @@ This section is a high level overview of what is being or can be worked on.
|
||||
|
||||
## Vehicle Physics Model
|
||||
|
||||
- [ ] Kinematics low-speed model.
|
||||
- [ ] Dynamics (force based) vehicle model.
|
||||
- [ ] Implement rear-wheel drive.
|
||||
- [ ] Implement four-wheel drive.
|
||||
- [ ] Implement no-grip state (wheels have lost static grip on the road).
|
||||
- [ ] Implement handbrake.
|
||||
- [ ] Kinematics low-speed model.
|
||||
- [ ] Weight shifting
|
||||
- [ ] Export parameters to be configurable in Godot editor.
|
||||
|
||||
## Audio
|
||||
@ -38,3 +39,7 @@ This section is a high level overview of what is being or can be worked on.
|
||||
## Multiplayer
|
||||
|
||||
- [ ] ?
|
||||
|
||||
## CI
|
||||
|
||||
- [ ] Build and host book
|
||||
|
5
godot/addons/ldtk-importer/README.md
Normal file
@ -0,0 +1,5 @@
|
||||
# ldtk-importer / godot-ldtk
|
||||
|
||||
[LDtk](https://ldtk.io/) importer for [Godot 4](https://godotengine.org/)
|
||||
|
||||
[Github](https://github.com/heygleeson/godot-ldtk)
|
311
godot/addons/ldtk-importer/ldtk-importer.gd
Normal file
@ -0,0 +1,311 @@
|
||||
@tool
|
||||
extends EditorImportPlugin
|
||||
|
||||
const LDTK_LATEST_VERSION = "1.5.3"
|
||||
|
||||
enum Presets {DEFAULT}
|
||||
|
||||
const Util = preload("src/util/util.gd")
|
||||
const World = preload("src/world.gd")
|
||||
const Level = preload("src/level.gd")
|
||||
const Tileset = preload("src/tileset.gd")
|
||||
const DefinitionUtil = preload("src/util/definition_util.gd")
|
||||
|
||||
#region EditorImportPlugin Overrides
|
||||
|
||||
#region Simple
|
||||
func _get_importer_name():
|
||||
return "ldtk.import"
|
||||
|
||||
func _get_visible_name():
|
||||
return "LDTK Scene"
|
||||
|
||||
func _get_priority():
|
||||
return 1.0
|
||||
|
||||
func _get_import_order():
|
||||
return IMPORT_ORDER_SCENE
|
||||
|
||||
func _get_resource_type():
|
||||
return "PackedScene"
|
||||
|
||||
func _get_recognized_extensions():
|
||||
return ["ldtk"]
|
||||
|
||||
func _get_save_extension():
|
||||
return "scn"
|
||||
|
||||
func _get_preset_count():
|
||||
return Presets.size()
|
||||
|
||||
func _get_preset_name(index):
|
||||
match index:
|
||||
Presets.DEFAULT:
|
||||
return "Default"
|
||||
_:
|
||||
return "Unknown"
|
||||
|
||||
func _get_option_visibility(path, option_name, options):
|
||||
match option_name:
|
||||
_:
|
||||
return true
|
||||
return true
|
||||
|
||||
func _can_import_threaded() -> bool:
|
||||
return false
|
||||
|
||||
#endregion
|
||||
|
||||
func _get_import_options(path, index):
|
||||
return [
|
||||
# --- World --- #
|
||||
{"name": "World", "default_value":"", "usage": PROPERTY_USAGE_GROUP},
|
||||
{
|
||||
# Group LDTKLevels in 'LDTKWorldLayer' nodes if using LDTK's WorldDepth.
|
||||
"name": "group_world_layers",
|
||||
"default_value": false,
|
||||
},
|
||||
# --- Levels --- #
|
||||
{"name": "Level", "default_value":"", "usage": PROPERTY_USAGE_GROUP},
|
||||
{
|
||||
# Save LDTKLevels as PackedScenes.
|
||||
"name": "pack_levels",
|
||||
"default_value": true,
|
||||
},
|
||||
# --- Layers --- #
|
||||
{"name": "Layer", "default_value":"", "usage": PROPERTY_USAGE_GROUP},
|
||||
{
|
||||
# Save LDTKLevels as PackedScenes.
|
||||
"name": "layers_always_visible",
|
||||
"default_value": false,
|
||||
},
|
||||
# --- Tileset --- #
|
||||
{"name": "Tileset", "default_value":"", "usage": PROPERTY_USAGE_GROUP},
|
||||
{
|
||||
# Add LDTK Custom Data to Tilesets
|
||||
"name": "tileset_custom_data",
|
||||
"default_value": false,
|
||||
},
|
||||
{
|
||||
# Create TileAtlasSources & TileMapLayers for IntGrid Layers
|
||||
"name": "integer_grid_tilesets",
|
||||
"default_value": false,
|
||||
},
|
||||
{
|
||||
# Define default texture type for TilesetAtlasSource (e.g. to apply normal maps to tilesets after import)
|
||||
"name": "atlas_texture_type",
|
||||
"default_value": 0,
|
||||
"property_hint": PROPERTY_HINT_ENUM,
|
||||
"hint_string": "CompressedTexture2D,CanvasTexture",
|
||||
},
|
||||
# --- Entities --- #
|
||||
{"name": "Entity", "default_value":"", "usage": PROPERTY_USAGE_GROUP},
|
||||
{
|
||||
#
|
||||
"name": "resolve_entityrefs",
|
||||
"default_value": true,
|
||||
},
|
||||
{
|
||||
# Create LDTKEntityPlaceholder nodes to help debug importing.
|
||||
"name": "use_entity_placeholders",
|
||||
"default_value": false,
|
||||
},
|
||||
# --- Post Import --- #
|
||||
{"name": "Post Import", "default_value":"", "usage": PROPERTY_USAGE_GROUP},
|
||||
{
|
||||
# Define a post-import script to apply on imported Tilesets.
|
||||
"name": "tileset_post_import",
|
||||
"default_value": "",
|
||||
"property_hint": PROPERTY_HINT_FILE,
|
||||
"hint_string": "*.gd;GDScript"
|
||||
},
|
||||
{
|
||||
# Define a post-import script to apply on imported Entities.
|
||||
"name": "entities_post_import",
|
||||
"default_value": "",
|
||||
"property_hint": PROPERTY_HINT_FILE,
|
||||
"hint_string": "*.gd;GDScript"
|
||||
},
|
||||
{
|
||||
# Define a post-import script to apply on imported Levels.
|
||||
"name": "level_post_import",
|
||||
"default_value": "",
|
||||
"property_hint": PROPERTY_HINT_FILE,
|
||||
"hint_string": "*.gd;GDScript"
|
||||
},
|
||||
{
|
||||
# Define a post-import script to apply on imported Worlds.
|
||||
"name": "world_post_import",
|
||||
"default_value": "",
|
||||
"property_hint": PROPERTY_HINT_FILE,
|
||||
"hint_string": "*.gd;GDScript"
|
||||
},
|
||||
# --- Debug --- #
|
||||
{"name": "Debug", "default_value":"", "usage": PROPERTY_USAGE_GROUP},
|
||||
{
|
||||
# Force Tilesets to be recreated, resetting modifications (if experiencing import issues)
|
||||
"name": "force_tileset_reimport",
|
||||
"default_value": false,
|
||||
},
|
||||
{
|
||||
# Debug: Enable Verbose Output (used by the importer)
|
||||
"name": "verbose_output", "default_value": false
|
||||
}
|
||||
]
|
||||
|
||||
func _import(
|
||||
source_file: String,
|
||||
save_path: String,
|
||||
options: Dictionary,
|
||||
platform_variants: Array[String],
|
||||
gen_files: Array[String]
|
||||
) -> Error:
|
||||
|
||||
Util.timer_reset()
|
||||
Util.timer_start(Util.DebugTime.TOTAL)
|
||||
Util.print("import_start", source_file)
|
||||
|
||||
# Add options to static var in "Util", accessible from any script.
|
||||
Util.options = options
|
||||
|
||||
# Parse source_file
|
||||
var base_dir := source_file.get_base_dir() + "/"
|
||||
var file_name := source_file.get_file()
|
||||
var world_name := file_name.split(".")[0]
|
||||
|
||||
Util.timer_start(Util.DebugTime.LOAD)
|
||||
var world_data := Util.parse_file(source_file)
|
||||
Util.timer_finish("File parsed")
|
||||
|
||||
# Check version
|
||||
if Util.check_version(world_data.jsonVersion, LDTK_LATEST_VERSION):
|
||||
Util.print("item_ok", "LDTK VERSION (%s) OK" % [world_data.jsonVersion])
|
||||
else:
|
||||
return ERR_PARSE_ERROR
|
||||
|
||||
Util.timer_start(Util.DebugTime.GENERAL)
|
||||
var definitions := DefinitionUtil.build_definitions(world_data)
|
||||
var tileset_overrides := Tileset.get_tileset_overrides(world_data)
|
||||
Util.timer_finish("Definitions Created")
|
||||
|
||||
# Build Tilesets and save as Resources
|
||||
if Util.options.verbose_output: Util.print("block", "Tilesets")
|
||||
var tileset_paths := Tileset.build_tilesets(definitions, base_dir, tileset_overrides)
|
||||
gen_files.append_array(tileset_paths)
|
||||
|
||||
# Fetch EntityDef Tile textures
|
||||
Tileset.get_entity_def_tiles(definitions, Util.tilesets)
|
||||
|
||||
# Detect Multi-Worlds
|
||||
var external_levels: bool = world_data.externalLevels
|
||||
var world_iid: String = world_data.iid
|
||||
|
||||
var world: LDTKWorld
|
||||
if world_data.worldLayout == null:
|
||||
var world_nodes: Array[LDTKWorld] = []
|
||||
var world_instances: Array = world_data.worlds
|
||||
# Build each world instance
|
||||
for world_instance in world_instances:
|
||||
var world_instance_name: String = world_instance.identifier
|
||||
var world_instance_iid: String = world_instance.iid
|
||||
var levels := Level.build_levels(world_instance, definitions, base_dir, external_levels)
|
||||
var world_node := World.create_world(world_instance_name, world_instance_iid, levels, base_dir)
|
||||
world_nodes.append(world_node)
|
||||
|
||||
world = World.create_multi_world(world_name, world_iid, world_nodes)
|
||||
else:
|
||||
if Util.options.verbose_output: Util.print("block", "Levels")
|
||||
var levels := Level.build_levels(world_data, definitions, base_dir, external_levels)
|
||||
|
||||
# Save Levels (after Level Post-Import)
|
||||
if (Util.options.pack_levels):
|
||||
var levels_path := base_dir + 'levels/'
|
||||
var directory = DirAccess.open(base_dir)
|
||||
if not directory.dir_exists(levels_path):
|
||||
directory.make_dir(levels_path)
|
||||
|
||||
# Resolve Refs + Cleanup Resolvers. We don't want to save 'NodePathResolver' in the Level scene.
|
||||
#if (Util.options.verbose_output): Util.print("block", "References")
|
||||
if (Util.options.verbose_output): Util.print("block", "Save Levels")
|
||||
Util.handle_references()
|
||||
var packed_levels = save_levels(levels, levels_path, gen_files)
|
||||
|
||||
if (Util.options.verbose_output): Util.print("block", "Save World")
|
||||
world = World.create_world(world_name, world_iid, packed_levels, base_dir)
|
||||
else:
|
||||
if (Util.options.verbose_output): Util.print("block", "Save World")
|
||||
world = World.create_world(world_name, world_iid, levels, base_dir)
|
||||
|
||||
Util.handle_references()
|
||||
|
||||
# Save World as PackedScene
|
||||
Util.timer_start(Util.DebugTime.SAVE)
|
||||
var err = save_world(world, save_path, gen_files)
|
||||
Util.timer_finish("World Saved", 1)
|
||||
|
||||
if Util.options.verbose_output: Util.print("block", "Results")
|
||||
|
||||
Util.timer_finish("Completed.")
|
||||
|
||||
var total_time: int = Util.DebugTime.get_total_time()
|
||||
var result_message: String = Util.DebugTime.get_result()
|
||||
|
||||
if Util.options.verbose_output: Util.print("item_info", result_message)
|
||||
Util.print("import_finish", str(total_time))
|
||||
|
||||
return err
|
||||
|
||||
#endregion
|
||||
|
||||
func save_world(
|
||||
world: LDTKWorld,
|
||||
save_path: String,
|
||||
gen_files: Array[String]
|
||||
) -> Error:
|
||||
var packed_world = PackedScene.new()
|
||||
packed_world.pack(world)
|
||||
|
||||
Util.print("item_save", "Saving World [color=#fe8019][i]'%s'[/i][/color]" % [save_path], 1)
|
||||
|
||||
var world_path = "%s.%s" % [save_path, _get_save_extension()]
|
||||
var err = ResourceSaver.save(packed_world, world_path)
|
||||
if err == OK:
|
||||
gen_files.append(world_path)
|
||||
return err
|
||||
|
||||
func save_levels(
|
||||
levels: Array[LDTKLevel],
|
||||
save_path: String,
|
||||
gen_files: Array[String]
|
||||
) -> Array[LDTKLevel]:
|
||||
Util.timer_start(Util.DebugTime.SAVE)
|
||||
var packed_levels: Array[LDTKLevel] = []
|
||||
|
||||
|
||||
var level_names := levels.map(func(elem): return elem.name)
|
||||
Util.print("item_save", "Saving Levels: [color=#fe8019]%s[/color]" % [level_names], 1)
|
||||
|
||||
for level in levels:
|
||||
for child in level.get_children():
|
||||
Util.recursive_set_owner(child, level)
|
||||
var level_path = save_level(level, save_path, gen_files)
|
||||
var packed_level = load(level_path).instantiate()
|
||||
packed_levels.append(packed_level)
|
||||
|
||||
Util.timer_finish("%s Levels Saved" % [levels.size()], 1)
|
||||
return packed_levels
|
||||
|
||||
func save_level(
|
||||
level: LDTKLevel,
|
||||
save_path: String,
|
||||
gen_files: Array[String]
|
||||
) -> String:
|
||||
var packed_level = PackedScene.new()
|
||||
packed_level.pack(level)
|
||||
var level_path = "%s%s.%s" % [save_path, level.name, _get_save_extension()]
|
||||
|
||||
var err = ResourceSaver.save(packed_level, level_path)
|
||||
if err == OK:
|
||||
gen_files.append(level_path)
|
||||
|
||||
return level_path
|
1
godot/addons/ldtk-importer/ldtk-importer.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://lnqlil4dt4ca
|
7
godot/addons/ldtk-importer/plugin.cfg
Normal file
@ -0,0 +1,7 @@
|
||||
[plugin]
|
||||
|
||||
name="LDTK"
|
||||
description="LDtk Import plugin for Godot 4!"
|
||||
author="Andy Gleeson (gleeson.dev)"
|
||||
version="2.0.1"
|
||||
script="plugin.gd"
|
19
godot/addons/ldtk-importer/plugin.gd
Normal file
@ -0,0 +1,19 @@
|
||||
@tool
|
||||
extends EditorPlugin
|
||||
|
||||
var ldtk_plugin
|
||||
var config = ConfigFile.new()
|
||||
|
||||
func _enter_tree() -> void:
|
||||
ldtk_plugin = preload("ldtk-importer.gd").new()
|
||||
add_import_plugin(ldtk_plugin)
|
||||
|
||||
var config = ConfigFile.new()
|
||||
var err = config.load("res://addons/ldtk-importer/plugin.cfg")
|
||||
var version = config.get_value("plugin", "version", "0.0")
|
||||
|
||||
print_rich("[color=#ffcc00]█ Godot-LDtk-Importer █[/color] %s | [url=https://gleeson.dev]@gleeson.dev[/url] | [url=https://github.com/heygleeson/godot-ldtk-importer]View on Github[/url]" % [version])
|
||||
|
||||
func _exit_tree() -> void:
|
||||
remove_import_plugin(ldtk_plugin)
|
||||
ldtk_plugin = null
|
1
godot/addons/ldtk-importer/plugin.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bqk4o57wsdal2
|
15
godot/addons/ldtk-importer/post-import/entity-template.gd
Normal file
@ -0,0 +1,15 @@
|
||||
@tool
|
||||
|
||||
# Entity Post-Import Template for LDTK-Importer.
|
||||
|
||||
func post_import(entity_layer: LDTKEntityLayer) -> LDTKEntityLayer:
|
||||
var definition: Dictionary = entity_layer.definition
|
||||
var entities: Array = entity_layer.entities
|
||||
|
||||
#print("EntityLayer: ", entity_layer.name, " | Count: ", entities.size())
|
||||
|
||||
for entity in entities:
|
||||
# Perform operations here
|
||||
pass
|
||||
|
||||
return entity_layer
|
@ -0,0 +1 @@
|
||||
uid://c56fiui27y4ww
|
8
godot/addons/ldtk-importer/post-import/level-template.gd
Normal file
@ -0,0 +1,8 @@
|
||||
@tool
|
||||
|
||||
# Level Post-Import Template for LDTK-Importer.
|
||||
|
||||
func post_import(level: LDTKLevel) -> LDTKLevel:
|
||||
# Behaviour goes here
|
||||
#print("Level: ", level)
|
||||
return level
|
@ -0,0 +1 @@
|
||||
uid://05p0rgy0kklx
|
10
godot/addons/ldtk-importer/post-import/tileset-template.gd
Normal file
@ -0,0 +1,10 @@
|
||||
@tool
|
||||
|
||||
# Tileset Post-Import Template for LDTK-Importer.
|
||||
|
||||
func post_import(tilesets: Dictionary) -> Dictionary:
|
||||
# Behaviour goes here
|
||||
for tileset: TileSet in tilesets.values():
|
||||
#print("Tileset: ", tileset, tileset.tile_size)
|
||||
pass
|
||||
return tilesets
|
@ -0,0 +1 @@
|
||||
uid://bwex3re84r6o2
|
8
godot/addons/ldtk-importer/post-import/world-template.gd
Normal file
@ -0,0 +1,8 @@
|
||||
@tool
|
||||
|
||||
# World Post-Import Template for LDTK-Importer.
|
||||
|
||||
func post_import(world: LDTKWorld) -> LDTKWorld:
|
||||
# Behaviour goes here
|
||||
#print("World: ", world)
|
||||
return world
|
@ -0,0 +1 @@
|
||||
uid://bko7ojwy2jnl
|
@ -0,0 +1,8 @@
|
||||
@tool
|
||||
@icon("ldtk-entity-layer.svg")
|
||||
class_name LDTKEntityLayer
|
||||
extends Node2D
|
||||
|
||||
@export var iid: String
|
||||
@export var definition: Dictionary
|
||||
@export var entities: Array
|
@ -0,0 +1 @@
|
||||
uid://c6umqvu1pttjk
|
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.a{fill:#fc0;}</style></defs><polygon class="a" points="8.47 12.55 8 12.79 7.53 12.55 2.05 9.8 0.85 10.4 8 14 15.15 10.4 13.95 9.8 8.47 12.55"/><polygon class="a" points="8.47 10.15 8 10.39 7.53 10.15 2.05 7.4 0.85 8 8 11.6 15.15 8 13.95 7.4 8.47 10.15"/><polygon class="a" points="8 2 0.85 5.6 8 9.2 15.15 5.6 8 2"/></svg>
|
After Width: | Height: | Size: 396 B |
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://cjhytibfjjb2y"
|
||||
path="res://.godot/imported/ldtk-entity-layer.svg-4429574458c1abbb5905ae9e6174fef3.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/ldtk-importer/src/components/ldtk-entity-layer.svg"
|
||||
dest_files=["res://.godot/imported/ldtk-entity-layer.svg-4429574458c1abbb5905ae9e6174fef3.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://dndtl8sgpdo6m"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/ldtk-importer/src/components/ldtk-entity-layer.gd" id="1_kdfje"]
|
||||
|
||||
[node name="LDTK Entity Layer" type="Node2D"]
|
||||
script = ExtResource("1_kdfje")
|
96
godot/addons/ldtk-importer/src/components/ldtk-entity.gd
Normal file
@ -0,0 +1,96 @@
|
||||
@icon("ldtk-entity.svg")
|
||||
@tool
|
||||
class_name LDTKEntity
|
||||
extends Node2D
|
||||
|
||||
## Placeholder Node for importing LDTK maps.
|
||||
## Used to demonstrate import functionality - please write an Entity Post-Import script to spawn
|
||||
## your own instances when using this in your project.
|
||||
|
||||
@export var iid: String
|
||||
@export var identifier := "EntityPlaceholder"
|
||||
@export var fields := {}
|
||||
@export var pivot := Vector2.ZERO
|
||||
@export var size := Vector2.ZERO
|
||||
@export var smart_color := Color.hex(0xffcc0088)
|
||||
@export var definition := {}
|
||||
|
||||
@onready var sprite: Sprite2D = $Sprite
|
||||
|
||||
var _refs := []
|
||||
var _points := []
|
||||
var _drawPaths := false
|
||||
|
||||
func _ready() -> void:
|
||||
_points.append(Vector2.ZERO)
|
||||
for key in fields:
|
||||
if fields[key] is NodePath:
|
||||
_refs.append(fields[key])
|
||||
elif fields[key] is Vector2i:
|
||||
_points.append(fields[key])
|
||||
elif fields[key] is Array:
|
||||
for value in fields[key]:
|
||||
if value is NodePath:
|
||||
_refs.append(value)
|
||||
elif value is Vector2i:
|
||||
_points.append(value)
|
||||
else:
|
||||
break
|
||||
_drawPaths = _refs.size() > 0 or _points.size() > 0
|
||||
_points = _parse_points(_points)
|
||||
queue_redraw()
|
||||
|
||||
func _draw() -> void:
|
||||
if definition.is_empty():
|
||||
return
|
||||
|
||||
match definition.renderMode:
|
||||
"Ellipse":
|
||||
if definition.hollow:
|
||||
draw_arc((size * 0.5) + size * -pivot, size.x * 0.5, 0, TAU, 24, smart_color, 1.0)
|
||||
else:
|
||||
draw_circle((size * 0.5) + size * -pivot, size.x * 0.5, smart_color)
|
||||
"Rectangle":
|
||||
if definition.hollow:
|
||||
draw_rect(Rect2(size * -pivot, size), smart_color, false, 1.0)
|
||||
else:
|
||||
draw_rect(Rect2(size * -pivot, size), smart_color, true)
|
||||
"Cross":
|
||||
draw_line(Vector2.ZERO, size, smart_color, 3.0)
|
||||
draw_line(Vector2(0, size.y), Vector2(size.x, 0), smart_color, 3.0)
|
||||
"Tile":
|
||||
if definition.tile == null:
|
||||
pass
|
||||
if definition.tile is Texture2D:
|
||||
sprite.texture = definition.tile
|
||||
|
||||
if _drawPaths:
|
||||
for path in _refs:
|
||||
if path is not NodePath:
|
||||
continue
|
||||
elif path.is_empty():
|
||||
continue
|
||||
var node = get_node(path)
|
||||
if node != null:
|
||||
draw_dashed_line(Vector2.ZERO, node.global_position - global_position, smart_color)
|
||||
|
||||
var previousPoint = _points[0]
|
||||
for point in _points:
|
||||
if point == previousPoint:
|
||||
continue
|
||||
draw_dashed_line(Vector2(previousPoint), Vector2(point), smart_color, 1.0, 4.0)
|
||||
draw_arc(point, 4.0, 0, TAU, 5, smart_color, 1.0)
|
||||
previousPoint = point
|
||||
|
||||
func _parse_points(points: Array) -> Array:
|
||||
if points.size() == 0:
|
||||
return points
|
||||
if get_parent() is SubViewport:
|
||||
return points
|
||||
var origin = get_parent().global_position - global_position
|
||||
var gridSize = get_parent().definition.gridSize
|
||||
var cellOffset = gridSize * Vector2(0.5, 0.5)
|
||||
for index in range(1, points.size()):
|
||||
var pixelCoord = points[index] * gridSize
|
||||
points[index] = origin + pixelCoord + cellOffset
|
||||
return points
|
@ -0,0 +1 @@
|
||||
uid://ciskd8jyty7gq
|
14
godot/addons/ldtk-importer/src/components/ldtk-entity.svg
Normal file
@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- Generator: Adobe Illustrator 24.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
|
||||
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
|
||||
viewBox="0 0 16 16" style="enable-background:new 0 0 16 16;" xml:space="preserve">
|
||||
<style type="text/css">
|
||||
.st0{fill:none;stroke:#FFCC00;stroke-width:2;stroke-linejoin:round;}
|
||||
.st1{fill:#FFCC00;stroke:#FFCC00;stroke-miterlimit:10;}
|
||||
</style>
|
||||
<path class="st0" d="M3,2h10c0.6,0,1,0.4,1,1v10c0,0.6-0.4,1-1,1H3c-0.6,0-1-0.4-1-1V3C2,2.4,2.4,2,3,2z"/>
|
||||
<rect x="5" y="4.5" class="st1" width="6" height="1"/>
|
||||
<rect x="7" y="7.5" class="st1" width="2" height="1"/>
|
||||
<rect x="5" y="10.5" class="st1" width="6" height="1"/>
|
||||
<rect x="4.8" y="4.5" class="st1" width="1.5" height="7"/>
|
||||
</svg>
|
After Width: | Height: | Size: 842 B |
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://c8jokgcqymy8i"
|
||||
path="res://.godot/imported/ldtk-entity.svg-0e9e158ef8dc55d8602ffe476bdddfa3.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/ldtk-importer/src/components/ldtk-entity.svg"
|
||||
dest_files=["res://.godot/imported/ldtk-entity.svg-0e9e158ef8dc55d8602ffe476bdddfa3.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
@ -0,0 +1,9 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://c36l6fpttus66"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/ldtk-importer/src/components/ldtk-entity.gd" id="1_hclg8"]
|
||||
|
||||
[node name="LDTK Entity" type="Node2D"]
|
||||
script = ExtResource("1_hclg8")
|
||||
smart_color = Color(1, 0.8, 0, 0.533333)
|
||||
|
||||
[node name="Sprite" type="Sprite2D" parent="."]
|
18
godot/addons/ldtk-importer/src/components/ldtk-level.gd
Normal file
@ -0,0 +1,18 @@
|
||||
@icon("ldtk-level.svg")
|
||||
@tool
|
||||
class_name LDTKLevel
|
||||
extends Node2D
|
||||
|
||||
@export var iid: String
|
||||
@export var world_position: Vector2
|
||||
@export var size: Vector2i
|
||||
@export var fields: Dictionary
|
||||
@export var neighbours: Array
|
||||
@export var bg_color: Color
|
||||
|
||||
func _ready() -> void:
|
||||
queue_redraw()
|
||||
|
||||
func _draw() -> void:
|
||||
if Engine.is_editor_hint():
|
||||
draw_rect(Rect2(Vector2.ZERO, size), bg_color, false, 2.0)
|
@ -0,0 +1 @@
|
||||
uid://devv1ueptyklo
|
1
godot/addons/ldtk-importer/src/components/ldtk-level.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.a{fill:#fc0;}</style></defs><path class="a" d="M8.61,14.27c1.32-1.65,4.33-5.65,4.33-7.9a4.94,4.94,0,1,0-9.88,0h0c0,2.25,3,6.25,4.33,7.9a.78.78,0,0,0,1.1.13ZM8,4.72A1.65,1.65,0,1,1,6.35,6.37,1.65,1.65,0,0,1,8,4.72Z"/></svg>
|
After Width: | Height: | Size: 296 B |
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bn8846b01v84"
|
||||
path="res://.godot/imported/ldtk-level.svg-7c4c13bcdc12c18fc0e3338deb18be11.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/ldtk-importer/src/components/ldtk-level.svg"
|
||||
dest_files=["res://.godot/imported/ldtk-level.svg-7c4c13bcdc12c18fc0e3338deb18be11.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://5jp4st4vnouy"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/ldtk-importer/src/components/ldtk-level.gd" id="1_4fbyi"]
|
||||
|
||||
[node name="LDTK Level" type="Node2D"]
|
||||
script = ExtResource("1_4fbyi")
|
@ -0,0 +1,9 @@
|
||||
@tool
|
||||
@icon("ldtk-entity-layer.svg")
|
||||
class_name LDTKWorldLayer
|
||||
extends Node2D
|
||||
|
||||
@export var depth: int:
|
||||
set(d):
|
||||
depth = d
|
||||
z_index = depth
|
@ -0,0 +1 @@
|
||||
uid://cvvkp8akfp51o
|
@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://tiadjdf7bmjd"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/ldtk-importer/src/components/ldtk-world-layer.gd" id="1_tla3i"]
|
||||
|
||||
[node name="World Layer" type="Node2D"]
|
||||
script = ExtResource("1_tla3i")
|
22
godot/addons/ldtk-importer/src/components/ldtk-world.gd
Normal file
@ -0,0 +1,22 @@
|
||||
@tool
|
||||
@icon("ldtk-world.svg")
|
||||
class_name LDTKWorld
|
||||
extends Node2D
|
||||
|
||||
@export var iid: String
|
||||
@export var rect: Rect2i
|
||||
@export var levels: Array[LDTKLevel]
|
||||
|
||||
func _init() -> void:
|
||||
child_order_changed.connect(_find_level_children)
|
||||
|
||||
func _find_level_children() -> void:
|
||||
for child in get_children():
|
||||
if child is LDTKLevel:
|
||||
if not levels.has(child):
|
||||
levels.append(child)
|
||||
else:
|
||||
for grandchild in child.get_children():
|
||||
if grandchild is LDTKLevel:
|
||||
if not levels.has(grandchild):
|
||||
levels.append(grandchild)
|
@ -0,0 +1 @@
|
||||
uid://boyf0tvvercyb
|
1
godot/addons/ldtk-importer/src/components/ldtk-world.svg
Normal file
@ -0,0 +1 @@
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><defs><style>.a{fill:none;stroke:#fc0;stroke-miterlimit:10;stroke-width:2px;}</style></defs><polygon class="a" points="10.93 2.92 5.07 2.92 2.13 8 5.07 13.08 10.93 13.08 13.87 8 10.93 2.92"/></svg>
|
After Width: | Height: | Size: 257 B |
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://bhwcp2d8b7ofe"
|
||||
path="res://.godot/imported/ldtk-world.svg-afb7a8fc57a903b71679a614011495e8.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://addons/ldtk-importer/src/components/ldtk-world.svg"
|
||||
dest_files=["res://.godot/imported/ldtk-world.svg-afb7a8fc57a903b71679a614011495e8.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
||||
svg/scale=1.0
|
||||
editor/scale_with_editor_scale=false
|
||||
editor/convert_colors_with_editor_theme=false
|
@ -0,0 +1,6 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://b5smuv4vq5lsp"]
|
||||
|
||||
[ext_resource type="Script" path="res://addons/ldtk-importer/src/components/ldtk-world.gd" id="1_k4v0p"]
|
||||
|
||||
[node name="LDTK World" type="Node2D"]
|
||||
script = ExtResource("1_k4v0p")
|
291
godot/addons/ldtk-importer/src/layer.gd
Normal file
@ -0,0 +1,291 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util/util.gd")
|
||||
const LayerUtil = preload("util/layer-util.gd")
|
||||
const FieldUtil = preload("util/field-util.gd")
|
||||
const TileUtil = preload("util/tile-util.gd")
|
||||
|
||||
# Used to display helpful error messages (which level we are in)
|
||||
static var current_level: String = "N/A"
|
||||
|
||||
static func create_layers(
|
||||
level_data: Dictionary,
|
||||
layer_instances: Array,
|
||||
definitions: Dictionary
|
||||
) -> Array:
|
||||
|
||||
current_level = level_data.identifier
|
||||
|
||||
var layer_nodes := []
|
||||
var layer_index: int = 0
|
||||
|
||||
for layer_instance in layer_instances:
|
||||
var layer_def: Dictionary = definitions.layers[layer_instance.layerDefUid]
|
||||
var layer_type: String = layer_instance.__type
|
||||
|
||||
match layer_type:
|
||||
"Entities":
|
||||
var layer = create_entity_layer(layer_instance, layer_def, definitions.entities)
|
||||
layer_nodes.push_front(layer)
|
||||
|
||||
"IntGrid":
|
||||
# Determines if this has an 'AutoLayer' assigned
|
||||
var has_tileset := layer_instance.__tilesetDefUid != null
|
||||
|
||||
if has_tileset:
|
||||
var layer = create_tile_layer(layer_instance, layer_def)
|
||||
layer_nodes.push_front(layer)
|
||||
if (not has_tileset or Util.options.integer_grid_tilesets):
|
||||
var layer = create_intgrid_layer(layer_instance, layer_def)
|
||||
layer_nodes.push_front(layer)
|
||||
|
||||
"Tiles", "AutoLayer":
|
||||
var layer = create_tile_layer(layer_instance, layer_def)
|
||||
layer_nodes.push_front(layer)
|
||||
|
||||
_:
|
||||
push_warning("[LDtk] Tried importing an unsupported layer type: ", layer_type)
|
||||
|
||||
if (Util.options.verbose_output):
|
||||
var layer_names = layer_nodes.map(func(elem): return elem.name)
|
||||
Util.print("item_info", "Created Layers: [color=slategray]%s[/color]" % [layer_names], 2)
|
||||
return layer_nodes
|
||||
|
||||
#region Layer Types
|
||||
|
||||
static func create_entity_layer(
|
||||
layer_data: Dictionary,
|
||||
layer_def: Dictionary,
|
||||
entity_defs: Dictionary
|
||||
) -> LDTKEntityLayer:
|
||||
|
||||
var layer = LDTKEntityLayer.new()
|
||||
layer.name = layer_data.__identifier
|
||||
layer.iid = layer_data.iid
|
||||
layer.position = layer_def.offset
|
||||
|
||||
# Create a dummy child node so EntityRef fields get a correct NodePath
|
||||
# I need to find a better way to do this, but there are lots of funny behaviours to deal with.
|
||||
var pathResolver = Node2D.new()
|
||||
pathResolver.name = "NodePathResolver"
|
||||
layer.add_child(pathResolver)
|
||||
Util.path_resolvers.append(pathResolver)
|
||||
|
||||
var entities: Array = LayerUtil.parse_entity_instances(
|
||||
layer_data.entityInstances,
|
||||
entity_defs,
|
||||
pathResolver
|
||||
)
|
||||
|
||||
# Add instance references
|
||||
layer.definition = layer_def
|
||||
layer.entities = entities
|
||||
|
||||
if (Util.options.use_entity_placeholders):
|
||||
LayerUtil.placeholder_counts.clear()
|
||||
for entity in entities:
|
||||
var placeholder: LDTKEntity = LayerUtil.create_entity_placeholder(layer, entity)
|
||||
Util.update_instance_reference(placeholder.iid, placeholder)
|
||||
#try_push_placeholder_ref(placeholder, pathResolver)
|
||||
try_push_placeholder_ref(placeholder, placeholder)
|
||||
|
||||
return layer
|
||||
|
||||
static func create_intgrid_layer(
|
||||
layer_data: Dictionary,
|
||||
layer_def: Dictionary
|
||||
) -> TileMapLayer:
|
||||
|
||||
# Create TileMapLayer
|
||||
var tilemap: TileMapLayer = LayerUtil.create_layer_tilemap(layer_data)
|
||||
tilemap.position = layer_def.offset
|
||||
|
||||
# Retrieve IntGrid values - these do not always match their array index
|
||||
var values: Array = layer_def.intGridValues.map(
|
||||
func(item): return item.value
|
||||
)
|
||||
|
||||
# Set layer properties on the Tilemap
|
||||
var layer_name := str(layer_data.__identifier) + "-values"
|
||||
tilemap.set_name(layer_name)
|
||||
tilemap.set_modulate(Color(1, 1, 1, layer_data.__opacity))
|
||||
|
||||
# Get tile data
|
||||
var tiles: Array = layer_data.intGridCsv
|
||||
var tile_source_id: int = layer_data.layerDefUid
|
||||
var columns: int = layer_data.__cWid
|
||||
|
||||
# Place IntGrid value tiles
|
||||
for index in range(0, tiles.size()):
|
||||
var value = tiles[index]
|
||||
var value_index: int = values.find(value)
|
||||
if value_index != -1:
|
||||
var cell_coords := TileUtil.index_to_grid(index, columns)
|
||||
var tile_coords := Vector2i(value_index, 0)
|
||||
tilemap.set_cell(cell_coords, tile_source_id, tile_coords)
|
||||
|
||||
return tilemap
|
||||
|
||||
static func create_tile_layer(
|
||||
layer_data: Dictionary,
|
||||
layer_def: Dictionary
|
||||
) -> TileMapLayer:
|
||||
|
||||
# Create Tilemap
|
||||
var tilemap: TileMapLayer = LayerUtil.create_layer_tilemap(layer_data)
|
||||
tilemap.position = layer_def.offset
|
||||
|
||||
# Set layer properties on the Tilemap
|
||||
var layer_name := str(layer_data.__identifier)
|
||||
tilemap.set_name(layer_name)
|
||||
tilemap.set_modulate(Color(1, 1, 1, layer_data.__opacity))
|
||||
|
||||
if not Util.options.layers_always_visible:
|
||||
tilemap.set_enabled(layer_data.visible)
|
||||
|
||||
# Get tile data
|
||||
var tiles: Array
|
||||
if (layer_data.__type == "Tiles"):
|
||||
tiles = layer_data.gridTiles
|
||||
else:
|
||||
tiles = layer_data.autoLayerTiles
|
||||
|
||||
var tile_source_id = layer_data.__tilesetDefUid
|
||||
var grid_size := Vector2(layer_data.__gridSize, layer_data.__gridSize)
|
||||
|
||||
if (tile_source_id == null):
|
||||
Util.print("item_fail", "Null Tilemap on layerInstance: '%s'" % [layer_name], 2)
|
||||
push_warning("Detected null tilemap on a level '%s' layer instance '%s'. Please fix this in your LDTK project file." % [current_level, layer_name])
|
||||
return tilemap
|
||||
|
||||
__place_tiles(tilemap, tiles, tile_source_id, grid_size)
|
||||
|
||||
return tilemap
|
||||
|
||||
#endregion
|
||||
|
||||
static func try_push_placeholder_ref(
|
||||
placeholder: LDTKEntity,
|
||||
entity: Node
|
||||
) -> void:
|
||||
if not Util.options.resolve_entityrefs: return
|
||||
if not placeholder.fields: return
|
||||
|
||||
placeholder.fields = str_to_var(var_to_str(placeholder.fields))
|
||||
var field_defs = {}
|
||||
|
||||
for key in placeholder.definition.field_defs:
|
||||
var def = placeholder.definition.field_defs[key]
|
||||
field_defs[def.identifier] = def
|
||||
|
||||
for key in placeholder.fields:
|
||||
var def = field_defs[key]
|
||||
if not def.type.contains("EntityRef"): continue
|
||||
|
||||
var field = placeholder.fields[key]
|
||||
if not field: continue
|
||||
|
||||
if field is Array:
|
||||
for index in range(field.size()):
|
||||
Util.add_unresolved_reference(field, index, entity)
|
||||
else:
|
||||
Util.add_unresolved_reference(placeholder.fields, key, entity)
|
||||
|
||||
static func __place_tiles(
|
||||
tilemap: TileMapLayer,
|
||||
tiles: Array,
|
||||
tile_source_id: int,
|
||||
grid_size: Vector2,
|
||||
layer_index: int = 0
|
||||
) -> void:
|
||||
|
||||
var tile_source: TileSetAtlasSource
|
||||
if tilemap.tile_set.has_source(tile_source_id):
|
||||
tile_source = tilemap.tile_set.get_source(tile_source_id)
|
||||
else:
|
||||
push_error("TileSetAtlasSource missing")
|
||||
return
|
||||
|
||||
var tile_size := Vector2(tile_source.texture_region_size)
|
||||
|
||||
# Place tiles
|
||||
for tile in tiles:
|
||||
var cell_px := Vector2(tile.px[0], tile.px[1])
|
||||
var tile_px := Vector2(tile.src[0], tile.src[1])
|
||||
var cell_grid := TileUtil.px_to_grid(cell_px, grid_size, Vector2i.ZERO)
|
||||
var tile_grid := TileUtil.px_to_grid(tile_px, tile_size, tile_source.margins, tile_source.separation)
|
||||
|
||||
# Tile does not exist
|
||||
if not tile_source.has_tile(tile_grid):
|
||||
continue
|
||||
|
||||
# Handle flipped tiles
|
||||
var alternative_tile: int = 0
|
||||
var tile_flip := TileUtil.get_tile_flip_mask(int(tile.f))
|
||||
|
||||
if (tile_flip != 0):
|
||||
alternative_tile = tile_flip
|
||||
|
||||
# Handle alpha
|
||||
if tile.a < 1.0:
|
||||
var alternative_index := 1
|
||||
var alternative_exists := false
|
||||
var alternative_count := tile_source.get_alternative_tiles_count(tile_grid)
|
||||
|
||||
# Find alternate tile with same alpha
|
||||
if alternative_count > alternative_index:
|
||||
for i in range(alternative_index, alternative_count):
|
||||
var data = tile_source.get_tile_data(tile_grid, i)
|
||||
if is_equal_approx(data.modulate.a, tile.a):
|
||||
# Reverse flip bools back into an int
|
||||
var flip = TileUtil.get_tile_flip_mask(int(data.flip_h) + int(data.flip_v) * 2)
|
||||
if tile_flip == flip:
|
||||
alternative_index = i
|
||||
alternative_exists = true
|
||||
break
|
||||
|
||||
# Create new tile
|
||||
if not alternative_exists:
|
||||
alternative_index = tile_source.create_alternative_tile(tile_grid, alternative_count)
|
||||
var new_data = tile_source.get_tile_data(tile_grid, alternative_index)
|
||||
TileUtil.copy_and_modify_tile_data(
|
||||
new_data,
|
||||
tile_source.get_tile_data(tile_grid, 0),
|
||||
tilemap.tile_set.get_physics_layers_count(),
|
||||
tilemap.tile_set.get_navigation_layers_count(),
|
||||
tilemap.tile_set.get_occlusion_layers_count(),
|
||||
tile_flip
|
||||
)
|
||||
new_data.modulate.a = tile.a
|
||||
|
||||
alternative_tile = alternative_index
|
||||
|
||||
if not tilemap.get_cell_tile_data(cell_grid):
|
||||
tilemap.set_cell(cell_grid, tile_source_id, tile_grid, alternative_tile)
|
||||
else:
|
||||
__place_overlapping_tile(tilemap, cell_grid, tile_source_id, tile_grid, alternative_tile)
|
||||
|
||||
static func __place_overlapping_tile(
|
||||
tilemap: TileMapLayer,
|
||||
cell_grid: Vector2i,
|
||||
tile_source_id: int,
|
||||
tile_grid: Vector2i,
|
||||
alternative_tile: int
|
||||
) -> void:
|
||||
var tilemap_child: TileMapLayer
|
||||
var empty := false
|
||||
|
||||
# Loop through existing children to find empty cell
|
||||
for child in tilemap.get_children():
|
||||
if not child.get_cell_tile_data(cell_grid):
|
||||
tilemap_child = child
|
||||
empty = true
|
||||
break
|
||||
|
||||
# Create new child if no empty cell (or child) could be found
|
||||
if not empty:
|
||||
tilemap_child = LayerUtil.create_tilemap_child(tilemap)
|
||||
tilemap.add_child(tilemap_child)
|
||||
|
||||
# Set tile
|
||||
tilemap_child.set_cell(cell_grid, tile_source_id, tile_grid, alternative_tile)
|
1
godot/addons/ldtk-importer/src/layer.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://46jgmlrl0r3s
|
125
godot/addons/ldtk-importer/src/level.gd
Normal file
@ -0,0 +1,125 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util/util.gd")
|
||||
const LevelUtil = preload("util/level-util.gd")
|
||||
const FieldUtil = preload("util/field-util.gd")
|
||||
const PostImport = preload("post-import.gd")
|
||||
const Layer = preload("layer.gd")
|
||||
|
||||
static var base_directory: String
|
||||
|
||||
static func build_levels(
|
||||
world_data: Dictionary,
|
||||
definitions: Dictionary,
|
||||
base_dir: String,
|
||||
external_levels: bool
|
||||
) -> Array[LDTKLevel]:
|
||||
|
||||
Util.timer_start(Util.DebugTime.GENERAL)
|
||||
base_directory = base_dir
|
||||
var levels: Array[LDTKLevel] = []
|
||||
|
||||
# Calculate level positions
|
||||
var level_positions: Array
|
||||
match world_data.worldLayout:
|
||||
"LinearHorizontal":
|
||||
var x = 0
|
||||
for level in world_data.levels:
|
||||
level_positions.append(Vector2i(x, 0))
|
||||
x += level.pxWid
|
||||
"LinearVertical":
|
||||
var y := 0
|
||||
for level in world_data.levels:
|
||||
level_positions.append(Vector2i(0, y))
|
||||
y += level.pxHei
|
||||
"GridVania", "Free":
|
||||
level_positions = world_data.levels.map(
|
||||
func (current):
|
||||
return Vector2i(current.worldX, current.worldY)
|
||||
)
|
||||
_:
|
||||
printerr("World Layout not supported: ", world_data.worldLayout)
|
||||
|
||||
# Create levels
|
||||
for level_index in range(world_data.levels.size()):
|
||||
Util.timer_start(Util.DebugTime.GENERAL)
|
||||
var level_data
|
||||
var position: Vector2i = level_positions[level_index]
|
||||
level_data = world_data.levels[level_index]
|
||||
|
||||
if external_levels:
|
||||
level_data = LevelUtil.get_external_level(level_data, base_dir)
|
||||
|
||||
var level = create_level(level_data, position, definitions)
|
||||
Util.timer_finish("Built Level", 2)
|
||||
|
||||
if (Util.options.entities_post_import):
|
||||
level = PostImport.run_entity_post_import(level, Util.options.entities_post_import)
|
||||
|
||||
if (Util.options.level_post_import):
|
||||
level = PostImport.run_level_post_import(level, Util.options.level_post_import)
|
||||
|
||||
levels.append(level)
|
||||
|
||||
Util.timer_finish("Built %s Levels" % levels.size(), 1)
|
||||
return levels
|
||||
|
||||
static func create_level(
|
||||
level_data: Dictionary,
|
||||
position: Vector2i,
|
||||
definitions: Dictionary
|
||||
) -> LDTKLevel:
|
||||
var level_name: String = level_data.identifier
|
||||
var level := LDTKLevel.new()
|
||||
level.name = level_name
|
||||
level.iid = level_data.iid
|
||||
level.world_position = position
|
||||
level.size = Vector2i(level_data.pxWid, level_data.pxHei)
|
||||
level.bg_color = level_data.__bgColor
|
||||
level.z_index = level_data.worldDepth
|
||||
|
||||
if (Util.options.verbose_output): Util.print("block", level_name, 1)
|
||||
Util.update_instance_reference(level_data.iid, level)
|
||||
|
||||
var neighbours = level_data.__neighbours
|
||||
|
||||
if not Util.options.pack_levels:
|
||||
for neighbour in neighbours:
|
||||
Util.add_unresolved_reference(neighbour, "levelIid", level)
|
||||
|
||||
level.neighbours = neighbours
|
||||
|
||||
# Create background image
|
||||
if level_data.bgRelPath != null:
|
||||
var path := "%s/%s" % [base_directory, level_data.bgRelPath]
|
||||
var sprite := Sprite2D.new()
|
||||
sprite.name = "BG Image"
|
||||
sprite.centered = false
|
||||
sprite.texture = load(path)
|
||||
|
||||
# Calculate BG Position
|
||||
var bgData: Dictionary = level_data.__bgPos
|
||||
var pos: Array = bgData.topLeftPx
|
||||
var scale: Array = bgData.scale
|
||||
var region: Array = bgData.cropRect
|
||||
sprite.region_enabled = true
|
||||
sprite.position = Vector2i(pos[0], pos[1])
|
||||
sprite.scale = Vector2i(scale[0], scale[1])
|
||||
sprite.region_rect = Rect2i(region[0], region[1], region[2], region[3])
|
||||
|
||||
level.add_child(sprite)
|
||||
|
||||
# Create fields
|
||||
level.fields = FieldUtil.create_fields(level_data.fieldInstances, level)
|
||||
|
||||
var layer_instances = level_data.layerInstances
|
||||
if not layer_instances is Array:
|
||||
push_error("level '%s' has no layer instances." % [level_name])
|
||||
return level
|
||||
|
||||
# Create layers
|
||||
var layers = Layer.create_layers(level_data, layer_instances, definitions)
|
||||
for layer in layers:
|
||||
level.add_child(layer)
|
||||
|
||||
return level
|
1
godot/addons/ldtk-importer/src/level.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://w5et03vhdql6
|
60
godot/addons/ldtk-importer/src/post-import.gd
Normal file
@ -0,0 +1,60 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util/util.gd")
|
||||
|
||||
static func run(element: Variant, script_path: String) -> Variant:
|
||||
var element_type = typeof(element)
|
||||
|
||||
if not script_path.is_empty():
|
||||
var script = load(script_path)
|
||||
if not script or not script is GDScript:
|
||||
printerr("Post-Import: '%s' is not a GDScript" % [script_path])
|
||||
return ERR_INVALID_PARAMETER
|
||||
|
||||
script = script.new()
|
||||
if not script.has_method("post_import"):
|
||||
printerr("Post-Import: '%s' does not have a post_import() method" % [script_path])
|
||||
return ERR_INVALID_PARAMETER
|
||||
|
||||
element = script.post_import(element)
|
||||
|
||||
if element == null or typeof(element) != element_type:
|
||||
printerr("Post-Import: Invalid scene returned from script.")
|
||||
return ERR_INVALID_DATA
|
||||
|
||||
return element
|
||||
|
||||
static func run_tileset_post_import(tilesets: Dictionary, script_path: String) -> Dictionary:
|
||||
Util.timer_start(Util.DebugTime.POST_IMPORT)
|
||||
Util.print("tileset_post_import", str(tilesets), 1)
|
||||
tilesets = run(tilesets, Util.options.tileset_post_import)
|
||||
Util.timer_finish("Tileset Post-Import: Complete", 1)
|
||||
return tilesets
|
||||
|
||||
static func run_level_post_import(level: LDTKLevel, script_path: String) -> LDTKLevel:
|
||||
Util.timer_start(Util.DebugTime.POST_IMPORT)
|
||||
Util.print("level_post_import", level.name, 2)
|
||||
level = run(level, Util.options.level_post_import)
|
||||
Util.timer_finish("Level Post-Import: Complete", 2)
|
||||
return level
|
||||
|
||||
static func run_entity_post_import(level: LDTKLevel, script_path: String) -> LDTKLevel:
|
||||
Util.timer_start(Util.DebugTime.POST_IMPORT)
|
||||
var layers = level.get_children()
|
||||
for layer in layers:
|
||||
if layer is not LDTKEntityLayer:
|
||||
continue
|
||||
|
||||
var entityLayerName = layer.get_parent().name + "." + layer.name
|
||||
Util.print("entity_post_import", entityLayerName, 3)
|
||||
layer = run(layer, script_path)
|
||||
|
||||
Util.timer_finish("Entity Post-Import: Complete", 3)
|
||||
return level
|
||||
|
||||
static func run_world_post_import(world: LDTKWorld, script_path: String) -> LDTKWorld:
|
||||
Util.timer_start(Util.DebugTime.POST_IMPORT)
|
||||
Util.print("world_post_import", world.name, 1)
|
||||
world = run(world, Util.options.world_post_import)
|
||||
Util.timer_finish("World Post-Import: Complete", 1)
|
||||
return world
|
1
godot/addons/ldtk-importer/src/post-import.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bv4fn2n2ncw12
|
330
godot/addons/ldtk-importer/src/tileset.gd
Normal file
@ -0,0 +1,330 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util/util.gd")
|
||||
const TileUtil = preload("util/tile-util.gd")
|
||||
const FieldUtil = preload("util/field-util.gd")
|
||||
const PostImport = preload("post-import.gd")
|
||||
|
||||
enum AtlasTextureType {CompressedTexture2D, CanvasTexture}
|
||||
|
||||
static func build_tilesets(
|
||||
definitions: Dictionary,
|
||||
base_dir: String,
|
||||
tileset_overrides: Dictionary
|
||||
) -> Array:
|
||||
Util.timer_start(Util.DebugTime.TILES)
|
||||
var tilesets := {}
|
||||
var tileset_sources := {}
|
||||
|
||||
# Reduce Layer Defs to find all unique layer grid sizes and create TileSets for each.
|
||||
var layer_def_uids: Array = definitions.layers.keys()
|
||||
|
||||
tilesets = layer_def_uids.reduce(
|
||||
func(accum: Dictionary, current: float):
|
||||
var layer_def = definitions.layers[current]
|
||||
var grid_size: int = layer_def.gridSize
|
||||
|
||||
if not accum.has(grid_size):
|
||||
accum[grid_size] = get_tileset(grid_size, base_dir)
|
||||
|
||||
# Create TileSetSource for IntGrids
|
||||
if (Util.options.integer_grid_tilesets):
|
||||
if layer_def.type == "IntGrid" and layer_def.intGridValues.size() > 0:
|
||||
var intgrid_uid = layer_def.uid
|
||||
var intgrid_source = create_intgrid_source(layer_def)
|
||||
tileset_sources[intgrid_uid] = intgrid_source
|
||||
Util.add_tileset_reference(intgrid_uid, intgrid_source)
|
||||
return accum
|
||||
, tilesets)
|
||||
|
||||
# Create TileSetSources for each Tileset Def
|
||||
var tileset_def_uids = definitions.tilesets.keys()
|
||||
for uid in tileset_def_uids:
|
||||
var tileset_def: Dictionary = definitions.tilesets[uid]
|
||||
var source: TileSetSource = create_new_tileset_source(tileset_def, base_dir)
|
||||
tileset_sources[uid] = source
|
||||
Util.add_tileset_reference(tileset_def.uid, source)
|
||||
|
||||
# Add TileSetSources to TileSets
|
||||
# NOTE: We also add Sources to mismatched TileSet sizes (if a layer uses that TilesetDef as an override)
|
||||
for id in tilesets.keys():
|
||||
var tileset: TileSet = tilesets[id]
|
||||
var size: int = tileset.tile_size.x
|
||||
|
||||
for uid in tileset_sources.keys():
|
||||
var source: TileSetAtlasSource = tileset_sources[uid]
|
||||
if source == null: continue
|
||||
var source_size: int = source.texture_region_size.x
|
||||
|
||||
# Check if override exists.
|
||||
var has_override: bool = false
|
||||
if (tileset_overrides.has(size)):
|
||||
if (tileset_overrides[size].has(int(uid))):
|
||||
has_override = true
|
||||
|
||||
# Include Source if size matches grid (or is an override found for this grid size)
|
||||
if size == source_size or has_override:
|
||||
if tileset.has_source(uid):
|
||||
source = tileset.get_source(uid)
|
||||
else:
|
||||
source = source.duplicate()
|
||||
tileset.add_source(source, uid)
|
||||
|
||||
if (Util.options.tileset_custom_data):
|
||||
if definitions.tilesets.has(uid):
|
||||
var tileset_def: Dictionary = definitions.tilesets[uid]
|
||||
add_tileset_custom_data(tileset_def, tileset, source, tileset_def.__cWid)
|
||||
|
||||
# Post-Import
|
||||
if (Util.options.tileset_post_import):
|
||||
tilesets = PostImport.run_tileset_post_import(tilesets, Util.options.tileset_post_import)
|
||||
|
||||
# Store tilesets in Util
|
||||
Util.tilesets = tilesets
|
||||
|
||||
Util.timer_finish("Tilesets Created", 1)
|
||||
|
||||
# Save tilesets
|
||||
Util.timer_start(Util.DebugTime.SAVE)
|
||||
var files = save_tilesets(tilesets, base_dir)
|
||||
Util.timer_finish("Tilesets Saved", 1)
|
||||
|
||||
for key in tilesets.keys():
|
||||
# reload tileset (improves performance)
|
||||
var tileset = tilesets[key]
|
||||
if tileset == null: continue
|
||||
if not files.has(key): continue
|
||||
tilesets[key] = ResourceLoader.load(files[key])
|
||||
|
||||
return files.values()
|
||||
|
||||
static func get_tileset(tile_size: int,base_dir: String) -> TileSet:
|
||||
var tileset_name := "tileset_%spx" % [str(tile_size)]
|
||||
var path := base_dir + "tilesets/" + tileset_name + ".res"
|
||||
|
||||
if not (Util.options.force_tileset_reimport):
|
||||
if ResourceLoader.exists(path):
|
||||
var tileset = ResourceLoader.load(path)
|
||||
if tileset is TileSet:
|
||||
return tileset
|
||||
|
||||
# Create new TileSet
|
||||
var tileset := TileSet.new()
|
||||
tileset.resource_name = tileset_name
|
||||
tileset.tile_size = Vector2i(tile_size, tile_size)
|
||||
|
||||
if (Util.options.verbose_output):
|
||||
Util.print("item_info", "Created new TileSet: \"%s\"" % [tileset_name], 1)
|
||||
|
||||
return tileset
|
||||
|
||||
# Create an AtlasSource using tileset definition
|
||||
static func create_new_tileset_source(definition: Dictionary, base_dir: String) -> TileSetSource:
|
||||
# No source texture defined
|
||||
if definition.relPath == null:
|
||||
Util.print("item_fail", "No texture defined for tileset '%s'" % [definition.identifier])
|
||||
push_error("Tileset Definition '%s' has no source texture. Please fix this in your LDtk project file." % [definition.identifier])
|
||||
return null
|
||||
|
||||
# Check if relPath is an absolute directory
|
||||
var filepath: String
|
||||
if definition.relPath.contains(":"):
|
||||
push_warning("Absolute path detected for texture resource '%s'. This is not recommended. Please include this file in the Godot project." % [definition.identifier])
|
||||
filepath = definition.relPath
|
||||
else:
|
||||
filepath = base_dir + definition.relPath
|
||||
|
||||
var texture := load(filepath)
|
||||
|
||||
# Cannot load texture
|
||||
if texture == null:
|
||||
push_error("Cannot access source texture: %s. Please include this file in the Godot project." % [filepath])
|
||||
return null
|
||||
|
||||
var image: Image = texture.get_image()
|
||||
|
||||
# Convert texture from CompressedTexture2D to CanvasTexture
|
||||
if (Util.options.atlas_texture_type == AtlasTextureType.CanvasTexture):
|
||||
var canvas_texture = CanvasTexture.new()
|
||||
canvas_texture.diffuse_texture = texture
|
||||
texture = canvas_texture
|
||||
|
||||
var tile_size: int = definition.gridSize
|
||||
var margin: int = definition.padding
|
||||
var separation: int = definition.spacing
|
||||
var grid_w: int = definition.__cWid
|
||||
var grid_h: int = definition.__cHei
|
||||
|
||||
var source := TileSetAtlasSource.new()
|
||||
|
||||
# Apply TileSet properties
|
||||
if source.texture == null or source.texture.get_class() != texture.get_class():
|
||||
source.texture = texture
|
||||
|
||||
source.resource_name = definition.identifier
|
||||
source.margins = Vector2i(margin, margin)
|
||||
source.separation = Vector2i(separation, separation)
|
||||
source.texture_region_size = Vector2(tile_size, tile_size)
|
||||
source.use_texture_padding = false
|
||||
|
||||
# Create/remove tiles in non-empty/empty cells.
|
||||
for y in range(0, grid_h):
|
||||
for x in range(0, grid_w):
|
||||
var coords := Vector2i(x,y)
|
||||
var tile_region := TileUtil.get_tile_region(coords, tile_size, margin, separation, grid_w)
|
||||
var tile_image := image.get_region(tile_region)
|
||||
|
||||
if not tile_image.is_invisible():
|
||||
if source.get_tile_at_coords(coords) == Vector2i(-1,-1):
|
||||
source.create_tile(coords)
|
||||
elif not source.get_tile_at_coords(coords) == Vector2i(-1,-1):
|
||||
# TODO: Make this an import flag
|
||||
source.remove_tile(coords)
|
||||
|
||||
# Add definition UID to references
|
||||
Util.add_tileset_reference(definition.uid, source)
|
||||
|
||||
return source
|
||||
|
||||
static func add_tileset_custom_data(
|
||||
definition: Dictionary,
|
||||
tileset: TileSet,
|
||||
source: TileSetAtlasSource,
|
||||
grid_w: int
|
||||
) -> void:
|
||||
|
||||
if not definition.has("enumTags"):
|
||||
return
|
||||
|
||||
var customData: Array = definition.customData
|
||||
var custom_name: String = "LDTK Custom"
|
||||
clear_custom_data(tileset, custom_name)
|
||||
|
||||
if not customData.is_empty():
|
||||
ensure_custom_layer(tileset, custom_name)
|
||||
for entry in customData:
|
||||
var coords := TileUtil.tileid_to_grid(entry.tileId, grid_w)
|
||||
var tile_data: TileData = source.get_tile_data(coords, 0)
|
||||
if not tile_data == null:
|
||||
tile_data.set_custom_data(custom_name, entry.data)
|
||||
|
||||
var custom_enum_name: String = "LDTK Custom Enum"
|
||||
clear_custom_data(tileset, custom_enum_name)
|
||||
|
||||
var enumTags: Array = definition.enumTags
|
||||
if not enumTags.is_empty():
|
||||
ensure_custom_layer(tileset, custom_enum_name, TYPE_ARRAY)
|
||||
|
||||
for enumTag in enumTags:
|
||||
for tileId in enumTag.tileIds:
|
||||
var coords := TileUtil.tileid_to_grid(tileId, grid_w)
|
||||
var tile_data: TileData = source.get_tile_data(coords, 0)
|
||||
if not tile_data == null:
|
||||
# Add to already existing tags
|
||||
var tile_tags: Array = tile_data.get_custom_data(custom_enum_name)
|
||||
tile_tags.append(enumTag.enumValueId)
|
||||
tile_data.set_custom_data(custom_enum_name, tile_tags)
|
||||
|
||||
# Ensure custom data layer exists by name
|
||||
static func ensure_custom_layer(
|
||||
tileset: TileSet,
|
||||
layer_name: String,
|
||||
layer_type: int = TYPE_STRING
|
||||
) -> void:
|
||||
if tileset.get_custom_data_layer_by_name(layer_name) != -1:
|
||||
return
|
||||
var index_to_add = tileset.get_custom_data_layers_count()
|
||||
tileset.add_custom_data_layer(index_to_add)
|
||||
tileset.set_custom_data_layer_name(index_to_add, layer_name)
|
||||
tileset.set_custom_data_layer_type(index_to_add, layer_type)
|
||||
|
||||
# Clear custom data by layer name
|
||||
static func clear_custom_data(tileset: TileSet, layer_name: String) -> void:
|
||||
var layer = tileset.get_custom_data_layer_by_name(layer_name)
|
||||
if layer == -1:
|
||||
return
|
||||
tileset.remove_custom_data_layer(layer)
|
||||
|
||||
# Create an AtlasSource from IntGrid data
|
||||
static func create_intgrid_source(definition: Dictionary) -> TileSetAtlasSource:
|
||||
var values: Array = definition.intGridValues
|
||||
var grid_size: int = definition.gridSize
|
||||
|
||||
# Create texture from IntGrid values
|
||||
var width := grid_size * values.size()
|
||||
var height := grid_size
|
||||
var image := Image.create(width, height, false, Image.FORMAT_RGB8)
|
||||
|
||||
for index in range(0, values.size()):
|
||||
var value: Dictionary = values[index]
|
||||
var rect := Rect2i(index * grid_size, 0, grid_size, grid_size)
|
||||
var color := Color.from_string(value.color, Color.MAGENTA)
|
||||
image.fill_rect(rect, color)
|
||||
|
||||
var texture = ImageTexture.create_from_image(image)
|
||||
|
||||
var source := TileSetAtlasSource.new()
|
||||
source.resource_name = definition.identifier + "_Tiles"
|
||||
source.texture = texture
|
||||
source.texture_region_size = Vector2i(grid_size, grid_size)
|
||||
|
||||
# Create tiles
|
||||
for index in range(0, values.size()):
|
||||
var coords := Vector2i(index, 0)
|
||||
if not source.has_tile(coords):
|
||||
source.create_tile(coords)
|
||||
|
||||
return source
|
||||
|
||||
# Save TileSets as Resources
|
||||
static func save_tilesets(tilesets: Dictionary, base_dir: String) -> Dictionary:
|
||||
var save_path = base_dir + "tilesets/"
|
||||
var gen_files := {}
|
||||
var directory = DirAccess.open(base_dir)
|
||||
if not directory.dir_exists(save_path):
|
||||
directory.make_dir_recursive(save_path)
|
||||
|
||||
var tileset_names = tilesets.values().map(func(elem): return elem.resource_name)
|
||||
Util.print("item_save", "Saving Tilesets: [color=#fe8019]%s[/color]" % [tileset_names], 1)
|
||||
|
||||
for key in tilesets.keys():
|
||||
var tileset: TileSet = tilesets.get(key)
|
||||
if tileset.get_source_count() == 0:
|
||||
continue
|
||||
|
||||
var file_name = tileset.resource_name
|
||||
var file_path = "%s%s.%s" % [save_path, file_name, "res"]
|
||||
var err = ResourceSaver.save(tileset, file_path)
|
||||
if err == OK:
|
||||
gen_files[key] = file_path
|
||||
|
||||
return gen_files
|
||||
|
||||
static func get_entity_def_tiles(definitions: Dictionary, tilesets: Dictionary) -> Dictionary:
|
||||
# TODO: Loop through EntityDefs, and turn 'Tile' into an Image.
|
||||
for def in definitions.entities:
|
||||
var entity: Dictionary = definitions.entities[def]
|
||||
if (entity.tile == null):
|
||||
continue
|
||||
# Find associated TileSet
|
||||
var texture = FieldUtil.__parse_tile(entity.tile)
|
||||
entity.tile = texture
|
||||
|
||||
return definitions
|
||||
|
||||
# Collect all layer tileset overrides. Later we'll ensure these sources are included in TileSet resources.
|
||||
static func get_tileset_overrides(world_data: Dictionary) -> Dictionary:
|
||||
var overrides := {}
|
||||
for level in world_data.levels:
|
||||
for layer in level.layerInstances:
|
||||
if layer.overrideTilesetUid == null:
|
||||
continue
|
||||
var gridSize: int = layer.__gridSize
|
||||
var overrideUid: int = layer.overrideTilesetUid
|
||||
if overrideUid != null:
|
||||
if not overrides.has(gridSize):
|
||||
overrides[gridSize] = []
|
||||
var gridsize_overrides: Array = overrides[gridSize]
|
||||
if not gridsize_overrides.has(overrideUid):
|
||||
gridsize_overrides.append(overrideUid)
|
||||
return overrides
|
1
godot/addons/ldtk-importer/src/tileset.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://etx2miclpo5a
|
108
godot/addons/ldtk-importer/src/util/definition_util.gd
Normal file
@ -0,0 +1,108 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util.gd")
|
||||
|
||||
# When building definitions we are only collecting data we need and do some pre-parsing to make the next steps easier.
|
||||
|
||||
static func build_definitions(world_data: Dictionary) -> Dictionary:
|
||||
var definitions := {
|
||||
"enums": resolve_enum_definitions(world_data.defs.enums),
|
||||
"entities": resolve_entity_definitions(world_data.defs.entities),
|
||||
"layers": resolve_layer_definitions(world_data.defs.layers),
|
||||
"tilesets": resolve_tileset_definitions(world_data.defs.tilesets, world_data.defs.layers),
|
||||
"level_fields": resolve_level_field_definitions(world_data.defs.levelFields),
|
||||
}
|
||||
return definitions
|
||||
|
||||
static func resolve_layer_definitions(layer_defs: Array) -> Dictionary:
|
||||
var resolved_layer_defs := {}
|
||||
|
||||
for layer_def in layer_defs:
|
||||
resolved_layer_defs[layer_def.uid] = {
|
||||
"uid": layer_def.uid,
|
||||
"type": layer_def.type,
|
||||
"identifier": layer_def.identifier,
|
||||
"gridSize": layer_def.gridSize,
|
||||
"offset": Vector2i(layer_def.pxOffsetX, layer_def.pxOffsetY),
|
||||
"parallax": Vector2(layer_def.parallaxFactorX, layer_def.parallaxFactorY),
|
||||
"parallaxScaling": layer_def.parallaxScaling,
|
||||
"intGridValues": layer_def.intGridValues
|
||||
}
|
||||
|
||||
return resolved_layer_defs
|
||||
|
||||
static func resolve_entity_definitions(entity_defs: Array) -> Dictionary:
|
||||
var resolved_entity_defs := {}
|
||||
|
||||
for entity_def in entity_defs:
|
||||
resolved_entity_defs[entity_def.uid] = {
|
||||
"identifier": entity_def.identifier,
|
||||
"color": Color.from_string(entity_def.color, Color.MAGENTA),
|
||||
"renderMode": entity_def.renderMode,
|
||||
"hollow": entity_def.hollow,
|
||||
"tags": entity_def.tags,
|
||||
"field_defs": resolve_entity_field_defs(entity_def.fieldDefs),
|
||||
"tile": entity_def.uiTileRect
|
||||
}
|
||||
|
||||
return resolved_entity_defs
|
||||
|
||||
static func resolve_entity_field_defs(field_defs: Array) -> Dictionary:
|
||||
var resolved_entity_field_defs := {}
|
||||
|
||||
for field_def in field_defs:
|
||||
resolved_entity_field_defs[int(field_def.uid)] = {
|
||||
"identifier": field_def.identifier,
|
||||
"type": field_def.__type,
|
||||
}
|
||||
|
||||
return resolved_entity_field_defs
|
||||
|
||||
static func resolve_tileset_definitions(tileset_defs: Array, layer_defs: Array) -> Dictionary:
|
||||
var resolved_tileset_defs := {}
|
||||
|
||||
for tileset_def in tileset_defs:
|
||||
resolved_tileset_defs[tileset_def.uid] = {
|
||||
"uid": tileset_def.uid,
|
||||
"identifier": tileset_def.identifier,
|
||||
"relPath": tileset_def.relPath,
|
||||
"gridSize": tileset_def.tileGridSize,
|
||||
"pxWid": tileset_def.pxWid,
|
||||
"pxHei": tileset_def.pxHei,
|
||||
"spacing": tileset_def.spacing,
|
||||
"padding": tileset_def.padding,
|
||||
"tags": tileset_def.tags,
|
||||
"enumTagUid": tileset_def.tagsSourceEnumUid,
|
||||
"enumTags": tileset_def.enumTags,
|
||||
"customData": tileset_def.customData,
|
||||
"__cWid": tileset_def.__cWid,
|
||||
"__cHei": tileset_def.__cHei,
|
||||
}
|
||||
return resolved_tileset_defs
|
||||
|
||||
static func resolve_enum_definitions(enum_defs: Array) -> Dictionary:
|
||||
var resolved_enum_defs := {}
|
||||
|
||||
for enum_def in enum_defs:
|
||||
var uid = enum_def.uid
|
||||
var values := []
|
||||
for value in enum_def.values:
|
||||
values.append({
|
||||
"value": value.id,
|
||||
"color": Color.from_string(str(value.color), Color.MAGENTA),
|
||||
"tileRect": value.tileRect
|
||||
})
|
||||
resolved_enum_defs[uid] = values
|
||||
|
||||
return resolved_enum_defs
|
||||
|
||||
static func resolve_level_field_definitions(level_field_defs: Array) -> Dictionary:
|
||||
var resolved_level_field_defs := {}
|
||||
|
||||
for level_field_def in level_field_defs:
|
||||
resolved_level_field_defs[level_field_def.uid] = {
|
||||
"identifier": level_field_def.identifier,
|
||||
"type": level_field_def.__type
|
||||
}
|
||||
|
||||
return resolved_level_field_defs
|
@ -0,0 +1 @@
|
||||
uid://10dqdfkkqwa2
|
126
godot/addons/ldtk-importer/src/util/field-util.gd
Normal file
@ -0,0 +1,126 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util.gd")
|
||||
const TileUtil = preload("tile-util.gd")
|
||||
|
||||
static var hitUnresolved := false
|
||||
static var current_field_name: String = "N/A"
|
||||
|
||||
static func create_fields(fields: Array, entity: Variant = null) -> Dictionary:
|
||||
var dict := {}
|
||||
for field in fields:
|
||||
var key: String = field.__identifier
|
||||
current_field_name = key
|
||||
dict[key] = parse_field(field)
|
||||
|
||||
if not Util.options.resolve_entityrefs:
|
||||
hitUnresolved = false
|
||||
continue
|
||||
|
||||
if hitUnresolved and entity != null:
|
||||
if dict[key] is Array:
|
||||
for index in range(dict[key].size()):
|
||||
Util.add_unresolved_reference(dict[key], index, entity)
|
||||
else:
|
||||
Util.add_unresolved_reference(dict, key, entity)
|
||||
hitUnresolved = false
|
||||
|
||||
return dict
|
||||
|
||||
static func parse_field(field: Dictionary) -> Variant:
|
||||
var value: Variant = field.__value
|
||||
if value == null:
|
||||
return null
|
||||
|
||||
var type := field.__type as String
|
||||
|
||||
# Handle enum string
|
||||
var localEnum: String
|
||||
if type.contains("LocalEnum"):
|
||||
var regex = RegEx.new()
|
||||
regex.compile("(?<=\\.)\\w+")
|
||||
localEnum = regex.search(type).get_string()
|
||||
|
||||
if type.begins_with("Array"):
|
||||
type = "Array<LocalEnum>"
|
||||
else:
|
||||
type = "LocalEnum"
|
||||
|
||||
# Match field type
|
||||
match type:
|
||||
"Int":
|
||||
return int(value) as int
|
||||
"Color":
|
||||
return Color.from_string(value, Color.MAGENTA) as Color
|
||||
"Point":
|
||||
return __parse_point(value.cx, value.cy) as Vector2i
|
||||
"Tile":
|
||||
return __parse_tile(value) as AtlasTexture
|
||||
"EntityRef":
|
||||
hitUnresolved = true
|
||||
return value.entityIid as String
|
||||
"LocalEnum":
|
||||
return __parse_enum(localEnum, value) as String
|
||||
"Array<Int>":
|
||||
return value
|
||||
"Array<Color>":
|
||||
return value.map(
|
||||
func (color):
|
||||
return Color.from_string(color, Color.MAGENTA)
|
||||
)
|
||||
"Array<Point>":
|
||||
return value.map(
|
||||
func (point):
|
||||
return Vector2i(point.cx, point.cy)
|
||||
)
|
||||
"Array<Tile>":
|
||||
return value.map(
|
||||
func(tile) -> AtlasTexture:
|
||||
return __parse_tile(tile)
|
||||
)
|
||||
"Array<EntityRef>":
|
||||
hitUnresolved = true
|
||||
return value.map(
|
||||
func (ref) -> String:
|
||||
return ref.entityIid
|
||||
)
|
||||
"Array<LocalEnum>":
|
||||
var enums: Array[String] = []
|
||||
for index in range(value.size()):
|
||||
var parsed_enum = __parse_enum(localEnum, value[index])
|
||||
enums.append(parsed_enum)
|
||||
return enums
|
||||
_:
|
||||
return value
|
||||
|
||||
static func __parse_point(x: int, y: int) -> Vector2i:
|
||||
# NOTE: would convert gridcoords to pixelcoords here, but needs more data
|
||||
# LDTKEntity currently converts it using LayerDefinition.
|
||||
return Vector2i(x,y)
|
||||
|
||||
static func __parse_enum(enum_str: String, value: String) -> String:
|
||||
var result: String = "%s.%s" % [enum_str, value]
|
||||
return result
|
||||
|
||||
static func __parse_tile(value: Dictionary) -> AtlasTexture:
|
||||
var texture := AtlasTexture.new()
|
||||
var atlas: TileSetAtlasSource
|
||||
|
||||
if Util.tileset_refs.has(int(value.tilesetUid)):
|
||||
atlas = Util.tileset_refs[int(value.tilesetUid)]
|
||||
|
||||
if atlas == null :
|
||||
print("Tileset Refs: ", Util.tileset_refs)
|
||||
print("FieldDef<Tile> '%s' could not find Tileset with UID '%s'. Using empty texture instead. Please fix this in your LDtk file." % [current_field_name, value.tilesetUid])
|
||||
return texture
|
||||
|
||||
texture.atlas = atlas.texture
|
||||
|
||||
var coords = TileUtil.px_to_grid(
|
||||
Vector2(value.x, value.y),
|
||||
atlas.texture_region_size,
|
||||
atlas.margins,
|
||||
atlas.separation
|
||||
)
|
||||
texture.region = atlas.get_tile_texture_region(coords) as Rect2i
|
||||
return texture
|
1
godot/addons/ldtk-importer/src/util/field-util.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dp7qwjbyo1kx0
|
68
godot/addons/ldtk-importer/src/util/layer-util.gd
Normal file
@ -0,0 +1,68 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util.gd")
|
||||
const EntityPlaceHolder = preload("../components/ldtk-entity.tscn")
|
||||
const FieldUtil = preload("field-util.gd")
|
||||
|
||||
# Counter reset per level, used when creating EntityPlacholders
|
||||
static var placeholder_counts := {}
|
||||
|
||||
static func parse_entity_instances(
|
||||
entities: Array,
|
||||
entity_defs: Dictionary,
|
||||
pathResolver: Node2D
|
||||
) -> Array:
|
||||
return entities.map(
|
||||
func(entity):
|
||||
var definition = entity_defs[entity.defUid]
|
||||
return {
|
||||
"iid": entity.iid,
|
||||
"identifier": entity.__identifier,
|
||||
"smart_color": Color.from_string(entity.__smartColor, Color.WHITE),
|
||||
"size": Vector2i(entity.width, entity.height),
|
||||
"position": Vector2i(entity.px[0], entity.px[1]),
|
||||
"pivot": Vector2(entity.__pivot[0], entity.__pivot[1]),
|
||||
"fields": FieldUtil.create_fields(entity.fieldInstances, pathResolver),
|
||||
"definition": definition,
|
||||
}
|
||||
)
|
||||
|
||||
static func create_entity_placeholder(layer: Node2D, data: Dictionary) -> LDTKEntity:
|
||||
var placeholder: LDTKEntity = EntityPlaceHolder.instantiate()
|
||||
var count: int = __placeholder_count(data.identifier)
|
||||
placeholder.name = data.identifier
|
||||
|
||||
if count > 1:
|
||||
placeholder.name += str(count)
|
||||
|
||||
# Set properties
|
||||
for prop in data.keys():
|
||||
placeholder[prop] = data[prop]
|
||||
|
||||
layer.add_child(placeholder)
|
||||
return placeholder
|
||||
|
||||
static func __placeholder_count(name: String) -> int:
|
||||
if not name in placeholder_counts:
|
||||
placeholder_counts[name] = 1
|
||||
else:
|
||||
placeholder_counts[name] += 1
|
||||
return placeholder_counts[name]
|
||||
|
||||
static func create_layer_tilemap(layer_data: Dictionary) -> TileMapLayer:
|
||||
var grid_size = int(layer_data.__gridSize)
|
||||
|
||||
var tilemap := TileMapLayer.new()
|
||||
tilemap.name = layer_data.__identifier
|
||||
tilemap.tile_set = Util.tilesets.get(grid_size, null)
|
||||
var offset = Vector2(layer_data.__pxTotalOffsetX, layer_data.__pxTotalOffsetY)
|
||||
tilemap.position = offset
|
||||
|
||||
return tilemap
|
||||
|
||||
static func create_tilemap_child(tilemap: TileMapLayer) -> TileMapLayer:
|
||||
var child := TileMapLayer.new()
|
||||
var count := tilemap.get_child_count() + 1
|
||||
child.name = tilemap.name + str(count)
|
||||
child.tile_set = tilemap.tile_set
|
||||
return child
|
1
godot/addons/ldtk-importer/src/util/layer-util.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://cm8kt5fyu4max
|
54
godot/addons/ldtk-importer/src/util/level-util.gd
Normal file
@ -0,0 +1,54 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util.gd")
|
||||
|
||||
static func get_world_position(
|
||||
world_data: Dictionary,
|
||||
level_data: Dictionary
|
||||
) -> Vector2i:
|
||||
|
||||
var layout = world_data.worldLayout
|
||||
|
||||
if layout == "GridVania" or layout == "Free":
|
||||
return Vector2i(level_data.worldX, level_data.worldY)
|
||||
elif layout == "LinearHorizontal" or layout == "LinearVertical":
|
||||
# List level uids in order.
|
||||
var level_uids: Array = world_data.levels.map(
|
||||
func(item):
|
||||
return item.uid
|
||||
)
|
||||
# Find level index
|
||||
var index = level_uids.find(level_data.uid)
|
||||
if index == 0:
|
||||
return Vector2i(0,0)
|
||||
|
||||
if layout == "LinearHorizontal":
|
||||
var x: int = world_data.levels.slice(0, index).reduce(
|
||||
func (accum, current):
|
||||
return accum + current.pxWid
|
||||
, 0)
|
||||
return Vector2i(x, 0)
|
||||
else:
|
||||
var y: int = world_data.levels.slice(0, index).reduce(
|
||||
func (accum, current):
|
||||
return accum + current.pHei
|
||||
, 0)
|
||||
return Vector2i(0, y)
|
||||
else:
|
||||
push_warning("World layout not supported", world_data.worldLayout)
|
||||
return Vector2i.ZERO
|
||||
|
||||
|
||||
static func get_external_level(
|
||||
level_data: Dictionary,
|
||||
base_dir: String
|
||||
) -> Dictionary:
|
||||
|
||||
var level_file: String = base_dir + level_data.externalRelPath
|
||||
var new_level_data: Dictionary = Util.parse_file(level_file)
|
||||
if not new_level_data == null:
|
||||
return new_level_data
|
||||
else:
|
||||
push_warning("Could not parse external level: ", level_file)
|
||||
|
||||
return level_data
|
1
godot/addons/ldtk-importer/src/util/level-util.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://oc513xtwqvf1
|
126
godot/addons/ldtk-importer/src/util/tile-util.gd
Normal file
@ -0,0 +1,126 @@
|
||||
@tool
|
||||
|
||||
# Flip a vector based on a bitset
|
||||
static func _flip_vector_array_with_bitset(
|
||||
vecs: PackedVector2Array,
|
||||
bitset: int
|
||||
) -> PackedVector2Array:
|
||||
var new_vecs = PackedVector2Array(vecs)
|
||||
for point_idx in range(vecs.size()):
|
||||
var new_vec = Vector2(vecs[point_idx])
|
||||
if bitset & 1:
|
||||
new_vec.x = -new_vec.x
|
||||
if bitset & 2:
|
||||
new_vec.y = -new_vec.y
|
||||
new_vecs[point_idx] = new_vec
|
||||
return new_vecs
|
||||
|
||||
# Copy over and rotate extra tiledata
|
||||
static func copy_and_modify_tile_data(
|
||||
tile_data: TileData,
|
||||
orig_tile_data: TileData,
|
||||
physics_layers_cnt: int,
|
||||
navigation_layers_cnt: int,
|
||||
occluder_layers_cnt: int,
|
||||
bitset: int,
|
||||
) -> void:
|
||||
# Copy over physics
|
||||
for pli in range(physics_layers_cnt):
|
||||
var polygon_cnt = orig_tile_data.get_collision_polygons_count(pli)
|
||||
if polygon_cnt == 0:
|
||||
# We have no polygon for this layer
|
||||
continue
|
||||
for pi in range(polygon_cnt):
|
||||
tile_data.add_collision_polygon(pli)
|
||||
var points: PackedVector2Array = _flip_vector_array_with_bitset(orig_tile_data.get_collision_polygon_points(pli, pi), bitset)
|
||||
tile_data.set_collision_polygon_points(pli, pi, points)
|
||||
tile_data.set_constant_angular_velocity(pli, orig_tile_data.get_constant_angular_velocity(pli))
|
||||
var linvel = Vector2(orig_tile_data.get_constant_linear_velocity(pli))
|
||||
if bitset & TileSetAtlasSource.TRANSFORM_FLIP_H:
|
||||
linvel.x = -linvel.x
|
||||
if bitset & TileSetAtlasSource.TRANSFORM_FLIP_V:
|
||||
linvel.y = -linvel.y
|
||||
tile_data.set_constant_linear_velocity(pli, linvel)
|
||||
|
||||
# Copy over navigation
|
||||
for navi in range(navigation_layers_cnt):
|
||||
var nav_polygon: NavigationPolygon = orig_tile_data.get_navigation_polygon(navi)
|
||||
if nav_polygon == null:
|
||||
# We have no polygon for this layer
|
||||
continue
|
||||
var new_polygon = NavigationPolygon.new()
|
||||
for outline_idx in range(nav_polygon.get_outline_count()):
|
||||
var vertices = _flip_vector_array_with_bitset(nav_polygon.get_outline(outline_idx), bitset)
|
||||
new_polygon.add_outline(vertices)
|
||||
new_polygon.make_polygons_from_outlines()
|
||||
tile_data.set_navigation_polygon(navi, new_polygon)
|
||||
|
||||
# Copy over occluder
|
||||
for occi in range(occluder_layers_cnt):
|
||||
var occluder: OccluderPolygon2D = orig_tile_data.get_occluder(occi)
|
||||
if occluder == null:
|
||||
# We have no polygon for this layer
|
||||
continue
|
||||
var new_occluder: OccluderPolygon2D = OccluderPolygon2D.new()
|
||||
new_occluder.cull_mode = occluder.cull_mode
|
||||
new_occluder.closed = occluder.closed
|
||||
new_occluder.polygon = _flip_vector_array_with_bitset(occluder.polygon, bitset)
|
||||
tile_data.set_occluder(occi, new_occluder)
|
||||
|
||||
# Flip depending on bitset
|
||||
if bitset & TileSetAtlasSource.TRANSFORM_FLIP_H:
|
||||
tile_data.set_flip_h(true)
|
||||
if bitset & TileSetAtlasSource.TRANSFORM_FLIP_V:
|
||||
tile_data.set_flip_v(true)
|
||||
|
||||
# Get Rect of Tile for an AtlasSource using LDTK tileset data
|
||||
static func get_tile_region(
|
||||
coords: Vector2i,
|
||||
grid_size: int,
|
||||
padding: int,
|
||||
spacing: int,
|
||||
grid_w: int
|
||||
) -> Rect2i:
|
||||
var pixel_coords = grid_to_px(coords, grid_size, padding, spacing)
|
||||
return Rect2i(pixel_coords, Vector2i(grid_size, grid_size))
|
||||
|
||||
# Convert grid coords to pixel coords
|
||||
static func grid_to_px(
|
||||
grid_coords: Vector2i,
|
||||
grid_size: int,
|
||||
padding: int,
|
||||
spacing: int
|
||||
) -> Vector2i:
|
||||
var x: int = padding + grid_coords.x * (grid_size + spacing)
|
||||
var y: int = padding + grid_coords.y * (grid_size + spacing)
|
||||
return Vector2i(x, y)
|
||||
|
||||
# Converts px coords to grid coords
|
||||
static func px_to_grid(
|
||||
px_coords: Vector2,
|
||||
grid_size: Vector2,
|
||||
padding: Vector2 = Vector2.ZERO,
|
||||
spacing: Vector2 = Vector2.ZERO
|
||||
) -> Vector2i:
|
||||
var x: int = round((px_coords.x - padding.x) / (grid_size.x + spacing.x))
|
||||
var y: int = round((px_coords.y - padding.y) / (grid_size.y + spacing.y))
|
||||
return Vector2i(x, y)
|
||||
|
||||
# Convert TileId to grid coords
|
||||
static func tileid_to_grid(tile_id: int, grid_w: int) -> Vector2i:
|
||||
var y := int(tile_id / grid_w)
|
||||
var x := tile_id % grid_w
|
||||
return Vector2i(x, y)
|
||||
|
||||
static func index_to_grid(index: int, grid_w: int) -> Vector2i:
|
||||
var x: int = floor(index % grid_w)
|
||||
var y: int = floor(index / grid_w)
|
||||
return Vector2i(x, y)
|
||||
|
||||
# Converts '1' to (TRANSFORM_FLIP_H), and so on.
|
||||
static func get_tile_flip_mask(index: int) -> int:
|
||||
# 0 -> 0
|
||||
# 1 -> TileSetAtlasSource.TRANSFORM_FLIP_H (4096)
|
||||
# 2 -> TileSetAtlasSource.TRANSFORM_FLIP_V (8192)
|
||||
# 3 -> TileSetAtlasSource.TRANSFORM_FLIP_H | TileSetAtlasSource.TRANSFORM_FLIP_H (12_228)
|
||||
return index << 12
|
1
godot/addons/ldtk-importer/src/util/tile-util.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bp3rb0p6dsayi
|
48
godot/addons/ldtk-importer/src/util/time-util.gd
Normal file
@ -0,0 +1,48 @@
|
||||
@tool
|
||||
|
||||
## Helper Script to track performance of the importer.
|
||||
|
||||
enum { SAVE, LOAD, GENERAL, TILES, POST_IMPORT, TOTAL }
|
||||
|
||||
static var category_time := {
|
||||
SAVE: 0,
|
||||
LOAD: 0,
|
||||
GENERAL: 0,
|
||||
TILES: 0,
|
||||
POST_IMPORT: 0,
|
||||
TOTAL : 0,
|
||||
}
|
||||
|
||||
static var category_name := {
|
||||
SAVE: "save",
|
||||
LOAD: "load",
|
||||
GENERAL : "general",
|
||||
TILES: "tiles",
|
||||
POST_IMPORT: "post-import",
|
||||
TOTAL: "total"
|
||||
}
|
||||
|
||||
static func log_time(category: int, time: int = 0) -> void:
|
||||
if category_time.has(category):
|
||||
category_time[category] += time
|
||||
else:
|
||||
push_warning("No DebugTime Category '%s'" % [category_name[category]])
|
||||
|
||||
static func clear_time() -> void:
|
||||
for category in category_time:
|
||||
category_time[category] = 0
|
||||
|
||||
static func get_total_time() -> int:
|
||||
var sum: int = 0
|
||||
for category in category_time:
|
||||
if category != TOTAL:
|
||||
sum += category_time[category]
|
||||
return sum
|
||||
|
||||
static func get_result() -> String:
|
||||
var result: String = "Performance Results:"
|
||||
for category in category_time:
|
||||
if category != TOTAL:
|
||||
result += "\n [color=#8ec07c]%s [color=slategray](%sms)[/color]" % [category_name[category], category_time[category]]
|
||||
result.indent("\t")
|
||||
return result
|
1
godot/addons/ldtk-importer/src/util/time-util.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://bynwlcmpnsjit
|
226
godot/addons/ldtk-importer/src/util/util.gd
Normal file
@ -0,0 +1,226 @@
|
||||
@tool
|
||||
|
||||
const DebugTime = preload("time-util.gd")
|
||||
|
||||
enum LDTK_VERSION {
|
||||
FUTURE,
|
||||
v1_5,
|
||||
v1_4,
|
||||
v1_3,
|
||||
v1_2,
|
||||
v1_0,
|
||||
UNSUPPORTED
|
||||
}
|
||||
static var file_version = LDTK_VERSION.UNSUPPORTED
|
||||
|
||||
# Stores import flags (used throughout the importer)
|
||||
static var options := {}
|
||||
|
||||
static func parse_file(source_file: String) -> Dictionary:
|
||||
var json := FileAccess.open(source_file, FileAccess.READ)
|
||||
if json == null:
|
||||
push_error("\nFailed to open file: ", source_file)
|
||||
return {}
|
||||
var data := JSON.parse_string(json.get_as_text())
|
||||
return data
|
||||
|
||||
static func check_version(version: String, latest_version: String) -> bool:
|
||||
if version.begins_with("0."):
|
||||
push_error("LDTK version out of date. Please update LDtk to ", latest_version)
|
||||
file_version = LDTK_VERSION.UNSUPPORTED
|
||||
return false
|
||||
|
||||
var major_minor = version.substr(0, 3)
|
||||
match major_minor:
|
||||
"1.0", "1.1":
|
||||
file_version = LDTK_VERSION.v1_0
|
||||
"1.2":
|
||||
file_version = LDTK_VERSION.v1_2
|
||||
"1.3":
|
||||
file_version = LDTK_VERSION.v1_3
|
||||
"1.4":
|
||||
file_version = LDTK_VERSION.v1_4
|
||||
"1.5":
|
||||
file_version = LDTK_VERSION.v1_5
|
||||
_:
|
||||
push_warning("LDtk file version is newer than what is supported. Errors may occur.")
|
||||
file_version = LDTK_VERSION.FUTURE
|
||||
return true
|
||||
|
||||
static func recursive_set_owner(node: Node, owner: Node) -> void:
|
||||
node.set_owner(owner)
|
||||
for child in node.get_children():
|
||||
# Child is NOT an instantiated scene - this would otherwise cause errors
|
||||
if child.scene_file_path == "":
|
||||
recursive_set_owner(child, owner)
|
||||
else:
|
||||
child.set_owner(owner)
|
||||
|
||||
#region Performance Measurement
|
||||
|
||||
static var last_time: int = 0
|
||||
static var time_history: Array[Dictionary] = []
|
||||
|
||||
static func timer_start(category: int = 0) -> int:
|
||||
var t: int = Time.get_ticks_msec()
|
||||
var d: int = t - last_time
|
||||
last_time = t
|
||||
|
||||
if time_history.size() > 0:
|
||||
# Entering subcategory - log prev category up to here
|
||||
var last: Dictionary = time_history[-1]
|
||||
DebugTime.log_time(last.category, d)
|
||||
|
||||
time_history.append({"category": category, "time": t, "init": t})
|
||||
return t
|
||||
|
||||
static func timer_finish(message: String, indent: int = 0, doPrint: bool = true) -> int:
|
||||
if time_history.size() == 0:
|
||||
push_error("Unbalanced DebugTime stack")
|
||||
var last: Dictionary = time_history.pop_back()
|
||||
var t: int = Time.get_ticks_msec()
|
||||
var d: int = t - last.time
|
||||
last_time = t
|
||||
DebugTime.log_time(last.category, d)
|
||||
|
||||
if time_history.size() > 0:
|
||||
time_history[-1].time = t
|
||||
|
||||
if (doPrint and options.verbose_output):
|
||||
# Print 'gross' duration for this block
|
||||
var d2: int = t - last.init
|
||||
print_time("item_info_time", message, d2, indent)
|
||||
return d
|
||||
|
||||
static func timer_reset() -> void:
|
||||
last_time = 0
|
||||
time_history.clear()
|
||||
DebugTime.clear_time()
|
||||
|
||||
#endregion
|
||||
|
||||
#region Debug Output
|
||||
|
||||
const PRINT_SNIPPET := {
|
||||
"import_start": "[bgcolor=#ffcc00][color=black][LDTK][/color][/bgcolor][color=#ffcc00] Start Import: [color=#fe8019][i]'%s'[/i][/color]",
|
||||
"import_finish": "[bgcolor=#ffcc00][color=black][LDTK][/color][/bgcolor][color=#ffcc00] Finished Import. [color=slategray](Total Time: %sms)[/color]",
|
||||
"item_ok" : "[color=#b8bb26]• %s ✔[/color]",
|
||||
"item_fail": "[color=#fb4934]• %s ✘[/color]",
|
||||
"item_info": "[color=#8ec07c]• %s [/color]",
|
||||
"item_save": "[color=#ffcc00]• %s [/color]",
|
||||
"item_post_import": "[color=tomato]‣ %s[/color]",
|
||||
"block": "[color=#ffcc00]█[/color] [color=#fe8019]%s[/color]",
|
||||
"item_ok_time": "[color=#b8bb26]• %s ✔[/color]\t[color=slategray](%sms)[/color]",
|
||||
"item_fail_time": "[color=#fb4934]• %s ✘[/color]\t[color=slategray](%sms)[/color]",
|
||||
"item_info_time": "[color=#8ec07c]• %s [/color]\t[color=slategray](%sms)[/color]",
|
||||
"world_post_import": "[color=tomato]‣ World Post-Import: %s[/color]",
|
||||
"level_post_import": "[color=tomato]‣ Level Post-Import: %s[/color]",
|
||||
"tileset_post_import": "[color=tomato]‣ Tileset Post-Import: %s[/color]",
|
||||
"entity_post_import": "[color=tomato]‣ Entity Post-Import: %s[/color]",
|
||||
}
|
||||
|
||||
static func nice_print(type: String, message: String, indent: int = 0) -> void:
|
||||
if PRINT_SNIPPET.has(type):
|
||||
var snippet: String = PRINT_SNIPPET[type]
|
||||
snippet = snippet.indent(str("\t").repeat(indent))
|
||||
print_rich(snippet % [message])
|
||||
else:
|
||||
print_rich(message)
|
||||
|
||||
static func print(type: String, message: String, indent: int = 0) -> void:
|
||||
nice_print(type, message, indent)
|
||||
|
||||
static func print_time(type: String, message: String, time: int = -1, indent: int = 0) -> void:
|
||||
if PRINT_SNIPPET.has(type):
|
||||
var snippet: String = PRINT_SNIPPET[type]
|
||||
snippet = snippet.indent(str("\t").repeat(indent))
|
||||
print_rich(snippet % [message, time])
|
||||
else:
|
||||
print_rich(message)
|
||||
|
||||
#endregion
|
||||
|
||||
#region References
|
||||
static var tilesets := {}
|
||||
static var tileset_refs := {}
|
||||
static var instance_refs := {}
|
||||
static var unresolved_refs := []
|
||||
static var path_resolvers := []
|
||||
|
||||
static func update_instance_reference(iid: String, instance: Variant) -> void:
|
||||
instance_refs[iid] = instance
|
||||
|
||||
static func add_tileset_reference(uid: int, atlas: TileSetAtlasSource) -> void:
|
||||
tileset_refs[uid] = atlas
|
||||
|
||||
# This is useful for handling entity instances, as they might not exist yet when encountered
|
||||
# or be overwritten at a later stage (e.g. post-import) when importing an LDTK level/world.
|
||||
static func add_unresolved_reference(
|
||||
object: Variant,
|
||||
property: Variant,
|
||||
node: Variant = object,
|
||||
iid: String = str(object[property])
|
||||
) -> void:
|
||||
|
||||
unresolved_refs.append({
|
||||
"object": object,
|
||||
"property": property,
|
||||
"node": node,
|
||||
"iid": iid
|
||||
})
|
||||
|
||||
static func handle_references() -> void:
|
||||
resolve_references()
|
||||
clean_references()
|
||||
clean_resolvers()
|
||||
|
||||
static func resolve_references() -> void:
|
||||
var count := unresolved_refs.size()
|
||||
if (count == 0 or not options.resolve_entityrefs):
|
||||
if (options.verbose_output): nice_print("item_info", "No references to resolve", 1)
|
||||
return
|
||||
else:
|
||||
if (options.verbose_output): nice_print("item_info", "Resolving %s references" % [count], 1)
|
||||
|
||||
var solved_refcount := 0
|
||||
|
||||
for ref in unresolved_refs:
|
||||
var iid: String = ref.iid
|
||||
var object: Variant = ref.object # Expected: Node, Dict or Array
|
||||
var property: Variant = ref.property # Expected: String or Int
|
||||
var node: Variant = ref.node # Expected: Node, but needs to accept null
|
||||
|
||||
if instance_refs.has(iid):
|
||||
var instance = instance_refs[iid]
|
||||
|
||||
if instance is Node and node is Node:
|
||||
# BUG: When using 'Pack Levels', external references cannot be resolved at import time. (e.g. Level_0 -> Level_1)
|
||||
# Internal references can resolve, but Godot pushes the error: Parameter "common_parent" is null.
|
||||
# Currently it's a choice between a bunch of errors (that suppress other messages), or no resolving.
|
||||
if true: #instance.owner != null and node.owner != null:
|
||||
var path = node.get_path_to(instance)
|
||||
if path:
|
||||
object[property] = path
|
||||
else:
|
||||
nice_print("item_fail", "Cannot resolve ref (out-of-bounds?) '%s' '%s'" % [instance.name, node.name], 1)
|
||||
continue
|
||||
else:
|
||||
object[property] = instance
|
||||
|
||||
solved_refcount += 1
|
||||
|
||||
var leftover_refcount: int = unresolved_refs.size() - solved_refcount
|
||||
if leftover_refcount > 0:
|
||||
nice_print("item_info", "Could not resolve %s references, most likely non-existent entities." % [leftover_refcount], 1)
|
||||
|
||||
static func clean_references() -> void:
|
||||
tileset_refs.clear()
|
||||
instance_refs.clear()
|
||||
unresolved_refs.clear()
|
||||
|
||||
static func clean_resolvers() -> void:
|
||||
for resolver in path_resolvers:
|
||||
resolver.free()
|
||||
path_resolvers.clear()
|
||||
|
||||
#endregion
|
1
godot/addons/ldtk-importer/src/util/util.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://ddr8yxjsfqgji
|
89
godot/addons/ldtk-importer/src/world.gd
Normal file
@ -0,0 +1,89 @@
|
||||
@tool
|
||||
|
||||
const Util = preload("util/util.gd")
|
||||
const PostImport = preload("post-import.gd")
|
||||
|
||||
static func create_world(
|
||||
name: String,
|
||||
iid: String,
|
||||
levels: Array,
|
||||
base_dir: String
|
||||
) -> LDTKWorld:
|
||||
|
||||
Util.timer_start(Util.DebugTime.GENERAL)
|
||||
var world = LDTKWorld.new()
|
||||
world.name = name
|
||||
world.iid = iid
|
||||
|
||||
# Update World_Rect
|
||||
var x1 = world.rect.position.x
|
||||
var x2 = world.rect.end.x
|
||||
var y1 = world.rect.position.y
|
||||
var y2 = world.rect.end.y
|
||||
|
||||
var worldDepths := {}
|
||||
|
||||
for level in levels:
|
||||
level.position = level.world_position
|
||||
|
||||
if Util.options.group_world_layers:
|
||||
var worldDepthLayer: LDTKWorldLayer
|
||||
var z_index: int = level.z_index if (level is not PackedScene) else 0
|
||||
if not z_index in worldDepths:
|
||||
worldDepthLayer = LDTKWorldLayer.new()
|
||||
worldDepthLayer.name = "WorldLayer_" + str(z_index)
|
||||
worldDepthLayer.depth = z_index
|
||||
world.add_child(worldDepthLayer)
|
||||
worldDepthLayer.set_owner(world)
|
||||
worldDepths[z_index] = worldDepthLayer
|
||||
else:
|
||||
worldDepthLayer = worldDepths[z_index]
|
||||
worldDepthLayer.add_child(level)
|
||||
else:
|
||||
world.add_child(level)
|
||||
|
||||
x1 = min(x1, level.position.x)
|
||||
y1 = min(y1, level.position.y)
|
||||
x2 = max(x2, level.position.x + level.size.x)
|
||||
y2 = max(y2, level.position.y + level.size.y)
|
||||
|
||||
# Set owner - this ensures nodes get saved correctly
|
||||
level.set_owner(world)
|
||||
if not (Util.options.pack_levels):
|
||||
Util.recursive_set_owner(level, world)
|
||||
|
||||
# Sort WorldLayers based on depth
|
||||
if not worldDepths.is_empty():
|
||||
var keys = worldDepths.keys()
|
||||
keys.sort_custom(func(a,b): return a < b)
|
||||
for i in range(keys.size()):
|
||||
world.move_child(worldDepths[keys[i]], i)
|
||||
|
||||
world.rect.position = Vector2i(x1, y1)
|
||||
world.rect.end = Vector2i(x2, y2)
|
||||
|
||||
Util.timer_finish("World Created", 1)
|
||||
|
||||
# Post-Import
|
||||
if (Util.options.world_post_import):
|
||||
world = PostImport.run_world_post_import(world, Util.options.world_post_import)
|
||||
|
||||
return world
|
||||
|
||||
static func create_multi_world(
|
||||
name: String,
|
||||
iid: String,
|
||||
worlds: Array[LDTKWorld]
|
||||
) -> LDTKWorld:
|
||||
|
||||
var multi_world = LDTKWorld.new()
|
||||
multi_world.name = name
|
||||
multi_world.iid = iid
|
||||
|
||||
worlds.sort_custom(func(a, b): return a.depth < b.depth)
|
||||
|
||||
for world in worlds:
|
||||
multi_world.add_child(world)
|
||||
Util.recursive_set_owner(world, multi_world)
|
||||
|
||||
return multi_world
|
1
godot/addons/ldtk-importer/src/world.gd.uid
Normal file
@ -0,0 +1 @@
|
||||
uid://dxg0dj75iw7w8
|
Before Width: | Height: | Size: 23 KiB After Width: | Height: | Size: 23 KiB |
34
godot/assets/processed/tilesets/sidewalk_128x128.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://d0k22dcuuv8uy"
|
||||
path="res://.godot/imported/sidewalk_128x128.png-cde506a7daa48b45baa91d26762bc08d.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/processed/tilesets/sidewalk_128x128.png"
|
||||
dest_files=["res://.godot/imported/sidewalk_128x128.png-cde506a7daa48b45baa91d26762bc08d.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
Before Width: | Height: | Size: 523 KiB After Width: | Height: | Size: 523 KiB |
34
godot/assets/processed/tilesets/sidewalk_512x512.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b8sqa7vj6amn2"
|
||||
path="res://.godot/imported/sidewalk_512x512.png-14776adf2872cddafc407ac4b59f47ee.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/processed/tilesets/sidewalk_512x512.png"
|
||||
dest_files=["res://.godot/imported/sidewalk_512x512.png-14776adf2872cddafc407ac4b59f47ee.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
Before Width: | Height: | Size: 1.6 MiB After Width: | Height: | Size: 1.6 MiB |
34
godot/assets/raw/residential.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://dm00rxmhhswnc"
|
||||
path="res://.godot/imported/residential.png-262e0878b74a2d5b43ab285d83624e92.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/raw/residential.png"
|
||||
dest_files=["res://.godot/imported/residential.png-262e0878b74a2d5b43ab285d83624e92.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
Before Width: | Height: | Size: 2.5 MiB After Width: | Height: | Size: 2.5 MiB |
34
godot/assets/raw/sidewalk.png.import
Normal file
@ -0,0 +1,34 @@
|
||||
[remap]
|
||||
|
||||
importer="texture"
|
||||
type="CompressedTexture2D"
|
||||
uid="uid://b5gsu4gxtaxrw"
|
||||
path="res://.godot/imported/sidewalk.png-2c383e30cad907bc6b4c98e9b24edac2.ctex"
|
||||
metadata={
|
||||
"vram_texture": false
|
||||
}
|
||||
|
||||
[deps]
|
||||
|
||||
source_file="res://assets/raw/sidewalk.png"
|
||||
dest_files=["res://.godot/imported/sidewalk.png-2c383e30cad907bc6b4c98e9b24edac2.ctex"]
|
||||
|
||||
[params]
|
||||
|
||||
compress/mode=0
|
||||
compress/high_quality=false
|
||||
compress/lossy_quality=0.7
|
||||
compress/hdr_compression=1
|
||||
compress/normal_map=0
|
||||
compress/channel_pack=0
|
||||
mipmaps/generate=false
|
||||
mipmaps/limit=-1
|
||||
roughness/mode=0
|
||||
roughness/src_normal=""
|
||||
process/fix_alpha_border=true
|
||||
process/premult_alpha=false
|
||||
process/normal_map_invert_y=false
|
||||
process/hdr_as_srgb=false
|
||||
process/hdr_clamp_exposure=false
|
||||
process/size_limit=0
|
||||
detect_3d/compress_to=1
|
@ -3,7 +3,7 @@ extends RigidBody2D
|
||||
@onready var cam = $Camera2D
|
||||
|
||||
# zoom range: from close-in to far-out
|
||||
var min_zoom = Vector2(1.0, 1.0)
|
||||
var min_zoom = Vector2(0.5, 0.5)
|
||||
var max_zoom = Vector2(0.2, 0.2)
|
||||
var max_speed = 5000.0 # max expected speed
|
||||
|
||||
|
@ -3,7 +3,7 @@
|
||||
[ext_resource type="Texture2D" uid="uid://cds01w4trqeei" path="res://assets/police_car.png" id="1_7822p"]
|
||||
|
||||
[sub_resource type="RectangleShape2D" id="RectangleShape2D_37kl0"]
|
||||
size = Vector2(631, 1429)
|
||||
size = Vector2(604, 1300)
|
||||
|
||||
[node name="Car" type="Car"]
|
||||
mass = 1300.0
|
||||
@ -11,9 +11,9 @@ inertia = 5e+07
|
||||
|
||||
[node name="CollisionShape2D" type="CollisionShape2D" parent="."]
|
||||
position = Vector2(0.5, -0.5)
|
||||
scale = Vector2(0.5, 0.5)
|
||||
scale = Vector2(0.25, 0.25)
|
||||
shape = SubResource("RectangleShape2D_37kl0")
|
||||
|
||||
[node name="Sprite2D" type="Sprite2D" parent="."]
|
||||
scale = Vector2(0.5, 0.5)
|
||||
scale = Vector2(0.25, 0.25)
|
||||
texture = ExtResource("1_7822p")
|
||||
|
@ -1,39 +1,21 @@
|
||||
[gd_scene load_steps=5 format=3 uid="uid://7713s03g7nxw"]
|
||||
[gd_scene load_steps=3 format=3 uid="uid://7713s03g7nxw"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://ukvcgdjxtkqw" path="res://car.tscn" id="1_80nbo"]
|
||||
[ext_resource type="PackedScene" uid="uid://drkq82mmv8ofa" path="res://test_track.tscn" id="1_e2o6t"]
|
||||
[ext_resource type="Script" uid="uid://bic64vi6lpelp" path="res://car.gd" id="4_7jktm"]
|
||||
[ext_resource type="Texture2D" uid="uid://bxwhirvk4jjpl" path="res://assets/textures/curb_1.png" id="4_fc0e3"]
|
||||
[ext_resource type="PackedScene" uid="uid://c38ikiqaivhh5" path="res://map.tscn" id="1_feb5d"]
|
||||
|
||||
[node name="Node2D" type="Node2D"]
|
||||
|
||||
[node name="Node2D" parent="." instance=ExtResource("1_e2o6t")]
|
||||
position = Vector2(1024, -7680)
|
||||
rotation = 1.57079
|
||||
|
||||
[node name="Node2D2" parent="." instance=ExtResource("1_e2o6t")]
|
||||
position = Vector2(1024, -17920)
|
||||
rotation = 1.57079
|
||||
|
||||
[node name="Node2D3" parent="." instance=ExtResource("1_e2o6t")]
|
||||
position = Vector2(1024, -28160)
|
||||
rotation = 1.57079
|
||||
|
||||
[node name="Curb1" type="Sprite2D" parent="."]
|
||||
position = Vector2(-1024, 1022.44)
|
||||
rotation = 1.57079
|
||||
scale = Vector2(1.99747, 0.147406)
|
||||
texture = ExtResource("4_fc0e3")
|
||||
[node name="test" parent="." instance=ExtResource("1_feb5d")]
|
||||
position = Vector2(-2048, -2560)
|
||||
|
||||
[node name="Car" parent="." instance=ExtResource("1_80nbo")]
|
||||
controlled_by_player = true
|
||||
script = ExtResource("4_7jktm")
|
||||
|
||||
[node name="Camera2D" type="Camera2D" parent="Car"]
|
||||
position = Vector2(0, -1)
|
||||
scale = Vector2(0.1, 0.1)
|
||||
zoom = Vector2(0.36, 0.36)
|
||||
zoom = Vector2(0.5, 0.5)
|
||||
|
||||
[node name="Car2" parent="." instance=ExtResource("1_80nbo")]
|
||||
position = Vector2(512, -1536)
|
||||
rotation = -1.0472
|
||||
position = Vector2(-512, -1536)
|
||||
rotation = -1.83259
|
||||
|
BIN
godot/levels/Level_0.scn
Normal file
5
godot/map.tscn
Normal file
@ -0,0 +1,5 @@
|
||||
[gd_scene load_steps=2 format=3 uid="uid://c38ikiqaivhh5"]
|
||||
|
||||
[ext_resource type="PackedScene" uid="uid://bydclumuwqch8" path="res://test.ldtk" id="1_c7s6e"]
|
||||
|
||||
[node name="test" instance=ExtResource("1_c7s6e")]
|
@ -15,6 +15,10 @@ run/main_scene="uid://7713s03g7nxw"
|
||||
config/features=PackedStringArray("4.4", "Forward Plus")
|
||||
config/icon="res://icon.svg"
|
||||
|
||||
[editor_plugins]
|
||||
|
||||
enabled=PackedStringArray("res://addons/ldtk-importer/plugin.cfg")
|
||||
|
||||
[input]
|
||||
|
||||
move_forward={
|
||||
|
@ -226,7 +226,7 @@
|
||||
"__cHei": 4,
|
||||
"identifier": "Sidewalk_128x128",
|
||||
"uid": 1,
|
||||
"relPath": "../assets/processed/tilesets/sidewalk_128x128.png",
|
||||
"relPath": "assets/processed/tilesets/sidewalk_128x128.png",
|
||||
"embedAtlas": null,
|
||||
"pxWid": 128,
|
||||
"pxHei": 128,
|
||||
@ -245,7 +245,7 @@
|
||||
"__cHei": 1,
|
||||
"identifier": "Residential",
|
||||
"uid": 6,
|
||||
"relPath": "../assets/raw/residential.png",
|
||||
"relPath": "assets/raw/residential.png",
|
||||
"embedAtlas": null,
|
||||
"pxWid": 1024,
|
||||
"pxHei": 1024,
|
||||
@ -292,7 +292,7 @@
|
||||
"__pxTotalOffsetX": 0,
|
||||
"__pxTotalOffsetY": 0,
|
||||
"__tilesetDefUid": 6,
|
||||
"__tilesetRelPath": "../assets/raw/residential.png",
|
||||
"__tilesetRelPath": "assets/raw/residential.png",
|
||||
"iid": "2baa2510-3740-11f0-80fd-75b2b5fa7dd7",
|
||||
"levelId": 0,
|
||||
"layerDefUid": 9,
|
||||
@ -317,7 +317,7 @@
|
||||
"__pxTotalOffsetX": 0,
|
||||
"__pxTotalOffsetY": 0,
|
||||
"__tilesetDefUid": 1,
|
||||
"__tilesetRelPath": "../assets/processed/tilesets/sidewalk_128x128.png",
|
||||
"__tilesetRelPath": "assets/processed/tilesets/sidewalk_128x128.png",
|
||||
"iid": "c0f40ef0-3740-11f0-80fd-29ce8cc19ef6",
|
||||
"levelId": 0,
|
||||
"layerDefUid": 2,
|
37
godot/test.ldtk.import
Normal file
@ -0,0 +1,37 @@
|
||||
[remap]
|
||||
|
||||
importer="ldtk.import"
|
||||
type="PackedScene"
|
||||
uid="uid://bydclumuwqch8"
|
||||
path="res://.godot/imported/test.ldtk-e10b9543a89b321d04f19d9114d464c5.scn"
|
||||
|
||||
[deps]
|
||||
|
||||
files=["res:///tilesets/tileset_1024px.res", "res:///tilesets/tileset_32px.res", "res:///levels/Level_0.scn", "res://.godot/imported/test.ldtk-e10b9543a89b321d04f19d9114d464c5.scn"]
|
||||
|
||||
source_file="res://test.ldtk"
|
||||
dest_files=["res://.godot/imported/test.ldtk-e10b9543a89b321d04f19d9114d464c5.scn", "res:///tilesets/tileset_1024px.res", "res:///tilesets/tileset_32px.res", "res:///levels/Level_0.scn", "res://.godot/imported/test.ldtk-e10b9543a89b321d04f19d9114d464c5.scn"]
|
||||
|
||||
[params]
|
||||
|
||||
World=""
|
||||
group_world_layers=false
|
||||
Level=""
|
||||
pack_levels=true
|
||||
Layer=""
|
||||
layers_always_visible=false
|
||||
Tileset=""
|
||||
tileset_custom_data=false
|
||||
integer_grid_tilesets=false
|
||||
atlas_texture_type=0
|
||||
Entity=""
|
||||
resolve_entityrefs=true
|
||||
use_entity_placeholders=false
|
||||
Post Import=""
|
||||
tileset_post_import=""
|
||||
entities_post_import=""
|
||||
level_post_import=""
|
||||
world_post_import=""
|
||||
Debug=""
|
||||
force_tileset_reimport=false
|
||||
verbose_output=false
|