feat: introduce xorshift and rollBelow
This commit is contained in:
1 parent
38068617a9
commit
d234f6a884
1 file changed
+158
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user