feat: extend LootTables

This commit is contained in:
dualfroz committed 2026-09-08 17:45:55 +02:00
1 parent fdebb884e6
commit 20aecc6757
1 file changed
+145
+145
View File
@@ -630,3 +630,148 @@ LootTableId LootTableId::ofChest(ChestFill::Structure structure) {
return LootTableId{Domain::Chest, static_cast<uint16_t>(structure)};
}
LootTableId LootTableId::ofFishing(FishingLoot::Pool pool) {
return LootTableId{Domain::Fishing, static_cast<uint16_t>(pool)};
}
bool LootTableId::valid() const {
switch (domain) {
case Domain::Block: return index < static_cast<uint16_t>(Block::Count);
case Domain::Mob: return index < static_cast<uint16_t>(MobDropRule::Species::Count);
case Domain::Chest: return index < static_cast<uint16_t>(ChestFill::Structure::Count);
case Domain::Fishing: return index < static_cast<uint16_t>(FishingLoot::Pool::Count);
case Domain::Count: return false;
}
return false;
}
LootRegistry::LootRegistry()
: blocks(buildBlockRules()),
mobs(buildMobRules()),
chests(buildChestFills()),
fishingTables(buildFishingTables()) {
for (const BlockDropRule& rule : blocks) blockTables.push_back(blockRuleTable(rule));
for (const MobDropRule& rule : mobs) mobTables.push_back(mobRuleTable(rule));
for (const ChestFill& fill : chests) {
LootTable table;
table.pools.push_back(fill.pool);
chestTables.push_back(table);
}
}
const BlockDropRule& LootRegistry::blockRule(Block block) const {
return blocks[static_cast<std::size_t>(block)];
}
const MobDropRule& LootRegistry::mobRule(MobDropRule::Species species) const {
return mobs[static_cast<std::size_t>(species)];
}
const ChestFill& LootRegistry::chestFill(ChestFill::Structure structure) const {
return chests[static_cast<std::size_t>(structure)];
}
bool LootRegistry::coversEveryBlock() const {
if (blocks.size() != static_cast<std::size_t>(Block::Count)) return false;
for (std::size_t i = 0; i < blocks.size(); ++i) {
if (blocks[i].block != static_cast<Block>(i)) return false;
}
return true;
}
bool LootRegistry::coversEveryMob() const {
if (mobs.size() != static_cast<std::size_t>(MobDropRule::Species::Count)) return false;
for (std::size_t i = 0; i < mobs.size(); ++i) {
if (mobs[i].species != static_cast<MobDropRule::Species>(i)) return false;
}
return true;
}
bool LootRegistry::coversEveryStructure() const {
if (chests.size() != static_cast<std::size_t>(ChestFill::Structure::Count)) return false;
for (std::size_t i = 0; i < chests.size(); ++i) {
if (chests[i].structure != static_cast<ChestFill::Structure>(i)) return false;
}
return true;
}
const LootTable& LootRegistry::tableFor(LootTableId id) const {
if (!id.valid()) return emptyLootTable();
std::size_t index = static_cast<std::size_t>(id.index);
switch (id.domain) {
case LootTableId::Domain::Block: return blockTables[index];
case LootTableId::Domain::Mob: return mobTables[index];
case LootTableId::Domain::Chest: return chestTables[index];
case LootTableId::Domain::Fishing: return fishingTables[index];
case LootTableId::Domain::Count: break;
}
return emptyLootTable();
}
LootRoll LootRegistry::rollBlock(Block block, const LootContext& context) const {
if (block >= Block::Count) return {};
const BlockDropRule& rule = blockRule(block);
if (!toolAllows(rule, context.tool)) return {};
if (rule.shearHarvest && itemInfo(context.tool).toolType == shearToolType) {
LootRoll sheared;
ItemId harvested = rule.silkTouchDrop();
if (harvested != ItemId::None) sheared.items.push_back({harvested, 1, 0});
return sheared;
}
return rollLootTable(blockTables[static_cast<std::size_t>(block)], context);
}
LootRoll LootRegistry::rollMob(MobDropRule::Species species, const LootContext& context) const {
if (species >= MobDropRule::Species::Count) return {};
return rollLootTable(mobTables[static_cast<std::size_t>(species)], context);
}
LootRoll LootRegistry::rollChest(ChestFill::Structure structure, const LootContext& context) const {
if (structure >= ChestFill::Structure::Count) return {};
return rollLootTable(chestTables[static_cast<std::size_t>(structure)], context);
}
int LootRegistry::chestCountFor(ChestFill::Structure structure, uint32_t seed) const {
if (structure >= ChestFill::Structure::Count) return 0;
const ChestFill& fill = chestFill(structure);
uint32_t rng = seed != 0 ? seed : defaultLootSeed;
return rollBetween(rng, fill.chestsMin, fill.chestsMax);
}
FishingLoot LootRegistry::rollFishing(const LootContext& context) const {
uint32_t rng = context.seed != 0 ? context.seed : defaultLootSeed;
int luck = context.fortune();
int lure = context.toolEnchants.levelOf(EnchantKind::Efficiency);
FishingLoot outcome;
outcome.fishWeight = fishingFishWeight;
outcome.treasureWeight = fishingTreasureWeight + fishingLuckWeightShift * luck;
outcome.junkWeight = std::max(0, fishingJunkWeight - fishingLuckWeightShift * luck);
int pick = rollBelow(rng, outcome.fishWeight + outcome.treasureWeight + outcome.junkWeight);
if (pick < outcome.junkWeight) {
outcome.pool = FishingLoot::Pool::Junk;
} else if (pick < outcome.junkWeight + outcome.treasureWeight) {
outcome.pool = FishingLoot::Pool::Treasure;
} else {
outcome.pool = FishingLoot::Pool::Fish;
}
int waited = rollBetween(rng, fishingMinWaitTicks, fishingBaseWaitTicks);
outcome.waitTicks = std::max(fishingMinWaitTicks / 2,
waited - lure * fishingLureTicksPerLevel);
LootContext poolContext = context;
poolContext.seed = rng;
outcome.caught = rollLootTable(fishingTables[static_cast<std::size_t>(outcome.pool)],
poolContext);
return outcome;
}
const LootRegistry& lootRegistry() {
static const LootRegistry registry;
return registry;
}
} // namespace vf