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

summaryrefslogtreecommitdiff
path: root/source/game/StarTreasure.cpp
diff options
context:
space:
mode:
authorKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
committerKae <80987908+Novaenia@users.noreply.github.com>2023-06-20 14:33:09 +1000
commit6352e8e3196f78388b6c771073f9e03eaa612673 (patch)
treee23772f79a7fbc41bc9108951e9e136857484bf4 /source/game/StarTreasure.cpp
parent6741a057e5639280d85d0f88ba26f000baa58f61 (diff)
everything everywhere
all at once
Diffstat (limited to 'source/game/StarTreasure.cpp')
-rw-r--r--source/game/StarTreasure.cpp219
1 files changed, 219 insertions, 0 deletions
diff --git a/source/game/StarTreasure.cpp b/source/game/StarTreasure.cpp
new file mode 100644
index 0000000..5b7ce84
--- /dev/null
+++ b/source/game/StarTreasure.cpp
@@ -0,0 +1,219 @@
+#include "StarTreasure.hpp"
+#include "StarObjectDatabase.hpp"
+#include "StarRoot.hpp"
+#include "StarItemDatabase.hpp"
+#include "StarAssets.hpp"
+#include "StarItemBag.hpp"
+#include "StarWorld.hpp"
+#include "StarContainerObject.hpp"
+#include "StarJsonExtra.hpp"
+
+namespace Star {
+
+TreasureDatabase::TreasureDatabase() {
+ auto assets = Root::singleton().assets();
+
+ auto treasurePools = assets->scanExtension("treasurepools");
+ auto treasureChests = assets->scanExtension("treasurechests");
+
+ assets->queueJsons(treasurePools);
+ assets->queueJsons(treasureChests);
+
+ for (auto file : treasurePools) {
+ for (auto const& pair : assets->json(file).iterateObject()) {
+ if (m_treasurePools.contains(pair.first))
+ throw TreasureException(strf("Duplicate TreasurePool config '%s' from file '%s'", pair.first, file));
+
+ auto& treasurePool = m_treasurePools[pair.first];
+ for (auto const& entry : pair.second.iterateArray()) {
+ if (entry.size() != 2)
+ throw TreasureException("Wrong size for TreasurePool entry, list must be 2");
+
+ float startLevel = entry.getFloat(0);
+ auto config = entry.get(1);
+
+ ItemPool itemPool;
+
+ for (auto const& entry : config.getArray("fill", {}))
+ if (entry.contains("pool"))
+ itemPool.fill.append(entry.getString("pool"));
+ else if (entry.contains("item"))
+ itemPool.fill.append(ItemDescriptor(entry.get("item")));
+ else
+ throw TreasureException(strf("TreasurePool entry '%s' did not specify a valid 'item' or 'pool'", entry));
+
+ for (auto const& entry : config.getArray("pool", {})) {
+ if (!entry.contains("weight"))
+ throw TreasureException(strf("TreasurePool entry '%s' did not specify a weight", entry));
+
+ if (entry.contains("pool"))
+ itemPool.pool.add(entry.getFloat("weight"), entry.getString("pool"));
+ else if (entry.contains("item"))
+ itemPool.pool.add(entry.getFloat("weight"), ItemDescriptor(entry.get("item")));
+ else
+ throw TreasureException(strf("TreasurePool entry '%s' did not specify a valid 'item' or 'pool'", entry));
+ }
+
+ auto poolRounds = config.get("poolRounds", 1);
+ if (poolRounds.canConvert(Json::Type::Float)) {
+ itemPool.poolRounds = WeightedPool<int>(List<std::pair<double, int>>{{1.0, poolRounds.toFloat()}});
+ } else {
+ for (auto const& pair : poolRounds.iterateArray())
+ itemPool.poolRounds.add(pair.getDouble(0), pair.getInt(1));
+ }
+
+ itemPool.levelVariance = jsonToVec2F(config.get("levelVariance", JsonArray{0, 0}));
+ itemPool.allowDuplication = config.getBool("allowDuplication", true);
+
+ treasurePool.addPoint(startLevel, move(itemPool));
+ }
+ }
+ }
+
+ for (auto file : treasureChests) {
+ for (auto const& pair : assets->json(file).iterateObject()) {
+ if (m_treasureChestSets.contains(pair.first))
+ throw TreasureException(strf("Duplicate TreasureChestSet config '%s' from file '%s'", pair.first, file));
+
+ auto& treasureChestSet = m_treasureChestSets[pair.first];
+ for (auto const& entry : pair.second.iterateArray()) {
+ TreasureChest treasureChest;
+
+ treasureChest.containers = jsonToStringList(entry.get("containers"));
+ treasureChest.treasurePool = entry.getString("treasurePool");
+ treasureChest.minimumLevel = entry.getFloat("minimumLevel", 0);
+
+ if (!m_treasurePools.contains(treasureChest.treasurePool))
+ throw TreasureException(strf("No such TreasurePool '%s' for TreasureChestSet named '%s' in file '%s'", treasureChest.treasurePool, pair.first, file));
+
+ treasureChestSet.append(treasureChest);
+ }
+ }
+ }
+}
+
+StringList TreasureDatabase::treasurePools() const {
+ return m_treasurePools.keys();
+}
+
+bool TreasureDatabase::isTreasurePool(String const& treasurePool) const {
+ return m_treasurePools.contains(treasurePool);
+}
+
+StringList TreasureDatabase::treasureChestSets() const {
+ return m_treasureChestSets.keys();
+}
+
+bool TreasureDatabase::isTreasureChestSet(String const& treasurePool) const {
+ return m_treasureChestSets.contains(treasurePool);
+}
+
+TreasureDatabase::ItemPool::ItemPool() : allowDuplication() {}
+
+TreasureDatabase::TreasureChest::TreasureChest() : minimumLevel() {}
+
+List<ItemPtr> TreasureDatabase::createTreasure(String const& treasurePool, float level) const {
+ return createTreasure(treasurePool, level, Random::randu64());
+}
+
+List<ItemPtr> TreasureDatabase::createTreasure(String const& treasurePool, float level, uint64_t seed) const {
+ return createTreasure(treasurePool, level, seed, StringSet());
+}
+
+List<ItemPtr> TreasureDatabase::createTreasure(String const& treasurePool, float level, uint64_t seed, StringSet visitedPools) const {
+ if (!m_treasurePools.contains(treasurePool))
+ throw TreasureException(strf("Unknown treasure pool '%s'", treasurePool));
+
+ if (!visitedPools.add(treasurePool))
+ throw TreasureException(strf("Loop detected in treasure pool generation - set '%s' already contains '%s'", visitedPools, treasurePool));
+
+ auto itemDatabase = Root::singleton().itemDatabase();
+
+ List<ItemPtr> treasureItems;
+ HashSet<ItemDescriptor> previousDescriptors;
+ auto itemPool = m_treasurePools.get(treasurePool).get(level);
+
+ int mix = 0;
+ for (auto const& fillEntry : itemPool.fill) {
+ if (fillEntry.is<String>()) {
+ auto poolContents = createTreasure(fillEntry.get<String>(), level, seed + ++mix, visitedPools);
+ for (auto item : poolContents) {
+ if (itemPool.allowDuplication || previousDescriptors.add(item->descriptor().singular()))
+ treasureItems.append(item);
+ }
+ } else {
+ float itemLevel = level + itemPool.levelVariance[0] + staticRandomFloat(seed, ++mix, "FillLevelVariance") * (itemPool.levelVariance[1] - itemPool.levelVariance[0]);
+ auto fillItem = itemDatabase->item(fillEntry.get<ItemDescriptor>(), itemLevel, seed + ++mix);
+ if (itemPool.allowDuplication || previousDescriptors.add(fillItem->descriptor().singular()))
+ treasureItems.append(fillItem);
+ }
+ }
+
+ if (!itemPool.pool.empty()) {
+ int poolRounds = itemPool.poolRounds.select(staticRandomU64(seed, "TreasurePoolRounds"));
+
+ for (int i = 0; i < poolRounds; ++i) {
+ auto poolEntry = itemPool.pool.select(staticRandomU64(seed, i, "TreasureItem"));
+
+ if (poolEntry.is<String>()) {
+ auto poolContents = createTreasure(poolEntry.get<String>(), level, staticRandomU64(seed, i, "TreasureSeedRecursion"), visitedPools);
+ for (auto item : poolContents) {
+ if (itemPool.allowDuplication || previousDescriptors.add(item->descriptor().singular()))
+ treasureItems.append(item);
+ }
+ } else {
+ float itemLevel = level + itemPool.levelVariance[0] + staticRandomFloat(staticRandomU64(seed, i, "TreasureLevelSeedMixer"), "PoolLevelVariance") * (itemPool.levelVariance[1] - itemPool.levelVariance[0]);
+ auto roundItem = poolEntry.get<ItemDescriptor>();
+ if (itemPool.allowDuplication || previousDescriptors.add(roundItem.singular()))
+ treasureItems.append(itemDatabase->item(roundItem, itemLevel, seed + ++mix));
+ }
+ }
+ }
+
+ return treasureItems;
+}
+
+List<ItemPtr> TreasureDatabase::fillWithTreasure(
+ ItemBagPtr const& itemBag, String const& treasurePool, float level) const {
+ return fillWithTreasure(itemBag, treasurePool, level, Random::randu64());
+}
+
+List<ItemPtr> TreasureDatabase::fillWithTreasure(
+ ItemBagPtr const& itemBag, String const& treasurePool, float level, uint64_t seed) const {
+ List<ItemPtr> overflowItems;
+ for (auto const& treasureItem : createTreasure(treasurePool, level, seed)) {
+ if (auto overflow = itemBag->addItems(treasureItem))
+ overflowItems.append(overflow);
+ }
+
+ return overflowItems;
+}
+
+ContainerObjectPtr TreasureDatabase::createTreasureChest(World* world, String const& treasureChestSet, Vec2I const& position, Direction direction) const {
+ return createTreasureChest(world, treasureChestSet, position, direction, Random::randu64());
+}
+
+ContainerObjectPtr TreasureDatabase::createTreasureChest(World* world, String const& treasureChestSet, Vec2I const& position, Direction direction, uint64_t seed) const {
+ auto objectDatabase = Root::singleton().objectDatabase();
+
+ if (!m_treasureChestSets.contains(treasureChestSet))
+ throw StarException(strf("Unknown treasure chest set '%s'", treasureChestSet));
+
+ auto level = world->threatLevel();
+ auto boxSet = m_treasureChestSets.get(treasureChestSet);
+ eraseWhere(boxSet, [level](TreasureChest const& treasureChest) { return level < treasureChest.minimumLevel; });
+
+ if (boxSet.empty())
+ return {};
+
+ auto const& treasureChest = staticRandomFrom(boxSet, seed, "TreasureChest");
+ auto const& containerName = staticRandomFrom(treasureChest.containers, seed, "ContainerName");
+ ContainerObjectPtr containerObject;
+ auto parameters = JsonObject{{"treasurePools", JsonArray{treasureChest.treasurePool}}, {"treasureSeed", seed}};
+ if (auto object = objectDatabase->createForPlacement(world, containerName, position, direction, parameters))
+ containerObject = convert<ContainerObject>(object);
+
+ return containerObject;
+}
+
+}