diff --git a/src/saland/GameRegion.cpp b/src/saland/GameRegion.cpp index 1f6f5c5..17b67e1 100644 --- a/src/saland/GameRegion.cpp +++ b/src/saland/GameRegion.cpp @@ -96,18 +96,25 @@ void GameRegion::SpawnPrefab(const Prefab& prefab, int destX, int destY) { } } } + if (PrefabFootprintOverlapsPlaced(world.tm, destX, destY, prefab.width, prefab.height)) { + std::cout << "Do not spawn prefab " << prefab.name << "(" << destX << "," << destY << "), Overlaps an existing prefab\n"; + return; + } ApplyPrefab(world.tm, destX, destY, prefab); this->world.init_physics(this->physicsBox); } bool GameRegion::RemovePrefabAtTile(int tileX, int tileY) { + bool removedAny = false; PlacedPrefabInfo info; - if (!FindPlacedPrefabAtTile(world.tm, tileX, tileY, info)) { - return false; + while (FindPlacedPrefabAtTile(world.tm, tileX, tileY, info)) { + RemovePlacedPrefab(world.tm, info); + removedAny = true; } - RemovePlacedPrefab(world.tm, info); - this->world.init_physics(this->physicsBox); - return true; + if (removedAny) { + this->world.init_physics(this->physicsBox); + } + return removedAny; } void GameRegion::SpawnItem(const ItemDef& def, float destX, float destY) { diff --git a/src/saland/Prefabs.cpp b/src/saland/Prefabs.cpp index da89690..c1bb6e7 100644 --- a/src/saland/Prefabs.cpp +++ b/src/saland/Prefabs.cpp @@ -41,6 +41,13 @@ static int GetLayerNumber(const sago::tiled::TileMap& tm, const char* name) { return -1; } +static void MarkerToTileRect(const sago::tiled::TileObject& o, int& destX, int& destY, int& width, int& height) { + destX = (o.x - 2) / 32; + destY = (o.y - 2) / 32; + width = (o.width + 4) / 32; + height = (o.height + 4) / 32; +} + static int32_t translate_tile(const sago::tiled::TileMap& dest, const sago::tiled::TileMap& source, int32_t source_tile) { if (source_tile == 0) { return 0; @@ -137,10 +144,8 @@ bool FindPlacedPrefabAtTile(const sago::tiled::TileMap& tm, int tileX, int tileY continue; } for (const auto& o : group.objects) { - int destX = (o.x - 2) / 32; - int destY = (o.y - 2) / 32; - int width = (o.width + 4) / 32; - int height = (o.height + 4) / 32; + int destX, destY, width, height; + MarkerToTileRect(o, destX, destY, width, height); if (tileX >= destX && tileX < destX+width && tileY >= destY && tileY < destY+height) { out.name = o.name; out.destX = destX; @@ -154,6 +159,23 @@ bool FindPlacedPrefabAtTile(const sago::tiled::TileMap& tm, int tileX, int tileY return false; } +bool PrefabFootprintOverlapsPlaced(const sago::tiled::TileMap& tm, int destX, int destY, int width, int height) { + for (const auto& group : tm.object_groups) { + if (group.name != "prefab_marking") { + continue; + } + for (const auto& o : group.objects) { + int mDestX, mDestY, mWidth, mHeight; + MarkerToTileRect(o, mDestX, mDestY, mWidth, mHeight); + bool disjoint = destX+width <= mDestX || mDestX+mWidth <= destX || destY+height <= mDestY || mDestY+mHeight <= destY; + if (!disjoint) { + return true; + } + } + } + return false; +} + void RemovePlacedPrefab(sago::tiled::TileMap& dest, const PlacedPrefabInfo& info) { static const char* destLayers[] = {"prefab_ground_1", "blocking", "prefab_blocking_2", "prefab_overlay_1"}; for (const char* destLayer : destLayers) { @@ -173,7 +195,9 @@ void RemovePlacedPrefab(sago::tiled::TileMap& dest, const PlacedPrefabInfo& info } auto& objects = group.objects; objects.erase(std::remove_if(objects.begin(), objects.end(), [&info](const sago::tiled::TileObject& o) { - return o.name == info.name && (o.x - 2) / 32 == info.destX && (o.y - 2) / 32 == info.destY; + int destX, destY, width, height; + MarkerToTileRect(o, destX, destY, width, height); + return o.name == info.name && destX == info.destX && destY == info.destY; }), objects.end()); break; } diff --git a/src/saland/Prefabs.hpp b/src/saland/Prefabs.hpp index 6fd4b3b..0009fa5 100644 --- a/src/saland/Prefabs.hpp +++ b/src/saland/Prefabs.hpp @@ -68,6 +68,12 @@ struct PlacedPrefabInfo { bool FindPlacedPrefabAtTile(const sago::tiled::TileMap& tm, int tileX, int tileY, PlacedPrefabInfo& out); /** + * Returns true if a prefab footprint of the given size at (destX, destY) would overlap + * any already-placed prefab (tracked via "prefab_marking" objects). + */ +bool PrefabFootprintOverlapsPlaced(const sago::tiled::TileMap& tm, int destX, int destY, int width, int height); + +/** * Clears the tiles stamped by a previously placed prefab and removes its marking object. * Use FindPlacedPrefabAtTile to locate the prefab to remove. */