Add groupcache and number providers; Add loottables (WIP)

This commit is contained in:
Elias Fleckenstein 2021-03-08 14:40:57 +01:00
parent 6e6809f360
commit ca94a1c354
10 changed files with 274 additions and 0 deletions

View file

@ -0,0 +1,32 @@
mcl_groupcache = {
cache = {},
}
local function check_insert(item, group, cache)
if minetest.get_item_group(item, group) ~= 0 then
table.insert(cache, item)
end
end
local old_register_item = minetest.register_item
function minetest.register_item(name, def)
old_register_item(name, def)
for group, cache in pairs(mcl_groupcache.cache) do
check_insert(item, group, cache)
end
end
function mcl_groupcache.init_cache(group)
local cache = {}
for item in pairs(minetest.registered_items) do
check_insert(item, group, cache)
end
return cache
end
function mcl_groupcache.get_items_in_group(group)
local cache = mcl_groupcache.cache[group] or mcl_groupcache.init_cache(group)
mcl_groupcache.cache[group] = cache
return cache
end

View file

@ -0,0 +1,3 @@
name = mcl_groupcache
author = Fleckenstein
description = Keep track of items with certain groups

View file

@ -0,0 +1,118 @@
mcl_loottables.register_entry = mcl_util.registration_function(mcl_loottables.entries)
mcl_loottables.register_table = mcl_util.registration_function(mcl_loottables.tables, function(name, def)
local function set_parents(parent)
for _, child in ipairs(parent.children or parent.entries or parent.pools or {}) do
child.parent = parent
set_parents(child)
end
end
set_parents(def)
end)
function mcl_loottables.get_table(def)
local t = type(def)
if t == "nil" then
return {}
elseif t == "string" then
return assert(mcl_loottables.tables[def])
elseif t == "table" then
return def
else
error("invalid loottable type: " .. t)
end
end
function mcl_loottables.get_entry_type(entry)
return assert(mcl_loottables.entries[entry.type])
end
function mcl_loottables.get_candidates(entries, data, func)
local candidates = {}
local functions = {}
for _, entry in ipairs(entries) do
local success = mcl_predicates.do_predicates(entry.conditions, data)
if success then
local children = entry.children
if children then
table.insert_all(candidates, mcl_loottables.get_candidates(children, data, mcl_loottables.get_entry_type(entry).preprocess))
else
table.insert(candidates, entry)
end
end
if func and func(success, data) then
break
end
end
return candidates
end
function mcl_loottables.do_item_modifiers(itemstack, node, data)
if node then
mcl_functions.do_item_modifiers(itemstack, node.functions, data)
mcl_loottables.do_item_modifiers(itemstack, node.parent, data)
end
end
function mcl_loottables.do_pools(pools, functions, data)
local luck = data.luck or 0
local stacks = {}
for _, pool in ipairs(pools or {}) do
if mcl_conditions.do_conditions(pool.conditions, data) do
local rolls = mcl_loottables.get_number(pool.rolls, data) + mcl_loottables.get_number(pool.bonus_rolls, data) * luck
for i = 1, rolls do
local candidates = mcl_loottables.get_candidates(pool.entries, data)
if #candidates > 0 then
local total_weight = 0
local weights = {}
for _, candidate in ipairs(candidates)
total_weight = total_weight + math.floor((candidate.weight or 1) + (candidate.quality or 0) * luck)
table.insert(weights, total_weight)
end
local selected
local rnd = mcl_util.rand(data.pr, 0, weight - 1)
for i, w in ipairs(weights) do
if rnd < w then
selected = candidates[i]
break
end
end
local func = mcl_loottables.get_entry_type(entry).process
local stacks = func(selected, data)
for _, stack in ipairs(stacks) do
mcl_loottables.do_item_modifiers(stack, selected, data)
end
table.insert_all(stacks, stack)
end
end
end
end
return stacks
end
function mcl_loottables.get_loot(def, data)
def = mcl_loottables.get_table(def)
return mcl_loottables.do_pools(def.pools)
end
function mcl_loottables.drop_loot(def, data)
local loot = mcl_loottables.get_loot(def)
local old_loot = table.copy(loot)
for _, stack in ipairs(old_loot) do
local max_stack = stack:get_stack_max()
while max_stack < stack:get_count() do
table.insert(loot, stack:take_items(max_stack))
end
end
return loot
end
function mcl_loottables.fill_chest(def, data)
end

View file

@ -0,0 +1,44 @@
mcl_loottables.register_entry("mcl_loottables:alternatives", {
preprocess = function(success, data)
return success
end,
})
mcl_loottables.register_entry("mcl_loottables:group", {
preprocess = function(success, data)
return false
end,
})
mcl_loottables.register_entry("mcl_loottables:sequence", {
preprocess = function(success, data)
return not success
end,
})
mcl_loottables.register_entry("mcl_loottables:tag", function(entry, data)
local stacks = mcl_groupcache.get_items_in_group(entry.name)
if entry.expand then
stacks = {stacks[pr:next(1, #stacks)]}
end
return stacks
end)
mcl_loottables.register_entry("mcl_loottables:loot_table", {
process = function(entry, data)
return mcl_loottables.get_loot(entry.name, data)
end,
})
mcl_loottables.register_entry("mcl_loottables:empty", {
process = function(entry, data)
return {}
end,
})
mcl_loottables.register_entry("mcl_loottables:item", {
process = function(entry, data)
return {item = ItemStack(entry.name)}
end,
})

View file

@ -0,0 +1,9 @@
mcl_loottables = {
tables = {},
entries = {},
}
local modpath = minetest.get_modpath("mcl_loottables")
dofile(modpath .. "/api.lua")
dofile(modpath .. "/entries.lua")

View file

@ -0,0 +1,4 @@
name = mcl_loot
author = Fleckenstein
description = Provides Minecraft-like loot table definitions
depends = mcl_util, mcl_predicates, mcl_item_modifiers

View file

@ -0,0 +1,15 @@
mcl_numbers.register_provider = mcl_util.registration_function(mcl_numbers.providers)
function mcl_numbers.get_number(provider, data)
local t = type(provider)
if t == "nil" then
return 0
elseif t == "number" then
return provider
elseif t == "table" then
local func = assert(mcl_numbers.providers[data.type])
return assert(tonumber(func(provider, data)))
else
error("invalid number type: " .. t)
end
end

View file

@ -0,0 +1,23 @@
mcl_numbers = {
providers = {},
}
mcl_numbers.register_provider("mcl_numbers:constant", function(provider)
return provider.value
end)
mcl_numbers.register_provider("mcl_numbers:uniform", function(provider, data)
return mcl_util.rand(data.pr, mcl_numbers.get_number(provider.min), mcl_numbers.get_number(provider.max))
end)
mcl_numbers.register_provider("mcl_numbers:binomial", function(provider, data)
local n = mcl_numbers.get_number(provider.n)
local num = 0
for i = 1, n do
if mcl_util.rand_bool(mcl_numbers.get_number(provider.p), data.pr) then
num = num + 1
end
end
return num
end)

View file

@ -0,0 +1,4 @@
name = mcl_numbers
author = Fleckenstein
description = Minecraft-like number providers
depends = mcl_util

View file

@ -418,3 +418,25 @@ function mcl_util.get_color(colorstr)
return colorstr, hex return colorstr, hex
end end
end end
function mcl_util.registration_function(tbl, func)
return function(name, def)
if func then
local res = func(name, def)
if res == false then
return
elseif res ~= nil then
def = res
end
end
tbl[name] = def
end
end
function mcl_util.rand(pr, ...)
return pr and pr:next(...) or math.random(...)
end
function mcl_util.rand_bool(probability, pr)
return mcl_util.rand(pr, 0, 32767) < probability * 32768
end