diff --git a/core/src/LootTables.cpp b/core/src/LootTables.cpp new file mode 100644 index 0000000..e9853a2 --- /dev/null +++ b/core/src/LootTables.cpp @@ -0,0 +1,152 @@ +#include "vf/LootTables.h" + +#include + +namespace vf { + +namespace { + +constexpr uint32_t defaultLootSeed = 0x9e3779b9u; + +uint32_t xorshift(uint32_t& state) { + state ^= state << 13; + state ^= state >> 17; + state ^= state << 5; + return state; +} + +int rollBelow(uint32_t& rng, int bound) { + if (bound <= 1) return 0; + return static_cast(xorshift(rng) % static_cast(bound)); +} + +int rollBetween(uint32_t& rng, int low, int high) { + if (high <= low) return low; + return low + rollBelow(rng, high - low + 1); +} + +void appendPool(LootTable& table, const LootEntry& entry) { + LootPool pool; + pool.entries.push_back(entry); + table.pools.push_back(pool); +} + +BlockDropRule plainRule(Block block, ItemId drop, ToolType tool) { + BlockDropRule rule; + rule.block = block; + rule.drop = drop; + rule.harvestTool = tool; + return rule; +} + +BlockDropRule selfRule(Block block, ToolType tool) { + return plainRule(block, itemForBlock(block), tool); +} + +BlockDropRule gatedRule(Block block, ItemId drop, ToolType tool, ToolTier tier) { + BlockDropRule rule = plainRule(block, drop, tool); + rule.minimumTier = tier; + rule.toolRequiredForDrop = true; + return rule; +} + +BlockDropRule selfGatedRule(Block block, ToolType tool, ToolTier tier) { + return gatedRule(block, itemForBlock(block), tool, tier); +} + +BlockDropRule oreRule(Block block, ItemId raw, int minCount, int maxCount, ToolTier tier) { + BlockDropRule rule = gatedRule(block, raw, ToolType::Pickaxe, tier); + rule.minCount = minCount; + rule.maxCount = maxCount; + rule.silkDrop = itemForBlock(block); + rule.fortuneMultiplies = true; + return rule; +} + +// Blocks the palette has no item for still carry a rule: it names the tool class +// and harvest level, and records that the block yields nothing rather than +// inventing an item that Item.h does not have. +BlockDropRule barrenRule(Block block, ToolType tool) { return plainRule(block, ItemId::None, tool); } + +BlockDropRule barrenGatedRule(Block block, ToolType tool, ToolTier tier) { + return gatedRule(block, ItemId::None, tool, tier); +} + +// Wooden crafted blocks salvage back to the planks they were built from. +BlockDropRule plankSalvage(Block block, int count = 1) { + BlockDropRule rule = plainRule(block, ItemId::Planks, ToolType::Axe); + rule.minCount = count; + rule.maxCount = count; + return rule; +} + +// The palette carries exactly one broken stone item, so the overworld stone +// family salvages to cobblestone. +BlockDropRule stoneSalvage(Block block) { + return gatedRule(block, ItemId::Cobblestone, ToolType::Pickaxe, ToolTier::Wood); +} + +std::vector buildBlockRules() { + std::vector rules; + rules.reserve(static_cast(Block::Count)); + auto add = [&rules](const BlockDropRule& rule) { rules.push_back(rule); }; + + add(barrenRule(Block::Air, ToolType::None)); + + BlockDropRule grass = plainRule(Block::Grass, ItemId::Dirt, ToolType::Shovel); + grass.silkDrop = ItemId::Grass; + add(grass); + + add(selfRule(Block::Dirt, ToolType::Shovel)); + + BlockDropRule stone = gatedRule(Block::Stone, ItemId::Cobblestone, ToolType::Pickaxe, + ToolTier::Wood); + stone.silkDrop = ItemId::Stone; + stone.firedDrop = ItemId::Stone; + add(stone); + + add(selfRule(Block::Sand, ToolType::Shovel)); + add(selfRule(Block::Wood, ToolType::Axe)); + + // No sapling in the palette, so a wood cutting stands in as the replantable + // drop, the way the salvaged leaf table already does. + BlockDropRule leaves = plainRule(Block::Leaves, ItemId::Wood, shearToolType); + leaves.dropChance = 0.05f; + leaves.chancePerFortune = 0.04f; + leaves.silkDrop = ItemId::Leaves; + leaves.shearHarvest = true; + leaves.bonusDrop = ItemId::Stick; + leaves.bonusChance = 0.02f; + leaves.bonusMax = 2; + add(leaves); + + add(barrenRule(Block::Water, ToolType::None)); + add(selfRule(Block::Snow, ToolType::Shovel)); + add(oreRule(Block::CoalOre, ItemId::CoalLump, 1, 1, ToolTier::Wood)); + + BlockDropRule ironOre = oreRule(Block::IronOre, ItemId::IronOre, 1, 1, ToolTier::Stone); + ironOre.firedDrop = ItemId::IronIngot; + add(ironOre); + + BlockDropRule goldOre = oreRule(Block::GoldOre, ItemId::GoldOre, 1, 1, ToolTier::Iron); + goldOre.firedDrop = ItemId::GoldIngot; + add(goldOre); + + add(selfRule(Block::Gravel, ToolType::Shovel)); + + BlockDropRule glowstone = oreRule(Block::Glowstone, ItemId::Glowstone, 2, 4, ToolTier::None); + glowstone.toolRequiredForDrop = false; + add(glowstone); + + BlockDropRule cobblestone = selfGatedRule(Block::Cobblestone, ToolType::Pickaxe, ToolTier::Wood); + cobblestone.firedDrop = ItemId::Stone; + add(cobblestone); + + add(selfRule(Block::Planks, ToolType::Axe)); + add(selfGatedRule(Block::Sandstone, ToolType::Pickaxe, ToolTier::Wood)); + + // Ice melts away unless it is taken whole. + BlockDropRule ice = barrenRule(Block::Ice, ToolType::Pickaxe); + ice.silkDrop = ItemId::Ice; + add(ice); +