feat: extend LootTables
This commit is contained in:
1 parent
fdebb884e6
commit
20aecc6757
1 file changed
+145
@@ -630,3 +630,148 @@ LootTableId LootTableId::ofChest(ChestFill::Structure structure) {
|
|||||||
return LootTableId{Domain::Chest, static_cast<uint16_t>(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
|
||||||
Reference in new issue
Block a user