forked from VoxeLibre/VoxeLibre
Compare commits
15 Commits
Author | SHA1 | Date |
---|---|---|
Lizzy Fleckenstein | 87462ee853 | |
Lizzy Fleckenstein | 8f735cc644 | |
Lizzy Fleckenstein | 422076f761 | |
Lizzy Fleckenstein | 9ce59d4438 | |
Lizzy Fleckenstein | 99357d467c | |
Lizzy Fleckenstein | 018fc64a27 | |
Lizzy Fleckenstein | b53329b452 | |
Lizzy Fleckenstein | c27dea59b0 | |
Lizzy Fleckenstein | 80c6b547e5 | |
Lizzy Fleckenstein | 0de53a9dcb | |
Lizzy Fleckenstein | 67dd48b06c | |
Lizzy Fleckenstein | 34c31cac1f | |
Lizzy Fleckenstein | 788b6dc080 | |
Lizzy Fleckenstein | 200ce76c70 | |
Lizzy Fleckenstein | ca94a1c354 |
|
@ -0,0 +1,159 @@
|
||||||
|
local Object = {}
|
||||||
|
|
||||||
|
-- Define a getter that caches the result for the next time it is called
|
||||||
|
-- This is a static method (self = the class); in this class system static methods start with __ by convention
|
||||||
|
function Object:__cache_getter(name, func)
|
||||||
|
-- cache key: prevent overriding the getter function itself
|
||||||
|
local key = "_" .. name
|
||||||
|
-- add a function to the class
|
||||||
|
self[name] = function(self)
|
||||||
|
-- check if the value is present in the cache
|
||||||
|
local value = self[key]
|
||||||
|
|
||||||
|
-- `== nil` instead of `not value` to allow caching boolean values
|
||||||
|
if value == nil then
|
||||||
|
-- call the getter function
|
||||||
|
value = func(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- store result in cache
|
||||||
|
self[key] = value
|
||||||
|
|
||||||
|
-- return result
|
||||||
|
return value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Define a getter / setter
|
||||||
|
-- If no argument is specified, it will act as a getter, else as a setter
|
||||||
|
-- The specified function MUST return the new value, if it returns nil, nil will be used as new value
|
||||||
|
-- Optionally works in combination with a previously defined cache getter and only really makes sense in that context
|
||||||
|
function Object:__setter(name, func)
|
||||||
|
-- since the function is overridden, we need to store the old one in case a cache getter is defined
|
||||||
|
local cache_getter = self[name]
|
||||||
|
-- use same key as cache getter to modify getter cache if present
|
||||||
|
local key = "_" .. name
|
||||||
|
|
||||||
|
self[name] = function(self, new)
|
||||||
|
-- check whether an argument was specified
|
||||||
|
if new == nil then
|
||||||
|
if cache_getter then
|
||||||
|
-- call the cache getter if present
|
||||||
|
return cache_getter(self)
|
||||||
|
else
|
||||||
|
-- return the value else
|
||||||
|
return self[key]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- call the setter and set the new value to the result
|
||||||
|
self[key] = func(self, new)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Define a comparator function
|
||||||
|
-- Acts like a setter, except that it does not set the new value but rather compares the present and specified values and returns whether they are equal or not
|
||||||
|
-- Incompatible with setter
|
||||||
|
-- The function is optional. The == operator is used else.
|
||||||
|
function Object:__comparator(name, func)
|
||||||
|
local cache_getter = self[name]
|
||||||
|
local key = "_" .. name
|
||||||
|
|
||||||
|
self[name] = function(self, expected)
|
||||||
|
-- the current value is needed everytime, no matter whether there is an argument or not
|
||||||
|
local actual
|
||||||
|
|
||||||
|
if cache_getter then
|
||||||
|
-- call the cache getter if present
|
||||||
|
actual = cache_getter(self)
|
||||||
|
else
|
||||||
|
-- use the value else
|
||||||
|
actual = self[key]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- act as a getter if there is no argument
|
||||||
|
if expected == nil then
|
||||||
|
return actual
|
||||||
|
end
|
||||||
|
|
||||||
|
if func then
|
||||||
|
-- if a function as specified, call it
|
||||||
|
return func(actual, expected)
|
||||||
|
else
|
||||||
|
-- else, use the == operator to compare the expected value to the actual
|
||||||
|
return actual == expected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Override an already existing function in a way that the old function is called
|
||||||
|
-- If nil is returned, the old function is called. Else the return value is returned. (Only the first return value is taken into concern here, multiple are supported tho)
|
||||||
|
-- This works even if it is applied to the instance of a class when the function is defined by the class
|
||||||
|
-- It also works with overriding functions that are located in superclasses
|
||||||
|
function Object:__override(name, func)
|
||||||
|
-- store the old function
|
||||||
|
local old_func = self[name]
|
||||||
|
|
||||||
|
-- redefine the function with variable arguments
|
||||||
|
self[name] = function(...)
|
||||||
|
-- call the new function and store the return values in a table
|
||||||
|
local rvals = {func(...)}
|
||||||
|
-- if nil was returned, fall back to the old function
|
||||||
|
if rvals[1] == nil then
|
||||||
|
-- if present, call the return function with the values the new function returned (converted back to a tuple)
|
||||||
|
return old_func(...)
|
||||||
|
else
|
||||||
|
-- return the values from the new function else
|
||||||
|
return unpack(rvals)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Works like override except that the new function does not modify the output of the old function but rather the input
|
||||||
|
-- The new function can decide with what arguments by returing them, including the `self` reference
|
||||||
|
-- If the "self" arg is not returned the old function is not called
|
||||||
|
-- Note that this way the new function cannot change the return value of the old function
|
||||||
|
function Object:__pipe(name, func)
|
||||||
|
local old_func = self[name]
|
||||||
|
|
||||||
|
self[name] = function(self, ...)
|
||||||
|
local rvals = {func(self, ...)}
|
||||||
|
-- check if self was returned properly
|
||||||
|
if rvals[1] then
|
||||||
|
-- if present, call the return function with the values the new function returned (converted back to a tuple)
|
||||||
|
return old_func(unpack(rvals))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
-- Make class available as table to distribute the Object table
|
||||||
|
class = setmetatable({Object = Object}, {
|
||||||
|
-- Create a new class by calling class() with an optional superclass argument
|
||||||
|
__call = function(super)
|
||||||
|
return setmetatable({}, {
|
||||||
|
-- Create a new instance of the class when the class is called
|
||||||
|
__call = function(_class, ...)
|
||||||
|
-- Check whether the first argument is an instance of the class
|
||||||
|
-- If that is the case, just return it - this is to allow "making sure something is the instance of a class" by calling the constructor
|
||||||
|
local argtbl = {...}
|
||||||
|
local first_arg = args[1]
|
||||||
|
if first_arg and type(first_arg) == "table" and inst.CLASS = _class then
|
||||||
|
return inst
|
||||||
|
end
|
||||||
|
-- set the metatable and remember which class the object belongs to
|
||||||
|
local instance = setmetatable({CLASS = _class}, {
|
||||||
|
__index = _class,
|
||||||
|
})
|
||||||
|
-- call the constructor if present
|
||||||
|
if instance.constructor then
|
||||||
|
instance:constructor(...)
|
||||||
|
end
|
||||||
|
-- return the created instance
|
||||||
|
return instance
|
||||||
|
end,
|
||||||
|
-- Object as superclass of all classes that dont have a different one
|
||||||
|
__index = super or Object,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = class
|
||||||
|
author = Fleckenstein
|
||||||
|
description = A class system for MineClone2
|
|
@ -0,0 +1,29 @@
|
||||||
|
MCLDamageSource = class()
|
||||||
|
|
||||||
|
function MCLDamageSource:constructor(tbl)
|
||||||
|
if tbl then
|
||||||
|
for k, v in pairs(tbl) do
|
||||||
|
self[k] = v
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLDamageSource:from_mt(reason)
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLDamageSource:__getter("direct_object", function(self)
|
||||||
|
local obj = self.raw_source_object
|
||||||
|
if not obj then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return mcl_object_mgr.get(obj)
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLDamageSource:__getter("source_object", function(self)
|
||||||
|
local direct = self:direct_object()
|
||||||
|
if not direct then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
return direct.source_object or direct
|
||||||
|
end)
|
|
@ -0,0 +1,44 @@
|
||||||
|
MCLEntity = class(MCLObject)
|
||||||
|
|
||||||
|
MCLEntity:__getter("meta", MCLMetadata)
|
||||||
|
|
||||||
|
local last_inv_id = 0
|
||||||
|
|
||||||
|
MCLEntity:__getter("inventory", function(self)
|
||||||
|
local info = self.inventory_info
|
||||||
|
if not info then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
self.inventory_id = "mcl_entity:" .. last_inv_id
|
||||||
|
last_inv_id = last_inv_id + 1
|
||||||
|
local inv = minetest.create_detached_inventory(self.inventory_id, self.inventory_callbacks)
|
||||||
|
for list, size in pairs(data.sizes) do
|
||||||
|
inv:set_size(list, size)
|
||||||
|
end
|
||||||
|
for list, liststr in pairs(data.lists) do
|
||||||
|
inv:set_list(list, liststr)
|
||||||
|
end
|
||||||
|
return inv
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLEntity:on_activate(staticdata)
|
||||||
|
local data = minetest.deserialize(staticdata)
|
||||||
|
if data then
|
||||||
|
self:meta():from_table(data)
|
||||||
|
self.inventory_info = data.inventory
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLEntity:get_staticdata()
|
||||||
|
local data = self:meta():to_table()
|
||||||
|
local inventory_info = self.inventory_info
|
||||||
|
|
||||||
|
if inventory_info then
|
||||||
|
data.inventory = {
|
||||||
|
sizes = inventory_info.sizes,
|
||||||
|
lists = self:inventory():get_lists()
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
return minetest.serialize(data)
|
||||||
|
end
|
|
@ -0,0 +1,103 @@
|
||||||
|
MCLEquipment = class()
|
||||||
|
|
||||||
|
function MCLEquipment:constructor(inv, idx)
|
||||||
|
self.inv = inv
|
||||||
|
self.idx = idx
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLEquipment:__cache_getter("has_main", function(self)
|
||||||
|
return self.inv and self.idx and self.inv:get_list("main") and true or false
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLEquipment:__cache_getter("has_right", function(self)
|
||||||
|
return self.inv and self.inv:get_list("right_hand") and true or false
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLEquipment:__cache_getter("has_left", function(self)
|
||||||
|
return self.inv and self.inv:get_list("left_hand") and true or false
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLEquipment:__cache_getter("has_armor", function(self)
|
||||||
|
return self.inv and self.inv:get_list("armor") and true or false
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLEquipment:mainhand()
|
||||||
|
if self:has_main() then
|
||||||
|
return self.inv:get_stack("main", self.idx)
|
||||||
|
elseif self:has_right() then
|
||||||
|
return self.inv:get_stack("right_hand", 1)
|
||||||
|
else
|
||||||
|
return ItemStack()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLEquipment:__setter("mainhand", function(self, new)
|
||||||
|
if self:has_main() then
|
||||||
|
self.inv:set_stack("main", self.idx, stack)
|
||||||
|
elseif self:has_right() then
|
||||||
|
self.inv:set_stack("right_hand", 1, stack)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLEquipment:offhand()
|
||||||
|
if self:has_left() then
|
||||||
|
return self.inv:get_stack("left_hand", 1)
|
||||||
|
else
|
||||||
|
return ItemStack()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLEquipment:__setter("offhand", function(self, new)
|
||||||
|
if self:has_left() then
|
||||||
|
self.inv:set_stack("left_hand", 1, new)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLEquipment:__armor(idx, name)
|
||||||
|
self[name] = function(self)
|
||||||
|
if self:has_armor() then
|
||||||
|
return self.inv:get_stack("armor", idx)
|
||||||
|
else
|
||||||
|
return ItemStack()
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
self:__setter(name, function(self, new)
|
||||||
|
if self:has_armor() then
|
||||||
|
self.inv:set_stack("armor", idx, new)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
local armor_slots = {"helmet", "chestplace", "leggings", "boots"}
|
||||||
|
|
||||||
|
for i, name in ipairs(armor_slots) do
|
||||||
|
MCLEquipment:__armor(idx, name)
|
||||||
|
end
|
||||||
|
|
||||||
|
local function insert(tbl, key, stack)
|
||||||
|
if stack:get_name() ~= "" then
|
||||||
|
tbl[key] = stack
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLEquipment:get_armor()
|
||||||
|
local tbl = {}
|
||||||
|
if self:has_armor() then
|
||||||
|
for i, name in ipairs(armor_slots) do
|
||||||
|
insert(tbl, name, self.inv:get_stack("armor", i))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return tbl
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLEquipment:get_all()
|
||||||
|
local tbl = {}
|
||||||
|
insert(tbl, "mainhand", self:mainhand())
|
||||||
|
insert(tbl, "offhand", self:offhand())
|
||||||
|
for k, v in pairs(self:get_armor()) do
|
||||||
|
tbl[k] = v
|
||||||
|
end
|
||||||
|
return tbl
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,66 @@
|
||||||
|
mcl_gamerules = {
|
||||||
|
__defaults = {},
|
||||||
|
__rules = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
setmetatable(mcl_gamerules, {__index = mcl_gamerules.__rules})
|
||||||
|
|
||||||
|
local worldpath = minetest.get_worldpath()
|
||||||
|
|
||||||
|
function mcl_gamerules.__load()
|
||||||
|
local file = io.open(worldpath .. "gamerules.json", "r")
|
||||||
|
if file then
|
||||||
|
local contents = file:read("*all")
|
||||||
|
file:close()
|
||||||
|
local data = minetest.parse_json(contents)
|
||||||
|
local rules = mcl_gamerules.__rules
|
||||||
|
for rule, default in pairs(mcl_gamerules.__defaults) do
|
||||||
|
local value = data[rule]
|
||||||
|
if value == nil then
|
||||||
|
value = default
|
||||||
|
end
|
||||||
|
rules[rule] = value
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_gamerules.__save()
|
||||||
|
local file = io.open(worldpath .. "gamerules.json", "w")
|
||||||
|
file:write(minetest.write_json(mcl_gamerules.__rules, true))
|
||||||
|
file:close()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_gamerules.__set(rule, value)
|
||||||
|
if not mcl_gamerules.__defaults[rule] then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
mcl_gamerules.__rules[rule] = value
|
||||||
|
mcl_gamerules.__save()
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_gamerules.__register(rule, default)
|
||||||
|
mcl_gamerules.__defaults[rule] = default
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_gamerules.__register("announceAdvancements", true)
|
||||||
|
mcl_gamerules.__register("commandBlockOutput", true)
|
||||||
|
mcl_gamerules.__register("doDaylightCycle", true)
|
||||||
|
mcl_gamerules.__register("doFireTick", true)
|
||||||
|
mcl_gamerules.__register("doImmediateRespawn", false)
|
||||||
|
mcl_gamerules.__register("doMobLoot", true)
|
||||||
|
mcl_gamerules.__register("doMobSpawning", true)
|
||||||
|
mcl_gamerules.__register("doTileDrops", true)
|
||||||
|
mcl_gamerules.__register("doWeatherCycle", true)
|
||||||
|
mcl_gamerules.__register("drowningDamage", true)
|
||||||
|
mcl_gamerules.__register("fallDamage", true)
|
||||||
|
mcl_gamerules.__register("fireDamage", true)
|
||||||
|
mcl_gamerules.__register("keepInventory", false)
|
||||||
|
mcl_gamerules.__register("logAdminCommands", true)
|
||||||
|
mcl_gamerules.__register("mobGriefing", true)
|
||||||
|
mcl_gamerules.__register("naturalRegeneration", true)
|
||||||
|
mcl_gamerules.__register("pvp", true)
|
||||||
|
mcl_gamerules.__register("showDeathMessages", true)
|
||||||
|
mcl_gamerules.__register("tntExplodes", true)
|
||||||
|
|
||||||
|
minetest.register_on_mods_loaded(mcl_gamerules.__load)
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
||||||
|
name = mcl_groupcache
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Keep track of items with certain groups
|
|
@ -0,0 +1,43 @@
|
||||||
|
MCLItemStack = class()
|
||||||
|
|
||||||
|
function MCLItemStack:constructor(stack)
|
||||||
|
self.stack = stack
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLItemStack:__getter("enchantments", function(self)
|
||||||
|
return mcl_enchanting.get_enchantments(self.stack)
|
||||||
|
end)
|
||||||
|
MCLItemStack:__comparator("enchantments", mcl_types.match_enchantments)
|
||||||
|
|
||||||
|
function MCLItemStack:meta()
|
||||||
|
return self.stack:get_meta()
|
||||||
|
end
|
||||||
|
MCLItemStack:__comparator("meta", mcl_types.match_meta)
|
||||||
|
|
||||||
|
function MCLItemStack:get_enchantment(name)
|
||||||
|
return self:enchantments()[name] or 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:has_enchantment(name)
|
||||||
|
return self:get_enchantment(name) > 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:total_durability()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:durability()
|
||||||
|
local def = self.stack:get_definition()
|
||||||
|
if def then
|
||||||
|
local base_uses = def._durability
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:use_durability()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:restore_durability()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLItemStack:get_group()
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
MCLMetadata = class()
|
||||||
|
|
||||||
|
function MCLMetadata:constructor()
|
||||||
|
self.fields = {}
|
||||||
|
end
|
||||||
|
|
||||||
|
for _type, default in pairs({string = "", float = 0.0, int = 0}) do
|
||||||
|
MCLMetadata["set_" .. _type] = function(name, value) do
|
||||||
|
if value == default then
|
||||||
|
value = nil
|
||||||
|
end
|
||||||
|
self.fields[name] = value
|
||||||
|
end
|
||||||
|
MCLMetadata["get_" .. _type] = function(name) do
|
||||||
|
return self.fields[name] or default
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLMetadata:to_table()
|
||||||
|
return table.copy(self)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLMetadata:from_table(tbl)
|
||||||
|
self.fields = table.copy(tbl.fields)
|
||||||
|
end
|
|
@ -0,0 +1,18 @@
|
||||||
|
MCLMob = class(MCLEntity)
|
||||||
|
|
||||||
|
function MCLMob:get_hp()
|
||||||
|
self:meta():get_float("hp")
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLMob:set_hp()
|
||||||
|
self:meta():set_float("hp", hp)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLMob:on_damage(damage, source)
|
||||||
|
MCLEntity.on_damage(self, damage, source)
|
||||||
|
|
||||||
|
local new_hp = self:get_hp()
|
||||||
|
if new_hp <= 0 and new_hp + damage > 0 then
|
||||||
|
self:on_death(source)
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,121 @@
|
||||||
|
MCLObject = class()
|
||||||
|
|
||||||
|
function MCLObject:constructor(obj)
|
||||||
|
self.object = obj.object or obj
|
||||||
|
self.IS_MCL_OBJECT = true
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:on_punch(hitter, time_from_last_punch, tool_capabilities, dir, damage)
|
||||||
|
local source = MCLDamageSource({is_punch = true, raw_source_object = hitter})
|
||||||
|
|
||||||
|
local knockback = {
|
||||||
|
hitter = hitter,
|
||||||
|
time_from_last_punch = time_from_last_punch,
|
||||||
|
tool_capabilities = tool_capabilities,
|
||||||
|
dir = dir,
|
||||||
|
}
|
||||||
|
|
||||||
|
self:damage(damage, source, knockback)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:apply_knockback(strength, dir)
|
||||||
|
local oldvel = self.object:get_velocity()
|
||||||
|
local vel = vector.multiply(vector.normalize(vector.new(dir.x, 0, dir.z)), strength)
|
||||||
|
|
||||||
|
if self:is_on_ground() then
|
||||||
|
local old_y = oldvel.y / 2
|
||||||
|
|
||||||
|
y = math.min(0.4, old_y / 2 + strenth)
|
||||||
|
vel.y
|
||||||
|
end
|
||||||
|
|
||||||
|
vel = vector.subtract(vel, vector.divide(vector.new(oldvel.x, 0, oldvel.z), 2))
|
||||||
|
|
||||||
|
self.object:add_velocity(vel)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- use this function to deal regular damage to an object (do NOT use :punch() unless toolcaps need to be handled)
|
||||||
|
function MCLObject:damage(damage, source, knockback)
|
||||||
|
damage = self:damage_modifier(damage, source) or damage
|
||||||
|
self:set_hp(self:get_hp() - damage)
|
||||||
|
|
||||||
|
if type(knockback) == "table" then
|
||||||
|
knockback = self:calculate_knockback(
|
||||||
|
knockback.hitter,
|
||||||
|
knockback.time_from_last_punch,
|
||||||
|
knockback.tool_capabilities,
|
||||||
|
knockback.dir or vector.direction(knockback.hitter:get_pos(), self.object:get_pos(),
|
||||||
|
knockback.distance or vector.distance(knockback.hitter:get_pos(), self.object:get_pos(),
|
||||||
|
damage,
|
||||||
|
source)
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(self.damage_info, {
|
||||||
|
damage = damage,
|
||||||
|
source = source,
|
||||||
|
knockback = knockback,
|
||||||
|
})
|
||||||
|
|
||||||
|
return damage
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:wield_index()
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLObject:__getter("equipment", function(self)
|
||||||
|
return MCLEquipment(self:inventory(), self:wield_index())
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLObject:get_hp()
|
||||||
|
return self.object:get_hp()
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:set_hp(hp)
|
||||||
|
self.object:set_hp(hp)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:death_drop(inventory, listname, index, stack)
|
||||||
|
minetest.add_item(self.object:get_pos(), stack)
|
||||||
|
inventory:set_stack(listname, index, nil)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:on_death(source)
|
||||||
|
local inventory = self:inventory()
|
||||||
|
if inventory then
|
||||||
|
for listname, list in pairs(inventory:get_lists()) do
|
||||||
|
for index, stack in pairs(list) do
|
||||||
|
if stack:get_name() ~= "" and then
|
||||||
|
self:death_drop(inventory, listname, index, stack)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:damage_modifier(damage, source)
|
||||||
|
if self.invulnerable and not source.bypasses_invulnerability then
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:on_damage(damage, source, knockback)
|
||||||
|
end
|
||||||
|
|
||||||
|
MCLObject.calculate_knockback = minetest.calculate_knockback
|
||||||
|
|
||||||
|
function minetest.calculate_knockback()
|
||||||
|
return 0
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:on_step()
|
||||||
|
local damage_info = self.damage_info
|
||||||
|
if damage_info then
|
||||||
|
self.damage_info = nil
|
||||||
|
self:on_damage(damage_info.damage, damage_info.source)
|
||||||
|
|
||||||
|
if damage_info.knockback then
|
||||||
|
self.object:add_velocity(damage_info.knockback)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,121 @@
|
||||||
|
mcl_object_mgr = {
|
||||||
|
players = {}
|
||||||
|
}
|
||||||
|
|
||||||
|
-- functions
|
||||||
|
|
||||||
|
function mcl_object_mgr.get(obj)
|
||||||
|
local rval
|
||||||
|
|
||||||
|
if mcl_object_mgr.is_mcl_object(obj) then
|
||||||
|
rval = obj
|
||||||
|
elseif mcl_object_mgr.is_player(obj) then
|
||||||
|
rval = mcl_object_mgr.get_player(obj)
|
||||||
|
elseif mcl_object_mgr.is_entity(obj) then
|
||||||
|
rval = mcl_object_mgr.get_entity(obj)
|
||||||
|
end
|
||||||
|
|
||||||
|
return assert(rval, "No matching MCLObject found. This is most likely an error caused by custom mods.")
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.is_mcl_object(obj)
|
||||||
|
return type(obj) == "table" and obj.IS_MCL_OBJECT
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.is_player(obj)
|
||||||
|
return type(obj) == "string" or type(obj) == "userdata" and obj:is_player()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.is_is_entity(obj)
|
||||||
|
return type(obj) == "table" and obj.object or type(obj) == "userdata" and obj:get_luaentity()
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.get_entity(ent)
|
||||||
|
if type(ent) == "userdata" then
|
||||||
|
ent = ent:get_luaentity()
|
||||||
|
end
|
||||||
|
return ent.mcl_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.get_player(name)
|
||||||
|
if type(name) == "userdata" then
|
||||||
|
name = name:get_player_name()
|
||||||
|
end
|
||||||
|
return mcl_player_mgr.players[name]
|
||||||
|
end
|
||||||
|
|
||||||
|
-- entity wrappers
|
||||||
|
|
||||||
|
local function add_entity_wrapper(def, name)
|
||||||
|
def[name] = function(luaentity, ...)
|
||||||
|
local func = self.mcl_entity[name]
|
||||||
|
if func then
|
||||||
|
return func(self.mcl_entity, ...)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_object_mgr.register_entity(name, initial_properties, base_class)
|
||||||
|
local def = {
|
||||||
|
initial_properties = initial_properties,
|
||||||
|
|
||||||
|
on_activate = function(self, ...)
|
||||||
|
local entity = base_class(self.object)
|
||||||
|
self.mcl_entity = entity
|
||||||
|
if entity.on_activate then
|
||||||
|
entity:on_activate(...)
|
||||||
|
end
|
||||||
|
end,
|
||||||
|
}
|
||||||
|
|
||||||
|
add_entity_wrapper(def, "on_deactivate")
|
||||||
|
add_entity_wrapper(def, "on_step")
|
||||||
|
add_entity_wrapper(def, "on_punch")
|
||||||
|
add_entity_wrapper(def, "on_death")
|
||||||
|
add_entity_wrapper(def, "on_rightclick")
|
||||||
|
add_entity_wrapper(def, "on_attach_child")
|
||||||
|
add_entity_wrapper(def, "on_detach_child")
|
||||||
|
add_entity_wrapper(def, "on_detach")
|
||||||
|
add_entity_wrapper(def, "get_staticdata")
|
||||||
|
|
||||||
|
minetest.register_entity(name, def)
|
||||||
|
end
|
||||||
|
|
||||||
|
-- player wrappers
|
||||||
|
|
||||||
|
minetest.register_on_joinplayer(function(player)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
mcl_player_mgr.players[name] = MCLPlayer(player)
|
||||||
|
mcl_player_mgr.players[name]:on_join()
|
||||||
|
end)
|
||||||
|
|
||||||
|
minetest.register_on_leaveplayer(function(player)
|
||||||
|
local name = player:get_player_name()
|
||||||
|
mcl_player_mgr.players[name]:on_leave()
|
||||||
|
mcl_player_mgr.players[name] = nil
|
||||||
|
end)
|
||||||
|
|
||||||
|
local function add_player_wrapper(wrapper, regfunc)
|
||||||
|
minetest[regfunc or "register_" .. wrapper .. "player"](function(player, ...)
|
||||||
|
local mclplayer = mcl_player_mgr.players[player:get_player_name()]
|
||||||
|
local func = mclplayer[funcname or wrapper]
|
||||||
|
if func then
|
||||||
|
func(mclplayer, ...)
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
end
|
||||||
|
|
||||||
|
add_player_wrapper("on_punch")
|
||||||
|
add_player_wrapper("on_rightclick")
|
||||||
|
add_player_wrapper("on_death", "register_on_dieplayer")
|
||||||
|
add_player_wrapper("on_respawn")
|
||||||
|
|
||||||
|
minetest.register_on_player_hpchange(function(player, hp_change, reason)
|
||||||
|
if not reason.auto then
|
||||||
|
local mclplayer = mcl_object_mgr.get(player)
|
||||||
|
local source = MCLDamageSource():from_mt(reason)
|
||||||
|
|
||||||
|
mclplayer:damage(hp_change, source)
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end, true)
|
|
@ -0,0 +1,19 @@
|
||||||
|
MCLPlayer = class(MCLObject)
|
||||||
|
|
||||||
|
MCLPlayer:__cache_getter("meta", function(self)
|
||||||
|
return self.object:get_meta()
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLPlayer:__cache_getter("inventory", function(self)
|
||||||
|
return self.object:get_inventory()
|
||||||
|
end)
|
||||||
|
|
||||||
|
MCLPlayer:__override_pipe("death_drop", function(self, inventory, listname, index, stack)
|
||||||
|
if not mcl_gamerules.keepInventory then
|
||||||
|
return self, inventory, listname, index, stack
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
|
function MCLPlayer:on_damage(damage, source, knockback)
|
||||||
|
MCLObject.on_damage(self, damage, source, knockback)
|
||||||
|
end
|
|
@ -418,3 +418,31 @@ 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)
|
||||||
|
if probability >= 1 then
|
||||||
|
return true
|
||||||
|
elseif probability <= 0 then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return mcl_util.rand(pr, 0, 32767) < probability * 32768
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
|
@ -0,0 +1,101 @@
|
||||||
|
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_entry_type(entry)
|
||||||
|
return mcl_loottables.entries[entry.type]
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_loottables.get_candidates(entries, data, func)
|
||||||
|
local candidates = {}
|
||||||
|
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_item_modifiers.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_predicates.do_predicates(pool.conditions, data) do
|
||||||
|
local rolls = mcl_numbers.get_number(pool.rolls, data) + mcl_numbers.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 pool_stacks = mcl_loottables.get_entry_type(entry).process(selected, data)
|
||||||
|
|
||||||
|
for _, stack in ipairs(pool_stacks) do
|
||||||
|
mcl_item_modifiers.do_item_modifiers(stack, selected, data)
|
||||||
|
end
|
||||||
|
table.insert_all(stacks, pool_stacks)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return stacks
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_loottables.get_loot(def, data)
|
||||||
|
if type(def) == "string" then
|
||||||
|
def = mcl_loottables.tables[def]
|
||||||
|
end
|
||||||
|
return mcl_loottables.do_pools(def.pools)
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_loottables.drop_loot(def, data)
|
||||||
|
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()
|
||||||
|
while max_stack < stack:get_count() do
|
||||||
|
table.insert(loot, stack:take_items(max_stack))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return loot
|
||||||
|
end
|
|
@ -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,
|
||||||
|
})
|
||||||
|
|
|
@ -0,0 +1,9 @@
|
||||||
|
mcl_loottables = {
|
||||||
|
tables = {},
|
||||||
|
entries = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
local modpath = minetest.get_modpath("mcl_loottables")
|
||||||
|
|
||||||
|
dofile(modpath .. "/api.lua")
|
||||||
|
dofile(modpath .. "/entries.lua")
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_loottables
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Provides Minecraft-like loot table definitions
|
||||||
|
depends = mcl_util, mcl_predicates, mcl_item_modifiers, mcl_groupcache
|
|
@ -0,0 +1,19 @@
|
||||||
|
mcl_numbers.register_provider = mcl_util.registration_function(mcl_numbers.providers)
|
||||||
|
|
||||||
|
function mcl_numbers.get_number(provider, data)
|
||||||
|
if type(provider) == "number" then
|
||||||
|
return provider
|
||||||
|
else
|
||||||
|
mcl_numbers.providers[data.type](provider, data)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_numbers.match_bounds(actual, expected, data)
|
||||||
|
if type(expected) == "table" then
|
||||||
|
expected = {
|
||||||
|
min = mcl_numbers.get_number(expected.min, data),
|
||||||
|
max = mcl_numbers.get_number(expected.max, data)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
return mcl_util.match_bounds(actual, expected)
|
||||||
|
end
|
|
@ -0,0 +1,25 @@
|
||||||
|
mcl_numbers = {
|
||||||
|
providers = {},
|
||||||
|
}
|
||||||
|
|
||||||
|
dofile(minetest.get_modpath("mcl_numbers") .. "/api.lua")
|
||||||
|
|
||||||
|
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, 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, data)
|
||||||
|
local num = 0
|
||||||
|
for i = 1, n do
|
||||||
|
if mcl_util.rand_bool(mcl_numbers.get_number(provider.p, data), data.pr) then
|
||||||
|
num = num + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return num
|
||||||
|
end)
|
||||||
|
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_numbers
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Minecraft-like number providers
|
||||||
|
depends = mcl_util
|
|
@ -0,0 +1,15 @@
|
||||||
|
mcl_predicates.register_predicate = mcl_util.registration_function(mcl_predicates.predicates)
|
||||||
|
|
||||||
|
function mcl_predicates.do_predicates(predicates, data, or_mode)
|
||||||
|
or_mode = or_mode or false
|
||||||
|
for _, func in ipairs(predicates) do
|
||||||
|
if type(func) == "string" then
|
||||||
|
func = mcl_predicates.predicates[func]
|
||||||
|
end
|
||||||
|
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
|
|
@ -0,0 +1,104 @@
|
||||||
|
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("match_node", function(predicate, data)
|
||||||
|
return mcl_types.match_node(data.node or minetest.get_node(data.pos), predicate, data.pos, data.nodemeta)
|
||||||
|
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 pos = vector.add(data.pos, vector.new(predicate.offset_x or 0, predicate.offset_y or 0, predicate.offset_z or 0))
|
||||||
|
return mcl_location(pos, data.nodemeta):match(predicate.predicate)
|
||||||
|
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)
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_predicates
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Provides MC-like predicates
|
||||||
|
depends = mcl_util, mcl_numbers
|
|
@ -0,0 +1,88 @@
|
||||||
|
mcl_types = {}
|
||||||
|
|
||||||
|
function mcl_types.match_bounds(actual, expected)
|
||||||
|
if type(expected) == "table" then
|
||||||
|
return actual <= expected.max and actual >= expected.min
|
||||||
|
else
|
||||||
|
return actual == expected
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_meta(actual, expected)
|
||||||
|
for k, v in pairs(expected) do
|
||||||
|
if actual:get_string(k) ~= v then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_vector(actual, expected)
|
||||||
|
for k, v in pairs(expected) do
|
||||||
|
if not mcl_types.match_bounds(actual[k], expected[k]) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_enchantments(actual, expected)
|
||||||
|
for _, v in ipairs(expected) do
|
||||||
|
if not mcl_types.match_bounds(actual[v.enchantment] or 0, v.levels or {min = 1, max = math.huge}) then
|
||||||
|
return false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_item(actual, expected)
|
||||||
|
actual = actual or ItemStack()
|
||||||
|
|
||||||
|
if expected.item and actual:get_name() ~= expected.item then
|
||||||
|
return false
|
||||||
|
elseif expected.group and minetest.get_item_group(actual:get_name(), expected.group) == 0 then
|
||||||
|
return false
|
||||||
|
elseif expected.count and not mcl_types.match_bounds(actual:get_count(), expected.count) then
|
||||||
|
return false
|
||||||
|
elseif expected.wear and not mcl_types.match_bounds(actual:get_wear(), expected.wear) then
|
||||||
|
return false
|
||||||
|
elseif expected.enchantments and not mcl_types.match_enchantments(mcl_enchanting.get_enchantments(actual), expected.enchantments) then
|
||||||
|
return false
|
||||||
|
elseif expected.meta and not mcl_types.match_meta(actual:get_meta(), expected.meta) then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_node(actual, expected, pos, meta)
|
||||||
|
if expected.node and actual.name ~= expected.node then
|
||||||
|
return false
|
||||||
|
elseif expected.group and minetest.get_item_group(actual.name, expected.group) == 0 then
|
||||||
|
return false
|
||||||
|
elseif expected.param1 and actual.param1 ~= compare.param1 then
|
||||||
|
return false
|
||||||
|
elseif expected.param2 and actual.param2 ~= compare.param2 then
|
||||||
|
return false
|
||||||
|
elseif expected.meta and not mcl_types.match_meta(meta or minetest.get_meta(pos), expected.meta) then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
function mcl_types.match_pos(actual, expected, meta)
|
||||||
|
if expected.pos and not mcl_types.match_vector(actual, expected.pos) then
|
||||||
|
return false
|
||||||
|
elseif expected.dimension and mcl_worlds.pos_to_dimension(actual) ~= expected.dimension then
|
||||||
|
return false
|
||||||
|
elseif expected.biome and minetest.get_biome_name(minetest.get_biome_data(actual).biome) ~= expected.biome then
|
||||||
|
return false
|
||||||
|
elseif expected.node and not mcl_types.match_node(minetest.get_node(actual), expected.node, actual, meta) then
|
||||||
|
return false
|
||||||
|
elseif expected.light and not mcl_types.match_bounds(minetest.get_node_light(actual), expected.light) then
|
||||||
|
return false
|
||||||
|
else
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,4 @@
|
||||||
|
name = mcl_types
|
||||||
|
author = Fleckenstein
|
||||||
|
description = Offers compare functions for many types, used for MineClone2 datapacks
|
||||||
|
depends =
|
|
@ -0,0 +1,2 @@
|
||||||
|
name = DATA
|
||||||
|
description = Meta-modpack containing the datapack system for MineClone 2
|
|
@ -9,7 +9,6 @@ local modpath = minetest.get_modpath(minetest.get_current_modname())
|
||||||
armor = {
|
armor = {
|
||||||
timer = 0,
|
timer = 0,
|
||||||
elements = {"head", "torso", "legs", "feet"},
|
elements = {"head", "torso", "legs", "feet"},
|
||||||
physics = {"jump","speed","gravity"},
|
|
||||||
formspec = "size[8,8.5]image[2,0.75;2,4;armor_preview]"
|
formspec = "size[8,8.5]image[2,0.75;2,4;armor_preview]"
|
||||||
.."list[current_player;main;0,4.5;8,4;]"
|
.."list[current_player;main;0,4.5;8,4;]"
|
||||||
.."list[current_player;craft;4,1;3,3;]"
|
.."list[current_player;craft;4,1;3,3;]"
|
||||||
|
@ -18,7 +17,6 @@ armor = {
|
||||||
.."listring[current_player;craft]",
|
.."listring[current_player;craft]",
|
||||||
textures = {},
|
textures = {},
|
||||||
default_skin = "character",
|
default_skin = "character",
|
||||||
last_damage_types = {},
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if minetest.get_modpath("mcl_skins") then
|
if minetest.get_modpath("mcl_skins") then
|
||||||
|
@ -33,7 +31,7 @@ elseif minetest.get_modpath("wardrobe") then
|
||||||
skin_mod = "wardrobe"
|
skin_mod = "wardrobe"
|
||||||
end
|
end
|
||||||
|
|
||||||
function armor.on_armor_use(itemstack, user, pointed_thing)
|
function mcl_armor.rightclick_equip(itemstack, user, pointed_thing)
|
||||||
if not user or user:is_player() == false then
|
if not user or user:is_player() == false then
|
||||||
return itemstack
|
return itemstack
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,105 @@
|
||||||
|
local old_damage_modifier = MCLObject.damage_modifier
|
||||||
|
|
||||||
|
function MCLObject:damage_modifier(damage, source)
|
||||||
|
local damage_old = old_damage_modifier(damage, source)
|
||||||
|
if damage_old then
|
||||||
|
return damage_old
|
||||||
|
end
|
||||||
|
|
||||||
|
if damage < 0 then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
if source.bypasses_armor and source.bypasses_magic then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
|
||||||
|
local uses = math.max(1, math.floor(math.abs(damage) / 4))
|
||||||
|
|
||||||
|
local source_object = source:source_object()
|
||||||
|
local equipment = self:equipment()
|
||||||
|
|
||||||
|
local points = self:base_armor_points()
|
||||||
|
local toughness = 0
|
||||||
|
local protection_factor = 0
|
||||||
|
local thorns_damage_regular = 0
|
||||||
|
local thorns_damage_irregular = 0
|
||||||
|
local thorns_pieces = {}
|
||||||
|
|
||||||
|
for location, rawstack in pairs(equipment:get_armor()) do
|
||||||
|
local stack = MCLItemStack(rawstack)
|
||||||
|
|
||||||
|
if not source.bypasses_armor then
|
||||||
|
points = points + stack:group("armor_points")
|
||||||
|
toughness = toughness + stack:group("armor_toughness")
|
||||||
|
|
||||||
|
stack:use_durability(uses)
|
||||||
|
equipment[location](equipment, rawstack)
|
||||||
|
end
|
||||||
|
|
||||||
|
if not source.bypasses_magic then
|
||||||
|
protection_factor = protection_factor + 1 * stack:get_enchantment("protection")
|
||||||
|
|
||||||
|
if source.is_explosion then
|
||||||
|
protection_factor = protection_factor + 2 * stack:get_enchantment("blast_protection")
|
||||||
|
end
|
||||||
|
|
||||||
|
if source.is_fire then
|
||||||
|
protection_factor = protection_factor + 2 * stack:get_enchantment("fire_protection")
|
||||||
|
end
|
||||||
|
|
||||||
|
if source.is_projectile then
|
||||||
|
protection_factor = protection_factor + 2 * stack:get_enchantment("projectile_protection")
|
||||||
|
end
|
||||||
|
|
||||||
|
if source.is_fall then
|
||||||
|
protection_factor = protection_factor + 3 * stack:get_enchantment("feather_falling")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
if source_object then
|
||||||
|
local thorns_level = stack:get_enchantment("thorns")
|
||||||
|
|
||||||
|
if thorns_level > 0 then
|
||||||
|
local do_irregular_damage = thorns_level > 10
|
||||||
|
|
||||||
|
if do_irregular_damage or thorns_damage_regular < 4 and math.random() < thorns_level * 0.15 then
|
||||||
|
if do_irregular_damage then
|
||||||
|
thorns_damage_irregular = thorns_damage_irregular + thorns_level - 10
|
||||||
|
else
|
||||||
|
thorns_damage_regular = math.min(4, thorns_damage_regular + math.random(4))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
table.insert(thorns_pieces, {location = location, stack = stack})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
-- https://minecraft.gamepedia.com/Armor#Damage_protection
|
||||||
|
damage = damage * (1 - math.min(20, math.max((points / 5), points - damage / (2 + (toughness / 4)))) / 25)
|
||||||
|
|
||||||
|
-- https://minecraft.gamepedia.com/Armor#Enchantments
|
||||||
|
damage = damage * (1 - math.min(20, protection_factor) / 25)
|
||||||
|
|
||||||
|
local thorns_damage = thorns_damage_regular + thorns_damage_irregular
|
||||||
|
|
||||||
|
if thorns_damage > 0 and source_object ~= self then
|
||||||
|
local thorns_damage_source = MCLDamageSource({direct_object = self, source_object = source_object, is_thorns = true})
|
||||||
|
local thorns_knockback = {hitter = self}
|
||||||
|
|
||||||
|
source_object:damage(thorns_damage, thorns_damage_source, thorns_knockback)
|
||||||
|
|
||||||
|
local piece = thorns_pieces[math.random(#thorns_pieces)]
|
||||||
|
local mclstack = piece.stack
|
||||||
|
mclstack:use_durability(2)
|
||||||
|
equipment[piece.location](equipment, mclstack.stack)
|
||||||
|
end
|
||||||
|
|
||||||
|
return math.floor(damage + 0.5)
|
||||||
|
end
|
||||||
|
|
||||||
|
function MCLObject:base_armor_points()
|
||||||
|
return 0
|
||||||
|
end
|
|
@ -0,0 +1,160 @@
|
||||||
|
local S = minetest.get_translator("mcl_armor")
|
||||||
|
|
||||||
|
mcl_enchanting.enchantments.protection = {
|
||||||
|
name = S("Protection"),
|
||||||
|
max_level = 4,
|
||||||
|
primary = {armor_points = true},
|
||||||
|
secondary = {},
|
||||||
|
disallow = {non_combat_armor = true},
|
||||||
|
incompatible = {blast_protection = true, fire_protection = true, projectile_protection = true},
|
||||||
|
weight = 10,
|
||||||
|
description = S("Reduces most types of damage by 4% for each level."),
|
||||||
|
curse = false,
|
||||||
|
on_enchant = function() end,
|
||||||
|
requires_tool = false,
|
||||||
|
treasure = false,
|
||||||
|
power_range_table = {{1, 12}, {12, 23}, {23, 34}, {34, 45}},
|
||||||
|
inv_combat_tab = true,
|
||||||
|
inv_tool_tab = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_enchanting.enchantments.blast_protection = {
|
||||||
|
name = S("Blast Protection"),
|
||||||
|
max_level = 4,
|
||||||
|
primary = {armor_points = true},
|
||||||
|
secondary = {},
|
||||||
|
disallow = {},
|
||||||
|
incompatible = {fire_protection = true, protection = true, projectile_protection = true},
|
||||||
|
weight = 2,
|
||||||
|
description = S("Reduces explosion damage and knockback."),
|
||||||
|
curse = false,
|
||||||
|
on_enchant = function() end,
|
||||||
|
requires_tool = false,
|
||||||
|
treasure = false,
|
||||||
|
power_range_table = {{5, 13}, {13, 21}, {21, 29}, {29, 37}},
|
||||||
|
inv_combat_tab = true,
|
||||||
|
inv_tool_tab = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_enchanting.enchantments.fire_protection = {
|
||||||
|
name = S("Fire Protection"),
|
||||||
|
max_level = 4,
|
||||||
|
primary = {armor_points = true},
|
||||||
|
secondary = {},
|
||||||
|
disallow = {},
|
||||||
|
incompatible = {blast_protection = true, protection = true, projectile_protection = true},
|
||||||
|
weight = 5,
|
||||||
|
description = S("Reduces fire damage."),
|
||||||
|
curse = false,
|
||||||
|
on_enchant = function() end,
|
||||||
|
requires_tool = false,
|
||||||
|
treasure = false,
|
||||||
|
power_range_table = {{10, 18}, {18, 26}, {26, 34}, {34, 42}},
|
||||||
|
inv_combat_tab = true,
|
||||||
|
inv_tool_tab = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_enchanting.enchantments.projectile_protection = {
|
||||||
|
name = S("Projectile Protection"),
|
||||||
|
max_level = 4,
|
||||||
|
primary = {armor_points = true},
|
||||||
|
secondary = {},
|
||||||
|
disallow = {},
|
||||||
|
incompatible = {blast_protection = true, fire_protection = true, protection = true},
|
||||||
|
weight = 5,
|
||||||
|
description = S("Reduces projectile damage."),
|
||||||
|
curse = false,
|
||||||
|
on_enchant = function() end,
|
||||||
|
requires_tool = false,
|
||||||
|
treasure = false,
|
||||||
|
power_range_table = {{1, 16}, {11, 26}, {21, 36}, {31, 46}, {41, 56}},
|
||||||
|
inv_combat_tab = true,
|
||||||
|
inv_tool_tab = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_enchanting.enchantments.feather_falling = {
|
||||||
|
name = S("Feather Falling"),
|
||||||
|
max_level = 4,
|
||||||
|
primary = {armor_feet = true},
|
||||||
|
secondary = {},
|
||||||
|
disallow = {non_combat_armor = true},
|
||||||
|
incompatible = {},
|
||||||
|
weight = 5,
|
||||||
|
description = S("Reduces fall damage."),curse = false,
|
||||||
|
on_enchant = function() end,
|
||||||
|
requires_tool = false,
|
||||||
|
treasure = false,
|
||||||
|
power_range_table = {{5, 11}, {11, 17}, {17, 23}, {23, 29}},
|
||||||
|
inv_combat_tab = true,
|
||||||
|
inv_tool_tab = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_enchanting.enchantments.thorns = {
|
||||||
|
name = S("Thorns"),
|
||||||
|
max_level = 3,
|
||||||
|
primary = {armor_points = true},
|
||||||
|
secondary = {},
|
||||||
|
disallow = {},
|
||||||
|
incompatible = {},
|
||||||
|
weight = 1,
|
||||||
|
description = S("Reflects some of the damage taken when hit, at the cost of reducing durability with each proc."),
|
||||||
|
curse = false,
|
||||||
|
on_enchant = function() end,
|
||||||
|
requires_tool = false,
|
||||||
|
treasure = false,
|
||||||
|
power_range_table = {{10, 61}, {30, 71}, {50, 81}},
|
||||||
|
inv_combat_tab = true,
|
||||||
|
inv_tool_tab = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_enchanting.enchantments.curse_of_binding = {
|
||||||
|
name = S("Curse of Binding"),
|
||||||
|
max_level = 1,
|
||||||
|
primary = {},
|
||||||
|
secondary = {armor = true},
|
||||||
|
disallow = {},
|
||||||
|
incompatible = {},
|
||||||
|
weight = 1,
|
||||||
|
description = S("Item cannot be removed from armor slots except due to death, breaking or in Creative Mode."),
|
||||||
|
curse = true,
|
||||||
|
on_enchant = function() end,
|
||||||
|
requires_tool = false,
|
||||||
|
treasure = true,
|
||||||
|
power_range_table = {{25, 50}},
|
||||||
|
inv_combat_tab = true,
|
||||||
|
inv_tool_tab = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
mcl_enchanting.enchantments.frost_walker = {
|
||||||
|
name = S("Frost Walker"),
|
||||||
|
max_level = 2,
|
||||||
|
primary = {},
|
||||||
|
secondary = {boots = true},
|
||||||
|
disallow = {non_combat_armor = true},
|
||||||
|
incompatible = {depth_strider = true},
|
||||||
|
weight = 2,
|
||||||
|
description = S("Turns water beneath the player into frosted ice and prevents the damage from magma blocks."),
|
||||||
|
curse = false,
|
||||||
|
on_enchant = function() end,
|
||||||
|
requires_tool = false,
|
||||||
|
treasure = true,
|
||||||
|
power_range_table = {{10, 25}, {20, 35}},
|
||||||
|
inv_combat_tab = true,
|
||||||
|
inv_tool_tab = false,
|
||||||
|
}
|
||||||
|
|
||||||
|
walkover.register_global(function(pos, _, player)
|
||||||
|
local boots = MCLItemStack(mcl_object_mgr.get(player):equipment():boots())
|
||||||
|
if not boots:has_enchantment("frost_walker") then
|
||||||
|
return
|
||||||
|
end
|
||||||
|
local radius = boots:get_enchantment("frost_walker") + 2
|
||||||
|
local minp = {x = pos.x - radius, y = pos.y, z = pos.z - radius}
|
||||||
|
local maxp = {x = pos.x + radius, y = pos.y, z = pos.z + radius}
|
||||||
|
local positions = minetest.find_nodes_in_area_under_air(minp, maxp, "mcl_core:water_source")
|
||||||
|
for _, p in ipairs(positions) do
|
||||||
|
if vector.distance(pos, p) <= radius then
|
||||||
|
minetest.set_node(p, {name = "mcl_core:frosted_ice_0"})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end)
|
|
@ -1,13 +1,68 @@
|
||||||
local S = minetest.get_translator("mcl_armor")
|
local S = minetest.get_translator("mcl_armor")
|
||||||
|
|
||||||
dofile(minetest.get_modpath(minetest.get_current_modname()).."/armor.lua")
|
mcl_armor = {
|
||||||
dofile(minetest.get_modpath(minetest.get_current_modname()).."/alias.lua")
|
elements = {
|
||||||
|
{
|
||||||
|
name = "helmet",
|
||||||
|
description = S("Helmet"),
|
||||||
|
durability = 0.6875,
|
||||||
|
recipe = function(m)
|
||||||
|
return {
|
||||||
|
{ m, m, m},
|
||||||
|
{ m, "", m},
|
||||||
|
{"", "", ""},
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "chestplate",
|
||||||
|
description = S("Chestplate"),
|
||||||
|
durability = 1.0,
|
||||||
|
recipe = function(m)
|
||||||
|
return {
|
||||||
|
{ m, "", m},
|
||||||
|
{ m, m, m},
|
||||||
|
{ m, m, m},
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "leggings",
|
||||||
|
description = S("Leggings"),
|
||||||
|
durability = 0.9375,
|
||||||
|
recipe = function(m)
|
||||||
|
return {
|
||||||
|
{ m, m, m},
|
||||||
|
{ m, "", m},
|
||||||
|
{ m, "", m},
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name = "boots",
|
||||||
|
description = S("Boots"),
|
||||||
|
durability = 0.8125,
|
||||||
|
recipe = function(m)
|
||||||
|
return {
|
||||||
|
{ m, "", m},
|
||||||
|
{ m, "", m},
|
||||||
|
}
|
||||||
|
end,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
longdesc = S("This is a piece of equippable armor which reduces the amount of damage you receive."),
|
||||||
|
usagehelp = S("To equip it, put it on the corresponding armor slot in your inventory menu."),
|
||||||
|
}
|
||||||
|
|
||||||
|
local modpath = minetest.get_modpath("mcl_armor")
|
||||||
|
|
||||||
|
dofile(modpath .. "/damage.lua")
|
||||||
|
dofile(modpath .. "/alias.lua")
|
||||||
|
dofile(modpath .. "/items.lua")
|
||||||
|
dofile(modpath .. "/enchantments.lua")
|
||||||
|
|
||||||
-- Regisiter Head Armor
|
-- Regisiter Head Armor
|
||||||
|
|
||||||
local longdesc = S("This is a piece of equippable armor which reduces the amount of damage you receive.")
|
|
||||||
local usage = S("To equip it, put it on the corresponding armor slot in your inventory menu.")
|
|
||||||
|
|
||||||
minetest.register_tool("mcl_armor:helmet_leather", {
|
minetest.register_tool("mcl_armor:helmet_leather", {
|
||||||
description = S("Leather Cap"),
|
description = S("Leather Cap"),
|
||||||
_doc_items_longdesc = longdesc,
|
_doc_items_longdesc = longdesc,
|
||||||
|
@ -323,70 +378,3 @@ local craft_ingreds = {
|
||||||
diamond = { "mcl_core:diamond" },
|
diamond = { "mcl_core:diamond" },
|
||||||
chain = { nil, "mcl_core:iron_nugget"} ,
|
chain = { nil, "mcl_core:iron_nugget"} ,
|
||||||
}
|
}
|
||||||
|
|
||||||
for k, v in pairs(craft_ingreds) do
|
|
||||||
-- material
|
|
||||||
local m = v[1]
|
|
||||||
-- cooking result
|
|
||||||
local c = v[2]
|
|
||||||
if m ~= nil then
|
|
||||||
minetest.register_craft({
|
|
||||||
output = "mcl_armor:helmet_"..k,
|
|
||||||
recipe = {
|
|
||||||
{m, m, m},
|
|
||||||
{m, "", m},
|
|
||||||
{"", "", ""},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
minetest.register_craft({
|
|
||||||
output = "mcl_armor:chestplate_"..k,
|
|
||||||
recipe = {
|
|
||||||
{m, "", m},
|
|
||||||
{m, m, m},
|
|
||||||
{m, m, m},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
minetest.register_craft({
|
|
||||||
output = "mcl_armor:leggings_"..k,
|
|
||||||
recipe = {
|
|
||||||
{m, m, m},
|
|
||||||
{m, "", m},
|
|
||||||
{m, "", m},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
minetest.register_craft({
|
|
||||||
output = "mcl_armor:boots_"..k,
|
|
||||||
recipe = {
|
|
||||||
{m, "", m},
|
|
||||||
{m, "", m},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
end
|
|
||||||
if c ~= nil then
|
|
||||||
minetest.register_craft({
|
|
||||||
type = "cooking",
|
|
||||||
output = c,
|
|
||||||
recipe = "mcl_armor:helmet_"..k,
|
|
||||||
cooktime = 10,
|
|
||||||
})
|
|
||||||
minetest.register_craft({
|
|
||||||
type = "cooking",
|
|
||||||
output = c,
|
|
||||||
recipe = "mcl_armor:chestplate_"..k,
|
|
||||||
cooktime = 10,
|
|
||||||
})
|
|
||||||
minetest.register_craft({
|
|
||||||
type = "cooking",
|
|
||||||
output = c,
|
|
||||||
recipe = "mcl_armor:leggings_"..k,
|
|
||||||
cooktime = 10,
|
|
||||||
})
|
|
||||||
minetest.register_craft({
|
|
||||||
type = "cooking",
|
|
||||||
output = c,
|
|
||||||
recipe = "mcl_armor:boots_"..k,
|
|
||||||
cooktime = 10,
|
|
||||||
})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,52 @@
|
||||||
|
local S = minetest.get_translator("mcl_armor")
|
||||||
|
|
||||||
|
function mcl_armor.register_set(def)
|
||||||
|
local modname = minetest.get_current_modname()
|
||||||
|
local sounds = {
|
||||||
|
_mcl_armor_equip = "mcl_armor_equip_" .. def.material,
|
||||||
|
_mcl_armor_unequip = "mcl_armor_unequip_" .. def.material,
|
||||||
|
}
|
||||||
|
|
||||||
|
for _, elem in pairs(mcl_armor.elements) do
|
||||||
|
local item_name = elem.name .. "_" .. def.name
|
||||||
|
local full_name = modname .. ":" .. item_name
|
||||||
|
|
||||||
|
minetest.register_tool(full_name, {
|
||||||
|
description = def.custom_descriptions[elem.name] or def.description .. " " .. elem.description,
|
||||||
|
_doc_items_longdesc = mcl_armor.longdesc,
|
||||||
|
_doc_items_usagehelp = mcl_armor.usagehelp,
|
||||||
|
inventory_image = modname .. "_inv_" .. item_name .. ".png",
|
||||||
|
groups = {[elem.name] = 1, armor_points = 1, armor = 1, enchantability = def.enchantability}, -- ToDo: armor_points
|
||||||
|
sounds = sounds,
|
||||||
|
on_place = mcl_armor.rightclick_equip,
|
||||||
|
on_secondary_use = mcl_armor.rightclick_equip,
|
||||||
|
_durability = def.durability * elem.durability,
|
||||||
|
_repair_material = def.craft_item,
|
||||||
|
})
|
||||||
|
|
||||||
|
if def.craft_material then
|
||||||
|
minetest.register_craft({
|
||||||
|
output = full_name,
|
||||||
|
recipe = elem.recipe(def.craft_material),
|
||||||
|
})
|
||||||
|
end
|
||||||
|
|
||||||
|
if def.cook_material then
|
||||||
|
minetest.register_craft({
|
||||||
|
type = "cooking",
|
||||||
|
output = def.cook_material,
|
||||||
|
recipe = full_name,
|
||||||
|
cooktime = 10,
|
||||||
|
})
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
mcl_armor.register_set {
|
||||||
|
name = "iron",
|
||||||
|
description = S("Iron"),
|
||||||
|
durability = 240,
|
||||||
|
enchantability =
|
||||||
|
craft_material = "mcl_core:iron_ingot",
|
||||||
|
cook_material = "mcl_core:iron_nugget",
|
||||||
|
}
|
|
@ -1050,6 +1050,12 @@ minetest.register_on_joinplayer(function(player)
|
||||||
inv:set_size("enderchest", 9*3)
|
inv:set_size("enderchest", 9*3)
|
||||||
end)
|
end)
|
||||||
|
|
||||||
|
function MCLPlayer:__override_pipe("death_drop", function(self, inventory, listname, index, stack)
|
||||||
|
if listname ~= "enderchest" then
|
||||||
|
return self, inventory, listname, index, stack
|
||||||
|
end
|
||||||
|
end)
|
||||||
|
|
||||||
minetest.register_craft({
|
minetest.register_craft({
|
||||||
output = 'mcl_chests:ender_chest',
|
output = 'mcl_chests:ender_chest',
|
||||||
recipe = {
|
recipe = {
|
||||||
|
|
|
@ -48,25 +48,6 @@ mcl_enchanting.enchantments.bane_of_arthropods = {
|
||||||
inv_tool_tab = false,
|
inv_tool_tab = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- implemented in mcl_armor
|
|
||||||
mcl_enchanting.enchantments.blast_protection = {
|
|
||||||
name = S("Blast Protection"),
|
|
||||||
max_level = 4,
|
|
||||||
primary = {armor_head = true, armor_torso = true, armor_legs = true, armor_feet = true},
|
|
||||||
secondary = {},
|
|
||||||
disallow = {non_combat_armor = true},
|
|
||||||
incompatible = {fire_protection = true, protection = true, projectile_protection = true},
|
|
||||||
weight = 2,
|
|
||||||
description = S("Reduces explosion damage and knockback."),
|
|
||||||
curse = false,
|
|
||||||
on_enchant = function() end,
|
|
||||||
requires_tool = false,
|
|
||||||
treasure = false,
|
|
||||||
power_range_table = {{5, 13}, {13, 21}, {21, 29}, {29, 37}},
|
|
||||||
inv_combat_tab = true,
|
|
||||||
inv_tool_tab = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- requires missing MineClone2 feature
|
-- requires missing MineClone2 feature
|
||||||
--[[mcl_enchanting.enchantments.channeling = {
|
--[[mcl_enchanting.enchantments.channeling = {
|
||||||
name = S("Channeling"),
|
name = S("Channeling"),
|
||||||
|
@ -86,26 +67,6 @@ mcl_enchanting.enchantments.blast_protection = {
|
||||||
inv_tool_tab = false,
|
inv_tool_tab = false,
|
||||||
}]]--
|
}]]--
|
||||||
|
|
||||||
-- implemented in mcl_armor
|
|
||||||
mcl_enchanting.enchantments.curse_of_binding = {
|
|
||||||
name = S("Curse of Binding"),
|
|
||||||
max_level = 1,
|
|
||||||
primary = {},
|
|
||||||
secondary = {armor_head = true, armor_torso = true, armor_legs = true, armor_feet = true},
|
|
||||||
disallow = {},
|
|
||||||
incompatible = {},
|
|
||||||
weight = 1,
|
|
||||||
description = S("Item cannot be removed from armor slots except due to death, breaking or in Creative Mode."),
|
|
||||||
curse = true,
|
|
||||||
on_enchant = function() end,
|
|
||||||
requires_tool = false,
|
|
||||||
treasure = true,
|
|
||||||
power_range_table = {{25, 50}},
|
|
||||||
inv_combat_tab = true,
|
|
||||||
inv_tool_tab = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- implemented in mcl_death_drop
|
|
||||||
mcl_enchanting.enchantments.curse_of_vanishing = {
|
mcl_enchanting.enchantments.curse_of_vanishing = {
|
||||||
name = S("Curse of Vanishing"),
|
name = S("Curse of Vanishing"),
|
||||||
max_level = 1,
|
max_level = 1,
|
||||||
|
@ -124,6 +85,14 @@ mcl_enchanting.enchantments.curse_of_vanishing = {
|
||||||
inv_tool_tab = true,
|
inv_tool_tab = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
MCLPlayer:__override("death_drop", function(self, inventory, listname, index, stack)
|
||||||
|
if mcl_enchanting.has_enchantment(stack, "curse_of_vanishing") then
|
||||||
|
stack = nil
|
||||||
|
end
|
||||||
|
|
||||||
|
return self, inventory, listname, index, stack
|
||||||
|
end)
|
||||||
|
|
||||||
-- implemented in mcl_playerplus
|
-- implemented in mcl_playerplus
|
||||||
mcl_enchanting.enchantments.depth_strider = {
|
mcl_enchanting.enchantments.depth_strider = {
|
||||||
name = S("Depth Strider"),
|
name = S("Depth Strider"),
|
||||||
|
@ -172,24 +141,6 @@ mcl_enchanting.enchantments.efficiency = {
|
||||||
inv_tool_tab = true,
|
inv_tool_tab = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- implemented in mcl_armor
|
|
||||||
mcl_enchanting.enchantments.feather_falling = {
|
|
||||||
name = S("Feather Falling"),
|
|
||||||
max_level = 4,
|
|
||||||
primary = {armor_feet = true},
|
|
||||||
secondary = {},
|
|
||||||
disallow = {non_combat_armor = true},
|
|
||||||
incompatible = {},
|
|
||||||
weight = 5,
|
|
||||||
description = S("Reduces fall damage."),curse = false,
|
|
||||||
on_enchant = function() end,
|
|
||||||
requires_tool = false,
|
|
||||||
treasure = false,
|
|
||||||
power_range_table = {{5, 11}, {11, 17}, {17, 23}, {23, 29}},
|
|
||||||
inv_combat_tab = true,
|
|
||||||
inv_tool_tab = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- implemented in mcl_mobs and via register_on_punchplayer callback
|
-- implemented in mcl_mobs and via register_on_punchplayer callback
|
||||||
mcl_enchanting.enchantments.fire_aspect = {
|
mcl_enchanting.enchantments.fire_aspect = {
|
||||||
name = S("Fire Aspect"),
|
name = S("Fire Aspect"),
|
||||||
|
@ -221,25 +172,6 @@ minetest.register_on_punchplayer(function(player, hitter, time_from_last_punch,
|
||||||
end
|
end
|
||||||
end)
|
end)
|
||||||
|
|
||||||
-- implemented in mcl_armor
|
|
||||||
mcl_enchanting.enchantments.fire_protection = {
|
|
||||||
name = S("Fire Protection"),
|
|
||||||
max_level = 4,
|
|
||||||
primary = {armor_head = true, armor_torso = true, armor_legs = true, armor_feet = true},
|
|
||||||
secondary = {},
|
|
||||||
disallow = {non_combat_armor = true},
|
|
||||||
incompatible = {blast_protection = true, protection = true, projectile_protection = true},
|
|
||||||
weight = 5,
|
|
||||||
description = S("Reduces fire damage."),
|
|
||||||
curse = false,
|
|
||||||
on_enchant = function() end,
|
|
||||||
requires_tool = false,
|
|
||||||
treasure = false,
|
|
||||||
power_range_table = {{10, 18}, {18, 26}, {26, 34}, {34, 42}},
|
|
||||||
inv_combat_tab = true,
|
|
||||||
inv_tool_tab = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
mcl_enchanting.enchantments.flame = {
|
mcl_enchanting.enchantments.flame = {
|
||||||
name = S("Flame"),
|
name = S("Flame"),
|
||||||
max_level = 1,
|
max_level = 1,
|
||||||
|
@ -277,42 +209,6 @@ mcl_enchanting.enchantments.fortune = {
|
||||||
inv_tool_tab = true,
|
inv_tool_tab = true,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- implemented via walkover.register_global
|
|
||||||
mcl_enchanting.enchantments.frost_walker = {
|
|
||||||
name = S("Frost Walker"),
|
|
||||||
max_level = 2,
|
|
||||||
primary = {},
|
|
||||||
secondary = {armor_feet = true},
|
|
||||||
disallow = {non_combat_armor = true},
|
|
||||||
incompatible = {depth_strider = true},
|
|
||||||
weight = 2,
|
|
||||||
description = S("Turns water beneath the player into frosted ice and prevents the damage from magma blocks."),
|
|
||||||
curse = false,
|
|
||||||
on_enchant = function() end,
|
|
||||||
requires_tool = false,
|
|
||||||
treasure = true,
|
|
||||||
power_range_table = {{10, 25}, {20, 35}},
|
|
||||||
inv_combat_tab = true,
|
|
||||||
inv_tool_tab = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
walkover.register_global(function(pos, _, player)
|
|
||||||
local boots = player:get_inventory():get_stack("armor", 5)
|
|
||||||
local frost_walker = mcl_enchanting.get_enchantment(boots, "frost_walker")
|
|
||||||
if frost_walker <= 0 then
|
|
||||||
return
|
|
||||||
end
|
|
||||||
local radius = frost_walker + 2
|
|
||||||
local minp = {x = pos.x - radius, y = pos.y, z = pos.z - radius}
|
|
||||||
local maxp = {x = pos.x + radius, y = pos.y, z = pos.z + radius}
|
|
||||||
local positions = minetest.find_nodes_in_area_under_air(minp, maxp, "mcl_core:water_source")
|
|
||||||
for _, p in ipairs(positions) do
|
|
||||||
if vector.distance(pos, p) <= radius then
|
|
||||||
minetest.set_node(p, {name = "mcl_core:frosted_ice_0"})
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end)
|
|
||||||
|
|
||||||
-- requires missing MineClone2 feature
|
-- requires missing MineClone2 feature
|
||||||
--[[mcl_enchanting.enchantments.impaling = {
|
--[[mcl_enchanting.enchantments.impaling = {
|
||||||
name = S("Impaling"),
|
name = S("Impaling"),
|
||||||
|
@ -538,44 +434,6 @@ mcl_enchanting.enchantments.power = {
|
||||||
inv_tool_tab = false,
|
inv_tool_tab = false,
|
||||||
}
|
}
|
||||||
|
|
||||||
-- implemented in mcl_armor
|
|
||||||
mcl_enchanting.enchantments.projectile_protection = {
|
|
||||||
name = S("Projectile Protection"),
|
|
||||||
max_level = 4,
|
|
||||||
primary = {armor_head = true, armor_torso = true, armor_legs = true, armor_feet = true},
|
|
||||||
secondary = {},
|
|
||||||
disallow = {non_combat_armor = true},
|
|
||||||
incompatible = {blast_protection = true, fire_protection = true, protection = true},
|
|
||||||
weight = 5,
|
|
||||||
description = S("Reduces projectile damage."),
|
|
||||||
curse = false,
|
|
||||||
on_enchant = function() end,
|
|
||||||
requires_tool = false,
|
|
||||||
treasure = false,
|
|
||||||
power_range_table = {{1, 16}, {11, 26}, {21, 36}, {31, 46}, {41, 56}},
|
|
||||||
inv_combat_tab = true,
|
|
||||||
inv_tool_tab = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- implemented in mcl_armor
|
|
||||||
mcl_enchanting.enchantments.protection = {
|
|
||||||
name = S("Protection"),
|
|
||||||
max_level = 4,
|
|
||||||
primary = {armor_head = true, armor_torso = true, armor_legs = true, armor_feet = true},
|
|
||||||
secondary = {},
|
|
||||||
disallow = {non_combat_armor = true},
|
|
||||||
incompatible = {blast_protection = true, fire_protection = true, projectile_protection = true},
|
|
||||||
weight = 10,
|
|
||||||
description = S("Reduces most types of damage by 4% for each level."),
|
|
||||||
curse = false,
|
|
||||||
on_enchant = function() end,
|
|
||||||
requires_tool = false,
|
|
||||||
treasure = false,
|
|
||||||
power_range_table = {{1, 12}, {12, 23}, {23, 34}, {34, 45}},
|
|
||||||
inv_combat_tab = true,
|
|
||||||
inv_tool_tab = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- implemented via minetest.calculate_knockback (together with the Knockback enchantment) and mcl_bows
|
-- implemented via minetest.calculate_knockback (together with the Knockback enchantment) and mcl_bows
|
||||||
mcl_enchanting.enchantments.punch = {
|
mcl_enchanting.enchantments.punch = {
|
||||||
name = S("Punch"),
|
name = S("Punch"),
|
||||||
|
@ -747,25 +605,6 @@ mcl_enchanting.enchantments.soul_speed = {
|
||||||
inv_tool_tab = false,
|
inv_tool_tab = false,
|
||||||
}]]--
|
}]]--
|
||||||
|
|
||||||
-- implemented in mcl_armor
|
|
||||||
mcl_enchanting.enchantments.thorns = {
|
|
||||||
name = S("Thorns"),
|
|
||||||
max_level = 3,
|
|
||||||
primary = {armor_head = true},
|
|
||||||
secondary = {armor_torso = true, armor_legs = true, armor_feet = true},
|
|
||||||
disallow = {non_combat_armor = true},
|
|
||||||
incompatible = {},
|
|
||||||
weight = 1,
|
|
||||||
description = S("Reflects some of the damage taken when hit, at the cost of reducing durability with each proc."),
|
|
||||||
curse = false,
|
|
||||||
on_enchant = function() end,
|
|
||||||
requires_tool = false,
|
|
||||||
treasure = false,
|
|
||||||
power_range_table = {{10, 61}, {30, 71}, {50, 81}},
|
|
||||||
inv_combat_tab = true,
|
|
||||||
inv_tool_tab = false,
|
|
||||||
}
|
|
||||||
|
|
||||||
-- for tools & weapons implemented via on_enchant; for bows implemented in mcl_bows; for armor implemented in mcl_armor and mcl_tt; for fishing rods implemented in mcl_fishing
|
-- for tools & weapons implemented via on_enchant; for bows implemented in mcl_bows; for armor implemented in mcl_armor and mcl_tt; for fishing rods implemented in mcl_fishing
|
||||||
mcl_enchanting.enchantments.unbreaking = {
|
mcl_enchanting.enchantments.unbreaking = {
|
||||||
name = S("Unbreaking"),
|
name = S("Unbreaking"),
|
||||||
|
|
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
Before Width: | Height: | Size: 850 B After Width: | Height: | Size: 850 B |
Before Width: | Height: | Size: 313 B After Width: | Height: | Size: 313 B |
Loading…
Reference in New Issue