commit c5ef19ab
Fix issue with overlapping prefabs
Changed files
| M | src/saland/GameRegion.cpp before |
| M | src/saland/Prefabs.cpp before |
| M | src/saland/Prefabs.hpp before |
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.
*/