25#include "../include/TiledLevelLoader.h"
26#include "../include/TiledToOlympe.h"
27#include "../include/IsometricProjection.h"
28#include "../include/ParallaxLayerManager.h"
29#include "../../OlympeTilemapEditor/include/LevelManager.h"
30#include "../../system/system_utils.h"
31#include "../../vector.h"
32#include "../../prefabfactory.h"
91 for (
const auto& layer :
tiledMap.layers)
98 for (
const auto&
chunk : layer->chunks)
143 SYSTEM_LOG <<
"\n+===========================================================+\n";
144 SYSTEM_LOG <<
"| TILED -> OLYMPE CONVERSION - COMPLETE PIPELINE |\n";
145 SYSTEM_LOG <<
"+===========================================================+\n\n";
152 SYSTEM_LOG <<
" /!\\ Map is INFINITE - calculating actual bounds...\n";
221 SYSTEM_LOG <<
"[Phase 1/6] Extracting Map Configuration & Metadata...\n";
228 SYSTEM_LOG <<
"[Phase 2/6] Processing Visual Layers...\n";
236 SYSTEM_LOG <<
"[Phase 3/6] Extracting Spatial Structures...\n";
244 SYSTEM_LOG <<
"[Phase 4/6] Converting Game Objects...\n";
255 SYSTEM_LOG <<
"[Post-Conversion] Normalizing Entity Types...\n";
259 for (
auto& entity :
outLevel.entities)
261 if (!entity)
continue;
273 for (
auto& entity :
outLevel.categorizedObjects.dynamicObjects)
275 if (entity) entity->type =
factory.NormalizeType(entity->type);
277 for (
auto& entity :
outLevel.categorizedObjects.staticObjects)
279 if (entity) entity->type =
factory.NormalizeType(entity->type);
281 for (
auto& entity :
outLevel.categorizedObjects.patrolPaths)
283 if (entity) entity->type =
factory.NormalizeType(entity->type);
285 for (
auto& entity :
outLevel.categorizedObjects.soundObjects)
287 if (entity) entity->type =
factory.NormalizeType(entity->type);
295 SYSTEM_LOG <<
"[Phase 5/6] Extracting Object Relationships...\n";
303 SYSTEM_LOG <<
"[Phase 6/6] Building Resource Catalog...\n";
306 <<
" | Images: " <<
outLevel.resources.imagePaths.size()
307 <<
" | Audio: " <<
outLevel.resources.audioPaths.size() <<
"\n";
312 SYSTEM_LOG <<
"\n+===========================================================+\n";
314 SYSTEM_LOG <<
"+===========================================================+\n";
316 <<
" " <<
outLevel.mapConfig.mapWidth <<
"x" <<
outLevel.mapConfig.mapHeight <<
"\n";
320 SYSTEM_LOG <<
"+===========================================================+\n\n";
327 SYSTEM_LOG <<
"TiledToOlympe: Converting tile layer '" << layer.
name <<
"'" << std::endl;
335 if (
index <
static_cast<int>(layer.
data.size())) {
339 level.collisionMap[y][x] = 0xFF;
354 SYSTEM_LOG <<
"TiledToOlympe: Converting object layer '" << layer.
name
355 <<
"' with " << layer.
objects.size() <<
" objects" << std::endl;
360 <<
", offsety=" << layer.
offsety << std::endl;
370 SYSTEM_LOG <<
"TiledToOlympe: Converting image layer '" << layer.
name <<
"'" << std::endl;
376 if (!layer.
image.empty()) {
399 SYSTEM_LOG <<
"TiledToOlympe: Converting group layer '" << layer.
name <<
"'" << std::endl;
470 if (y >= 0 && x >= 0) {
471 level.collisionMap[y][x] = 0xFF;
481 auto entity = std::make_unique<Olympe::Editor::EntityInstance>();
484 entity->id =
"sector_" + std::to_string(
obj.id);
485 entity->name =
obj.name.empty() ? (
"Sector " + std::to_string(
obj.id)) :
obj.name;
486 entity->prefabPath =
"Blueprints/Sector.json";
496 for (
const auto&
pt :
obj.polygon) {
500 polygon.push_back(
point);
503 entity->overrides[
"Sector"] = nlohmann::json::object();
504 entity->overrides[
"Sector"][
"polygon"] = polygon;
505 entity->overrides[
"Sector"][
"type"] =
obj.type;
510 level.entities.push_back(std::move(entity));
517 auto entity = std::make_unique<Olympe::Editor::EntityInstance>();
519 entity->id =
"collision_poly_" + std::to_string(
obj.id);
520 entity->name =
obj.name.empty() ? (
"CollisionPoly " + std::to_string(
obj.id)) :
obj.name;
521 entity->type =
"CollisionPolygon";
522 entity->prefabPath =
"Blueprints/CollisionPolygon.json";
526 entity->rotation =
obj.rotation;
535 for (
const auto&
pt : points)
540 polygon.push_back(
point);
543 entity->overrides[
"CollisionPolygon_data"] = nlohmann::json::object();
544 entity->overrides[
"CollisionPolygon_data"][
"points"] = polygon;
548 entity->overrides[
"width"] =
obj.width;
549 entity->overrides[
"height"] =
obj.height;
554 level.entities.push_back(std::move(entity));
561 auto entity = std::make_unique<Olympe::Editor::EntityInstance>();
563 entity->id =
"patrol_" + std::to_string(
obj.id);
564 entity->name =
obj.name.empty() ? (
"Patrol " + std::to_string(
obj.id)) :
obj.name;
565 entity->prefabPath =
"Blueprints/PatrolPath.json";
575 for (
const auto&
pt :
obj.polyline) {
579 path.push_back(
point);
585 entity->overrides[
"AIBlackboard_data"] = nlohmann::json::object();
586 entity->overrides[
"AIBlackboard_data"][
"patrolPath"] = path;
587 entity->overrides[
"patrolPath"] = path;
592 level.entities.push_back(std::move(entity));
633 if (
obj.rotation != 0.0f) {
652 const std::map<std::string, TiledProperty>& properties,
655 for (
const auto&
pair : properties) {
696 <<
"' has multiple dots (nested structures not supported). "
697 <<
"Treating as flat property." << std::endl;
726 if (objectType.empty()) {
737 return "Blueprints/" + objectType +
".json";
741 const std::vector<std::string>&
patterns)
806 const float halfWidth = tileWidth * 0.5f;
810 const float tileX = (tileHeight != 0.0f) ? (x / tileHeight) : 0.0f;
811 const float tileY = (tileHeight != 0.0f) ? (y / tileHeight) : 0.0f;
814 float worldX = (tileX - tileY) *
halfWidth;
829 worldX +=
static_cast<float>(tileset->
tileoffsetX);
830 worldY +=
static_cast<float>(tileset->
tileoffsetY);
835 SYSTEM_LOG <<
"[TransformObjectPosition] ISO: TMJ(" << x <<
", " << y <<
")"
836 <<
" -> tile(" << tileX <<
", " << tileY <<
")"
837 <<
" -> world(" << worldX <<
", " << worldY <<
")\n";
839 return Vector(worldX, worldY, 0.0f);
846 return Vector(posX, posY, 0.0f);
850 int width,
int height)
852 level.collisionMap.resize(height);
853 for (
int y = 0; y < height; ++y) {
854 level.collisionMap[y].resize(width, 0);
859 std::vector<std::vector<int>>& tileMap,
860 int mapWidth,
int mapHeight)
862 if (layer.
data.empty()) {
867 for (
int y = 0; y < layer.
height && y < mapHeight; ++y) {
868 for (
int x = 0; x < layer.
width && x < mapWidth; ++x) {
869 if (
index <
static_cast<int>(layer.
data.size())) {
875 tileMap[y][x] =
static_cast<int>(
tileId);
902 default:
outLevel.mapConfig.orientation =
"unknown";
break;
924 if (!
tiledMap.backgroundcolor.empty()) {
929 <<
" " <<
outLevel.mapConfig.mapWidth <<
"x" <<
outLevel.mapConfig.mapHeight
930 <<
" (tiles: " <<
outLevel.mapConfig.tileWidth <<
"x" <<
outLevel.mapConfig.tileHeight <<
")\n";
941 outLevel.metadata.customData[
"orientation"] =
"orthogonal";
944 outLevel.metadata.customData[
"orientation"] =
"isometric";
947 outLevel.metadata.customData[
"orientation"] =
"staggered";
950 outLevel.metadata.customData[
"orientation"] =
"hexagonal";
953 outLevel.metadata.customData[
"orientation"] =
"unknown";
987 for (
const auto& layer :
tiledMap.layers) {
988 if (!layer->visible)
continue;
990 switch (layer->type) {
996 visual.isParallax = (layer->parallaxx != 1.0f || layer->parallaxy != 1.0f);
998 visual.scrollFactorX = layer->parallaxx;
999 visual.scrollFactorY = layer->parallaxy;
1000 visual.offsetX = layer->offsetx;
1001 visual.offsetY = layer->offsety;
1002 visual.repeatX = layer->repeatx;
1003 visual.repeatY = layer->repeaty;
1004 visual.opacity = layer->opacity;
1005 visual.tintColor = layer->tintcolor;
1006 visual.visible = layer->visible;
1015 <<
visual.scrollFactorX <<
", z: " <<
visual.zOrder <<
")\n";
1029 tileDef.opacity = layer->opacity;
1030 tileDef.visible = layer->visible;
1031 tileDef.isInfinite = !layer->chunks.empty();
1034 if (layer->startx != 0 || layer->starty != 0) {
1035 SYSTEM_LOG <<
" -> [DEBUG] Tile Layer '" << layer->name
1036 <<
"' has startx=" << layer->startx
1037 <<
", starty=" << layer->starty <<
"\n";
1042 for (
const auto&
chunk : layer->chunks) {
1053 for (
int y = 0; y <
chunk.height; ++y) {
1056 for (
int x = 0; x <
chunk.width; ++x) {
1057 if (
index <
static_cast<int>(
chunk.data.size())) {
1070 <<
tileDef.chunks.size() <<
" chunks, z: " <<
tileDef.zOrder <<
")\n";
1075 tileDef.tiles.resize(layer->height);
1076 tileDef.tileFlipFlags.resize(layer->height);
1078 for (
int y = 0; y < layer->height; ++y) {
1079 tileDef.tiles[y].resize(layer->width, 0);
1080 tileDef.tileFlipFlags[y].resize(layer->width, 0);
1081 for (
int x = 0; x < layer->width; ++x) {
1082 if (
index <
static_cast<int>(layer->data.size())) {
1092 << layer->width <<
"x" << layer->height <<
" tiles, z: " <<
tileDef.zOrder <<
")\n";
1175 for (
int y = 0; y <
chunk.height; ++y)
1177 for (
int x = 0; x <
chunk.width; ++x)
1182 if (y <
chunk.tiles.size() && x <
chunk.tiles[y].size())
1184 gid =
chunk.tiles[y][x];
1187 if (y <
chunk.tileFlipFlags.size() && x <
chunk.tileFlipFlags[y].size())
1198 dataJson.push_back((
int)
fullGID);
1211 int width =
tileLayer.tiles.empty() ? 0 :
static_cast<int>(
tileLayer.tiles[0].size());
1212 int height =
static_cast<int>(
tileLayer.tiles.size());
1218 for (
int y = 0; y < height; ++y)
1220 for (
int x = 0; x < width; ++x)
1227 dataJson.push_back(
static_cast<int>(gid));
1239 <<
" tile layers in metadata\n";
1253 for (
const auto& layer :
tiledMap.layers) {
1254 if (!layer->visible)
continue;
1261 for (
int y = 0; y < layer->height && y <
mapHeight_; ++y) {
1262 for (
int x = 0; x < layer->width && x <
mapWidth_; ++x) {
1263 if (
index <
static_cast<int>(layer->data.size())) {
1266 outLevel.collisionMap[y][x] = 0xFF;
1279 for (
const auto&
obj : layer->objects) {
1287 for (
const auto&
pt :
obj.polygon) {
1297 for (
const auto&
prop :
obj.properties) {
1319 <<
shape.size.x <<
"x" <<
shape.size.y <<
")\n";
1332 "item",
"collectible",
"key",
"treasure",
"waypoint",
"way",
"trigger",
"portal",
"door",
"exit",
1333 "pickup",
"interactable",
"checkpoint",
"teleporter",
"switch",
"spawn"
1337 "player",
"npc",
"guard",
"enemy",
"zombie"
1341 "ambient",
"sound",
"music"
1347 for (
const auto& layer :
tiledMap.layers) {
1350 if (!layer->visible) {
1355 SYSTEM_LOG <<
"[CategorizeGameObjects] Processing object layer '"
1356 << layer->name <<
"' (zOrder: " <<
globalZOrder <<
")\n";
1358 for (
const auto&
obj : layer->objects) {
1371 auto entityCopy = std::make_unique<Olympe::Editor::EntityInstance>();
1396 if (
typeLower.find(
"sector") != std::string::npos ||
1397 typeLower.find(
"zone") != std::string::npos) {
1414 auto entityCopy = std::make_unique<Olympe::Editor::EntityInstance>();
1427 SYSTEM_LOG <<
" -> Patrol Path: '" <<
obj.name <<
"' (" <<
obj.polyline.size() <<
" points)\n";
1432 SYSTEM_LOG <<
" -> Sound Object: '" <<
obj.name <<
"' (type: " <<
obj.type <<
")\n";
1475 std::map<int, std::string>
idToName;
1476 for (
const auto& layer :
tiledMap.layers) {
1478 for (
const auto&
obj : layer->objects) {
1483 SYSTEM_LOG <<
"[DEBUG] ExtractObjectRelationships - Processing objects...\n";
1486 for (
const auto& layer :
tiledMap.layers) {
1489 for (
const auto&
obj : layer->objects) {
1495 SYSTEM_LOG <<
" [DEBUG] Object '" <<
obj.name <<
"' (type: " <<
obj.type <<
") properties:\n";
1496 for (
const auto&
prop :
obj.properties) {
1509 SYSTEM_LOG << (
prop.second.boolValue ?
"true" :
"false") <<
"\n";
1522 link.linkType =
"patrol_path";
1528 <<
link.targetObjectName <<
"' (patrol_path)\n";
1539 link.linkType =
"trigger_target";
1545 <<
link.targetObjectName <<
"' (trigger_target)\n";
1551 SYSTEM_LOG <<
" /!\\ No object relationships found. Check:\n";
1552 SYSTEM_LOG <<
" - Guards should have 'patrol way' property (Object type)\n";
1553 SYSTEM_LOG <<
" - Property must reference a 'way' object by ID\n";
1561 for (
const auto& tileset :
tiledMap.tilesets) {
1562 if (!tileset.source.empty()) {
1563 outLevel.resources.tilesetPaths.push_back(tileset.source);
1565 else if (!tileset.image.empty()) {
1566 outLevel.resources.imagePaths.push_back(tileset.image);
1571 for (
const auto& layer :
tiledMap.layers) {
1579 for (
const auto& layer :
tiledMap.layers) {
1581 for (
const auto&
obj : layer->objects) {
1592 std::sort(
vec.begin(),
vec.end());
1593 vec.erase(std::unique(
vec.begin(),
vec.end()),
vec.end());
1605 for (
const auto& tileset :
tiledMap.tilesets)
1620 if (!tileset.image.empty())
1626 else if (!tileset.tiles.empty())
1631 for (
const auto&
tile : tileset.tiles)
1657 if (imagePath.empty())
return "";
1667 switch (
prop.type) {
1671 return prop.stringValue;
1673 return prop.intValue;
1675 return prop.floatValue;
1677 return prop.boolValue;
1679 return prop.intValue;
1697 visual.zOrder = zOrder++;
1743 for (
int y = 0; y <
chunk.height; ++y) {
1746 for (
int x = 0; x <
chunk.width; ++x) {
1747 if (
index <
static_cast<int>(
chunk.data.size())) {
1765 for (
int y = 0; y <
childLayer->height; ++y) {
1768 for (
int x = 0; x <
childLayer->width; ++x) {
1797 if (!
file.is_open())
1810 catch (
const std::exception&
e)
1812 lastError_ = std::string(
"JSON parse error: ") +
e.what();
1818 if (!
j.contains(
"schema_version"))
1820 lastError_ =
"Missing 'schema_version' in prefab mapping file";
1834 if (!
j.contains(
"mapping") || !
j[
"mapping"].is_object())
1836 lastError_ =
"Missing or invalid 'mapping' object in prefab mapping file";
1841 const auto&
mapping =
j[
"mapping"];
1846 std::string objectType =
it.key();
1847 std::string prefabPath =
it.value().get<std::string>();
1851 SYSTEM_LOG <<
"TiledToOlympe::LoadPrefabMapping - Mapped '" << objectType
1852 <<
"' -> '" << prefabPath <<
"'" << std::endl;
1855 SYSTEM_LOG <<
"TiledToOlympe::LoadPrefabMapping - Loaded "
1866 if (
colorStr ==
"#")
return 0xFFFFFFFF;
1869 if (
hex.length() > 0 &&
hex[0] ==
'#') {
1874 if (
hex.empty())
return 0xFFFFFFFF;
1878 color =
static_cast<uint32_t>(std::stoul(
hex,
nullptr, 16));
1881 if (
hex.length() == 6) {
1882 color = 0xFF000000 | color;
1895 auto entityDescriptor = std::make_unique<Olympe::Editor::EntityInstance>();
1911 for (
const auto&
pt :
obj.polygon) {
1915 polygon.push_back(
point);
1931 auto entityDescriptor = std::make_unique<Olympe::Editor::EntityInstance>();
1947 for (
const auto&
pt :
obj.polyline) {
1951 path.push_back(
point);
1954 entityDescriptor->overrides[
"AIBlackboard_data"] = nlohmann::json::object();
1966 auto entityDescriptor = std::make_unique<Olympe::Editor::EntityInstance>();
1984 for (
const auto&
pt : points) {
1988 polygon.push_back(
point);
1991 entityDescriptor->overrides[
"CollisionPolygon"] = nlohmann::json::object();
ComponentTypeID GetComponentTypeID_Static()
void ExtractFlipFlags(uint32_t gid, bool &flipH, bool &flipV, bool &flipD)
const ParallaxLayer * GetLayer(size_t index) const
void AddLayer(const ParallaxLayer &layer)
size_t GetLayerCount() const
static ParallaxLayerManager & Get()
Factory class for creating entities from prefab blueprints.
static PrefabFactory & Get()
Get singleton instance.
bool IsFlippedDiagonally(uint32_t gid)
bool IsFlippedVertically(uint32_t gid)
bool IsFlippedHorizontally(uint32_t gid)
uint32_t GetTileId(uint32_t gid)
std::string sourceObjectName
std::vector< std::string > collisionLayerPatterns
std::string defaultPrefab
std::vector< std::string > sectorLayerPatterns
std::map< std::string, std::string > typeToPrefabMap
std::string resourceBasePath
std::string mapOrientation
std::vector< TiledObject > objects
std::vector< uint32_t > data
std::vector< std::shared_ptr< TiledLayer > > layers