Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/game/StarMicroDungeon.cpp
blob: aefdbb9fc5e5b02b34409805c94b0a5132b5041b (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
#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 {}, 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 '{}'", dungeonGenerator.definition()->name()), e);
  }

  return {};
}

}