osb/source/game/StarMicroDungeon.cpp
2023-06-20 14:33:09 +10:00

81 lines
2.5 KiB
C++

#include "StarMicroDungeon.hpp"
#include "StarRoot.hpp"
#include "StarInterpolation.hpp"
#include "StarLogging.hpp"
#include "StarDungeonGenerator.hpp"
namespace Star {
// Placed on server so it can keep a cacheing system which allows for a quick
// scan if a piece fits.
MicroDungeonFactory::MicroDungeonFactory() {
m_generating = false;
m_placementshifts.push_back(0);
for (int i = 1; i < 4; ++i)
m_placementshifts.push_back(i);
for (int i = 1; i < 4; ++i)
m_placementshifts.push_back(-i);
}
Maybe<pair<List<RectI>, Set<Vec2I>>> MicroDungeonFactory::generate(RectI const& bounds,
String const& dungeonName,
Vec2I const& position,
uint64_t seed,
float threatLevel,
DungeonGeneratorWorldFacadePtr facade,
bool forcePlacement) {
Dungeon::DungeonGeneratorWriter writer(facade, {}, {});
if (m_generating)
throw DungeonException("Not reentrant.");
m_generating = true;
auto generatingGuard = finally([this]() { m_generating = false; });
DungeonGenerator dungeonGenerator(dungeonName, seed, threatLevel, BiomeMicroDungeonId);
try {
// don't bother scanning around because its used in a bruteforce manner for now.
// try to stay a bit stable generation wise, maybe trash the cache after a sector is done ?
auto anchorPart = dungeonGenerator.pickAnchor();
if (!anchorPart) {
Logger::debug("No valid anchor piece found for microdungeon at %s, skipping", position);
return {};
}
if (forcePlacement) {
return dungeonGenerator.buildDungeon(anchorPart, position - anchorPart->anchorPoint(), &writer, forcePlacement);
} else {
for (int dy : m_placementshifts) {
auto pos = position - anchorPart->anchorPoint() + Vec2I{0, dy};
if (!bounds.contains(pos) || !bounds.contains(pos + Vec2I{anchorPart->size()} - Vec2I{1, 1}))
continue;
bool collision = false;
anchorPart->forEachTile([&](Vec2I tilePos, Dungeon::Tile const& tile) -> bool {
if (tile.usesPlaces()) {
if (facade->getDungeonIdAt(pos + tilePos) != NoDungeonId) {
collision = true;
return true;
}
}
return false;
});
if (!collision && anchorPart->canPlace(pos, &writer)) {
return dungeonGenerator.buildDungeon(anchorPart, pos, &writer, forcePlacement);
}
}
}
} catch (std::exception const& e) {
throw DungeonException(strf("Error generating microdungeon named '%s'", dungeonGenerator.definition()->name()), e);
}
return {};
}
}