feat: introduce xorshift and rollBelow

This commit is contained in:
dualfroz committed 2026-09-08 14:56:35 +02:00
1 parent 38068617a9
commit d234f6a884
1 file changed
+158
+158
View File
@@ -0,0 +1,158 @@
#include "vf/Loot.h"
#include <algorithm>
namespace vf {
namespace {
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<int>(xorshift(rng) % static_cast<uint32_t>(bound));
}
int rollBetween(uint32_t& rng, int low, int high) {
if (high <= low) return low;
return low + rollBelow(rng, high - low + 1);
}
float unitFloat(uint32_t& rng) { return static_cast<float>(xorshift(rng)) / 4294967295.0f; }
bool conditionsPass(const LootEntry& entry, const LootContext& context) {
if ((entry.conditions & LootEntry::PlayerKill) && !context.playerKill) return false;
if ((entry.conditions & LootEntry::Fiery) && !context.fiery) return false;
if ((entry.conditions & LootEntry::NotFiery) && context.fiery) return false;
if ((entry.conditions & LootEntry::SilkTouched) && !context.silkTouch()) return false;
if ((entry.conditions & LootEntry::NotSilkTouched) && context.silkTouch()) return false;
if ((entry.conditions & LootEntry::NeedsLooting) && context.looting() <= 0) return false;
if ((entry.conditions & LootEntry::NeedsFortune) && context.fortune() <= 0) return false;
if ((entry.conditions & LootEntry::Explosive) && context.killedBy != DamageType::Explosion) {
return false;
}
if ((entry.conditions & LootEntry::Ranged) && !isProjectileDamage(context.killedBy)) {
return false;
}
return true;
}
void addToRoll(LootRoll& roll, ItemId item, int count) {
if (item == ItemId::None || count <= 0) return;
if (isTool(item)) {
for (int i = 0; i < count; ++i) roll.items.push_back(makeToolStack(item));
return;
}
for (ItemSlot& stack : roll.items) {
if (stack.item != item) continue;
stack.count += count;
return;
}
roll.items.push_back({item, count, 0});
}
void emitEntry(LootRoll& roll, const LootEntry& entry, const LootContext& context, uint32_t& rng) {
float chance = entry.chance;
if (entry.functions & LootEntry::ChanceLooting) {
chance += entry.chancePerLevel * static_cast<float>(context.looting());
}
if (entry.functions & LootEntry::ChanceFortune) {
chance += entry.chancePerLevel * static_cast<float>(context.fortune());
}
if (chance < 1.0f && unitFloat(rng) > chance) return;
int count = entry.minCount;
if (entry.functions & LootEntry::CountRange) {
count = rollBetween(rng, entry.minCount, entry.maxCount);
}
if (entry.functions & LootEntry::OreFortune) {
int fortune = context.fortune();
if (fortune > 0) count *= std::max(1, rollBelow(rng, fortune + 2));
}
if (entry.functions & LootEntry::LootingBonus) {
count += rollBelow(rng, context.looting() + 1);
}
ItemId item = entry.item;
if ((entry.functions & LootEntry::SmeltIfFiery) && context.fiery) item = smeltedForm(item);
addToRoll(roll, item, count);
}
LootEntry fixedDrop(ItemId item, int count = 1) {
LootEntry entry;
entry.item = item;
entry.minCount = count;
entry.maxCount = count;
return entry;
}
LootEntry rangedDrop(ItemId item, int minCount, int maxCount) {
LootEntry entry;
entry.item = item;
entry.minCount = minCount;
entry.maxCount = maxCount;
entry.functions = LootEntry::CountRange;
return entry;
}
LootEntry weightedDrop(ItemId item, int weight, int minCount, int maxCount) {
LootEntry entry = rangedDrop(item, minCount, maxCount);
entry.weight = weight;
return entry;
}
LootEntry chanceDrop(ItemId item, float chance, float perLevel, uint32_t chanceFunction) {
LootEntry entry = fixedDrop(item);
entry.chance = chance;
entry.chancePerLevel = perLevel;
entry.functions = chanceFunction;
return entry;
}
void addPool(LootTable& table, const LootEntry& entry) {
LootPool pool;
pool.entries.push_back(entry);
table.pools.push_back(pool);
}
LootTable oreTable(Block block, ItemId rawDrop, int minCount, int maxCount) {
LootEntry silk = fixedDrop(itemForBlock(block));
silk.conditions = LootEntry::SilkTouched;
LootEntry raw = rangedDrop(rawDrop, minCount, maxCount);
raw.conditions = LootEntry::NotSilkTouched;
raw.functions = LootEntry::CountRange | LootEntry::OreFortune;
LootTable table;
addPool(table, silk);
addPool(table, raw);
return table;
}
LootTable selfDropTable(Block block) {
LootTable table;
ItemId self = itemForBlock(block);
if (self != ItemId::None) addPool(table, fixedDrop(self));
return table;
}
LootTable stoneTable() {
LootEntry silk = fixedDrop(ItemId::Stone);
silk.conditions = LootEntry::SilkTouched;
LootEntry cobble = fixedDrop(ItemId::Cobblestone);
cobble.conditions = LootEntry::NotSilkTouched;
LootTable table;
addPool(table, silk);
addPool(table, cobble);
return table;
}
LootTable leavesTable() {
LootEntry silk = fixedDrop(ItemId::Leaves);
silk.conditions = LootEntry::SilkTouched;