From 67dd48b06ca2adae8bfb572cc623c0df7b22e430 Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Thu, 11 Mar 2021 10:05:46 +0100 Subject: [PATCH] Add predicates --- mods/CORE/mcl_loottables/api.lua | 29 ++++---- mods/CORE/mcl_numbers/api.lua | 34 ++++++--- mods/CORE/mcl_numbers/init.lua | 6 +- mods/CORE/mcl_predicates/api.lua | 84 +++++++++++++++++++++++ mods/CORE/mcl_predicates/init.lua | 110 ++++++++++++++++++++++++++++++ mods/CORE/mcl_predicates/mod.conf | 4 ++ mods/CORE/mcl_util/init.lua | 49 ++++++++++++- 7 files changed, 287 insertions(+), 29 deletions(-) create mode 100644 mods/CORE/mcl_predicates/api.lua create mode 100644 mods/CORE/mcl_predicates/init.lua create mode 100644 mods/CORE/mcl_predicates/mod.conf diff --git a/mods/CORE/mcl_loottables/api.lua b/mods/CORE/mcl_loottables/api.lua index 010434aff..edec0e5bc 100644 --- a/mods/CORE/mcl_loottables/api.lua +++ b/mods/CORE/mcl_loottables/api.lua @@ -11,25 +11,25 @@ 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 + return mcl_util.switch_type(def, { + ["nil"] = function() + return {} + end, + ["string"] = function() + return mcl_loottables.tables[def], "table" + end, + ["table"] = function() + return def + end, + }, "loot table") end function mcl_loottables.get_entry_type(entry) - return assert(mcl_loottables.entries[entry.type]) + return 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) @@ -87,7 +87,7 @@ function mcl_loottables.do_pools(pools, functions, data) local stacks = func(selected, data) for _, stack in ipairs(stacks) do - mcl_loottables.do_item_modifiers(stack, selected, data) + mcl_item_modifiers.do_item_modifiers(stack, selected, data) end table.insert_all(stacks, stack) end @@ -103,7 +103,7 @@ function mcl_loottables.get_loot(def, data) end function mcl_loottables.drop_loot(def, data) - local loot = mcl_loottables.get_loot(def) + local loot = mcl_loottables.get_loot(def, data) local old_loot = table.copy(loot) for _, stack in ipairs(old_loot) do local max_stack = stack:get_stack_max() @@ -115,4 +115,5 @@ function mcl_loottables.drop_loot(def, data) end function mcl_loottables.fill_chest(def, data) + local loot = mcl_loottables.get_loot(def, data) end diff --git a/mods/CORE/mcl_numbers/api.lua b/mods/CORE/mcl_numbers/api.lua index fcb8392ed..160001697 100644 --- a/mods/CORE/mcl_numbers/api.lua +++ b/mods/CORE/mcl_numbers/api.lua @@ -1,15 +1,27 @@ 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 + return mcl_util.switch_type(provider, { + ["number"] = function() + return provider + end, + ["table"] = function() + local func = mcl_numbers.providers[data.type] + return func(provider, data) + end, + }, "number provider") +end + +function mcl_numbers.check_bounds(actual, expected, data) + return mcl_util.switch_type(actual, { + ["nil"] = function() + return true + end, + ["number"] = function() + return actual == expected + end, + ["table"] = function() + return actual <= mcl_numbers.get_number(expected.max, data) and actual >= mcl_numbers.get_number(expected.min, data) + end, + }, "range") end diff --git a/mods/CORE/mcl_numbers/init.lua b/mods/CORE/mcl_numbers/init.lua index d539edb92..d9aeb3bfd 100644 --- a/mods/CORE/mcl_numbers/init.lua +++ b/mods/CORE/mcl_numbers/init.lua @@ -7,14 +7,14 @@ mcl_numbers.register_provider("mcl_numbers:constant", function(provider) 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)) + return mcl_util.rand(data.pr, mcl_numbers.get_number(provider.min, data), mcl_numbers.get_number(provider.max, data)) end) mcl_numbers.register_provider("mcl_numbers:binomial", function(provider, data) - local n = mcl_numbers.get_number(provider.n) + local n = mcl_numbers.get_number(provider.n, data) local num = 0 for i = 1, n do - if mcl_util.rand_bool(mcl_numbers.get_number(provider.p), data.pr) then + if mcl_util.rand_bool(mcl_numbers.get_number(provider.p, data), data.pr) then num = num + 1 end end diff --git a/mods/CORE/mcl_predicates/api.lua b/mods/CORE/mcl_predicates/api.lua new file mode 100644 index 000000000..163911b41 --- /dev/null +++ b/mods/CORE/mcl_predicates/api.lua @@ -0,0 +1,84 @@ +mcl_predicates.register_predicate = mcl_util.registration_function(mcl_predicates.predicates) + +function mcl_predicates.get_predicate(id) + return mcl_util.switch_type(id, { + ["function"] = function(v) + return v, + end, + ["string"] = function(v) + return mcl_predicates.predicates[v] + end, + }, "predicate") +end + +function mcl_predicates.do_predicates(predicates, data, or_mode) + or_mode = or_mode or false + for _, def in ipairs() do + local func = mcl_predicates.get_predicate(def.condition) + local failure = func and not func(def, data) or false + if or_mode ~= failure then + return or_mode + end + end + return not or_mode or #predicates == 0 +end + +function mcl_predicates.match_block(location, block, properties) + local node = minetest.get_node(location) + if node.name ~= block then + return false + end + if properties then + local meta = minetest.get_meta(location) + for k, v in pairs(properties) do + if meta:get_string(k) ~= v then + return false + end + end + end + return true +end + + +function mcl_predicates.match_tool(itemstack, predicate) + itemstack = itemstack or ItemStack() + + local itemname = itemstack:get_name() + + local expected_name = predicate.item + + if expected_name and itemname ~= expected_name then + return false + end + + local tag = predicate.tag + + if tag and minetest.get_item_group(itemname, predicate) == 0 then + return false + end + + if not mcl_numbers.check_bounds(itemstack:get_count(), predicate.count, data) then + return false + end + + -- ToDo: Durability, needs research, needs abstraction ? + -- ToDo: potions, "nbt" aka metadata + + local enchantments, stored_enchantments = predicate.enchantments, predicate.stored_enchantments + + if enchantments and stored_enchantments then + enchantments = table.copy(enchantments) + table.insert_all(enchantments, stored_enchantments) + elseif stored_enchantments then + enchantments = stored_enchantments + end + if enchantments then + local actual_enchantments = mcl_enchanting.get_enchantments(itemstack) + for _, def in ipairs(actual_enchantments) do + local level = actual_enchantments[def.enchantment] + if not mcl_numbers.check_bounds(level, def.levels or {min = 1, max = math.huge}, data) then + return false + end + end + end +end diff --git a/mods/CORE/mcl_predicates/init.lua b/mods/CORE/mcl_predicates/init.lua new file mode 100644 index 000000000..56457c366 --- /dev/null +++ b/mods/CORE/mcl_predicates/init.lua @@ -0,0 +1,110 @@ +mcl_predicates = { + predicates = {}, +} + +local modpath = minetest.get_modpath("mcl_predicates") + +dofile(modpath .. "/api.lua") + +mcl_predicates.register_predicate("alternative", function(predicate, data) + return mcl_predicates.do_predicates(predicate.terms, data, true) +end) + +mcl_predicates.register_predicate("block_state_property", function(predicate, data) + return mcl_predicates.match_block(predicate.block, predicate.properties, data.location) +end) + +mcl_predicates.register_predicate("damage_source_properties", function(predicate, data) + -- ToDo: damage source abstraction + return nil +end) + +mcl_predicates.register_predicate("entity_properties", function(predicate, data) + local entity = predicate.entity + if not entity or (entity ~= "this" and entity ~= "killer" and entity ~= "killer_player") then + return false + end + + local ref = data[entity] + if not ref then + return false + end + + -- ToDo: entity / player abstraction + return nil +end) + +mcl_predicates.register_predicate("entity_scores", function(predicate, data) + -- ToDo: scoreboards + return nil +end) + +mcl_predicates.register_predicate("inverted", function(predicate, data) + return not mcl_predicates.do_predicates({predicate.term}, data) +end) + +mcl_predicates.register_predicate("killed_by_player", function(predicate, data) + return not predicate.inverse ~= not data.killer_player +end) + +mcl_predicates.register_predicate("location_check", function(predicate, data) + local location = vector.new(data.location) + + location.x = location.x + (data.offsetX or 0) + location.y = location.y + (data.offsetY or 0) + location.z = location.z + (data.offsetZ or 0) + + -- ToDo, needs abstraction? + return nil +end) + +mcl_predicates.register_predicate("match_tool", function(predicate, data) + return mcl_predicates.match_tool(data.tool, predicate.predicate) +end) + +mcl_predicates.register_predicate("random_chance", function(predicate, data) + return mcl_util.rand_bool(predicate.chance, data.pr) +end) + +mcl_predicates.register_predicate("random_chance_with_looting", function(predicate, data) + local chance = predicate.chance -- + (looting_level * looting_multiplier) + + -- ToDo: entity / player abstraction + return mcl_util.rand_bool(chance, data.pr) +end) + +mcl_predicates.register_predicate("reference", function(predicate, data) + -- ToDo: needs research + return nil +end) + +mcl_predicates.register_predicate("survives_explosion", function(predicate, data) + return mcl_util.rand_bool(data.drop_chance or 1, data.pr) +end) + +mcl_predicates.register_predicate("table_bonus", function(predicate, data) + -- ToDo: entity / player abstraction + return nil +end) + +mcl_predicates.register_predicate("time_check", function(predicate, data) + -- ToDo: needs research + return nil +end) + +mcl_predicates.register_predicate("weather_check", function(predicate, data) + local weather = mcl_weather.get_weather() + + if predicate.thundering then + return weather == "thunder" + elseif predicate.raining then + return weather == "rain" + else + return true + end +end) + +mcl_predicates.register_predicate("value_check", function(predicate, data) + local value = mcl_numbers.get_number(predicate.value, data) + return mcl_numbers.check_in_bounds(value, predicate.range, data) +end) diff --git a/mods/CORE/mcl_predicates/mod.conf b/mods/CORE/mcl_predicates/mod.conf new file mode 100644 index 000000000..843c06afb --- /dev/null +++ b/mods/CORE/mcl_predicates/mod.conf @@ -0,0 +1,4 @@ +name = mcl_predicates +author = Fleckenstein +description = Provides MC-like predicates +depends = mcl_util, mcl_number diff --git a/mods/CORE/mcl_util/init.lua b/mods/CORE/mcl_util/init.lua index 13432ed0c..7b0686a50 100644 --- a/mods/CORE/mcl_util/init.lua +++ b/mods/CORE/mcl_util/init.lua @@ -438,5 +438,52 @@ function mcl_util.rand(pr, ...) end function mcl_util.rand_bool(probability, pr) - return mcl_util.rand(pr, 0, 32767) < probability * 32768 + if probability >= 1 then + return true + elseif probability <= 0 then + return false + else + return mcl_util.rand(pr, 0, 32767) < probability * 32768 + end end + +function mcl_util.switch_type(value, funcs, name) + local t = type(value) + local func = funcs[func] or funcs["default"] + if func then + return func(value, ...) + else + error("invalid " .. (name and name .. " " or "") .. "type: " .. t) + end +end + +--[[ +function mcl_util.assert_type(value, expected_type, allow_nil) + local actual_type = type(value) + local errmsg = value .. " is not a " .. expected_type + + if expected_type == "itemstack" then + return mcl_util.switch_type(value, { + ["nil"] = function() + assert(allow_nil, errmsg) + end, + ["string"] = ItemStack, + ["table"] = ItemStack, + ["userdata"] = function() + assert(value.get_name, errmsg) + return value + end, + }, "ItemStack") + end + + if expected_type == "vector" then + assert(actual_type == "table", errmsg) + assert(value.x, errmsg) + assert(value.y, errmsg) + assert(value.z, errmsg) + else + assert((allow_nil and value == nil) or (actual_type == expected_type), errmsg) + end + return value +end +]]--