forked from VoxeLibre/VoxeLibre
Entity / Player abstraction (WIP)
This commit is contained in:
parent
b53329b452
commit
018fc64a27
|
@ -1,45 +1,159 @@
|
|||
local Object = {}
|
||||
|
||||
function Object:__define_getter(name, cached, get, cmp)
|
||||
-- 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
|
||||
self[name] = function (self, expected)
|
||||
local value
|
||||
-- add a function to the class
|
||||
self[name] = function(self)
|
||||
-- check if the value is present in the cache
|
||||
local value = self[key]
|
||||
|
||||
if cached then
|
||||
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
|
||||
|
||||
if not value then
|
||||
value = get(self)
|
||||
end
|
||||
-- store result in cache
|
||||
self[key] = value
|
||||
|
||||
if cached then
|
||||
self[key] = value
|
||||
end
|
||||
-- return result
|
||||
return value
|
||||
end
|
||||
end
|
||||
|
||||
if expected ~= nil then
|
||||
if cmp then
|
||||
return cmp(value, expected)
|
||||
-- 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 value == expected
|
||||
-- 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
|
||||
return value
|
||||
-- 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
|
||||
|
||||
function class(super)
|
||||
return setmetatable({}, {
|
||||
__call = function(_class, ...)
|
||||
local instance = setmetatable({}, {
|
||||
__index = _class,
|
||||
})
|
||||
if instance.constructor then
|
||||
instance:constructor(...)
|
||||
end
|
||||
return instance
|
||||
end,
|
||||
__index = super or Object,
|
||||
})
|
||||
-- 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,118 @@
|
|||
local old_damage_handler = MCLObject.handle_damage
|
||||
|
||||
function MCLObject:handle_damage(hp, source)
|
||||
local hp_old = old_damage_handler(hp, source)
|
||||
if hp_old then
|
||||
return hp_old
|
||||
end
|
||||
|
||||
if source.bypasses_armor and source.bypasses_magic then
|
||||
return
|
||||
end
|
||||
|
||||
local heal_max = 0
|
||||
local items = 0
|
||||
local armor_damage = math.max(1, math.floor(math.abs(hp) / 4))
|
||||
|
||||
local total_points = 0
|
||||
local total_toughness = 0
|
||||
local epf = 0
|
||||
local thorns_damage = 0
|
||||
local thorns_damage_regular = 0
|
||||
|
||||
for location, stack in pairs(self:equipment():get_armor()) do
|
||||
if stack:get_count() > 0 then
|
||||
local enchantments = mcl_enchanting.get_enchantments(stack)
|
||||
local pts = stack:get_definition().groups["mcl_armor_points"] or 0
|
||||
local tough = stack:get_definition().groups["mcl_armor_toughness"] or 0
|
||||
total_points = total_points + pts
|
||||
total_toughness = total_toughness + tough
|
||||
local protection_level = enchantments.protection or 0
|
||||
if protection_level > 0 then
|
||||
epf = epf + protection_level * 1
|
||||
end
|
||||
local blast_protection_level = enchantments.blast_protection or 0
|
||||
if blast_protection_level > 0 and damage_type == "explosion" then
|
||||
epf = epf + blast_protection_level * 2
|
||||
end
|
||||
local fire_protection_level = enchantments.fire_protection or 0
|
||||
if fire_protection_level > 0 and (damage_type == "burning" or damage_type == "fireball" or reason.type == "node_damage" and
|
||||
(reason.node == "mcl_fire:fire" or reason.node == "mcl_core:lava_source" or reason.node == "mcl_core:lava_flowing")) then
|
||||
epf = epf + fire_protection_level * 2
|
||||
end
|
||||
local projectile_protection_level = enchantments.projectile_protection or 0
|
||||
if projectile_protection_level and (damage_type == "projectile" or damage_type == "fireball") then
|
||||
epf = epf + projectile_protection_level * 2
|
||||
end
|
||||
local feather_falling_level = enchantments.feather_falling or 0
|
||||
if feather_falling_level and reason.type == "fall" then
|
||||
epf = epf + feather_falling_level * 3
|
||||
end
|
||||
|
||||
local did_thorns_damage = false
|
||||
local thorns_level = enchantments.thorns or 0
|
||||
if thorns_level then
|
||||
if thorns_level > 10 then
|
||||
thorns_damage = thorns_damage + thorns_level - 10
|
||||
did_thorns_damage = true
|
||||
elseif thorns_damage_regular < 4 and thorns_level * 0.15 > math.random() then
|
||||
local thorns_damage_regular_new = math.min(4, thorns_damage_regular + math.random(4))
|
||||
thorns_damage = thorns_damage + thorns_damage_regular_new - thorns_damage_regular
|
||||
thorns_damage_regular = thorns_damage_regular_new
|
||||
did_thorns_damage = true
|
||||
end
|
||||
end
|
||||
|
||||
-- Damage armor
|
||||
local use = stack:get_definition().groups["mcl_armor_uses"] or 0
|
||||
if use > 0 and regular_reduction then
|
||||
local unbreaking_level = enchantments.unbreaking or 0
|
||||
if unbreaking_level > 0 then
|
||||
use = use / (0.6 + 0.4 / (unbreaking_level + 1))
|
||||
end
|
||||
local wear = armor_damage * math.floor(65536/use)
|
||||
if did_thorns_damage then
|
||||
wear = wear * 3
|
||||
end
|
||||
stack:add_wear(wear)
|
||||
end
|
||||
|
||||
local item = stack:get_name()
|
||||
armor_inv:set_stack("armor", i, stack)
|
||||
player_inv:set_stack("armor", i, stack)
|
||||
items = items + 1
|
||||
if stack:get_count() == 0 then
|
||||
armor:set_player_armor(player)
|
||||
armor:update_inventory(player)
|
||||
end
|
||||
end
|
||||
end
|
||||
local damage = math.abs(hp_change)
|
||||
|
||||
if regular_reduction then
|
||||
-- Damage calculation formula (from <https://minecraft.gamepedia.com/Armor#Damage_protection>)
|
||||
damage = damage * (1 - math.min(20, math.max((total_points/5), total_points - damage / (2+(total_toughness/4)))) / 25)
|
||||
end
|
||||
damage = damage * (1 - (math.min(20, epf) / 25))
|
||||
damage = math.floor(damage+0.5)
|
||||
|
||||
if reason.type == "punch" and thorns_damage > 0 then
|
||||
local obj = reason.object
|
||||
if obj then
|
||||
local luaentity = obj:get_luaentity()
|
||||
if luaentity then
|
||||
local shooter = obj._shooter
|
||||
if shooter then
|
||||
obj = shooter
|
||||
end
|
||||
end
|
||||
obj:punch(player, 1.0, {
|
||||
full_punch_interval=1.0,
|
||||
damage_groups = {fleshy = thorns_damage},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
hp_change = -math.abs(damage)
|
||||
return hp
|
||||
end
|
|
@ -0,0 +1,24 @@
|
|||
MCLDamageSource = class()
|
||||
|
||||
function MCLDamageSource:constructor(tbl, hitter)
|
||||
for k, v in pairs(tbl or {}) do
|
||||
self[k] = v
|
||||
end
|
||||
self.hitter = hitter
|
||||
end
|
||||
|
||||
MCLDamageSource:__getter("direct_object", function(self)
|
||||
local hitter = self.hitter
|
||||
if not hitter then
|
||||
return
|
||||
end
|
||||
return mcl_object_mgr.get(hitter)
|
||||
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,62 @@
|
|||
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
|
||||
|
||||
function MCLEntity:calculate_knockback(...)
|
||||
return minetest.calculate_knockback(self.object, ...)
|
||||
end
|
||||
|
||||
function MCLEntity:on_punch(...)
|
||||
hp = MCLObject.on_punch(self, ...)
|
||||
|
||||
self.damage_info.info.knockback = self:calculate_knockback(...)
|
||||
end
|
||||
|
||||
function MCLEntity:on_damage(hp_change, source, info)
|
||||
MCLObject.on_damage(self, hp_change, source, info)
|
||||
|
||||
if info.knockback then
|
||||
self:add_velocity(info.knockback)
|
||||
end
|
||||
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 = {"head", "chest", "legs", "feet"}
|
||||
|
||||
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,31 @@
|
|||
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]
|
||||
end
|
||||
|
||||
function MCLItemStack:has_enchantment(name)
|
||||
return self:get_enchantment(name) > 0
|
||||
end
|
||||
|
||||
function MCLItemStack:durability()
|
||||
local def = self.stack:get_definition()
|
||||
if def then
|
||||
local base_uses = def._mcl_uses
|
||||
|
||||
end
|
||||
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(hp_change, source, info)
|
||||
MCLEntity.on_damage(self, hp_change, source, info)
|
||||
|
||||
local new_hp = self:get_hp()
|
||||
if new_hp <= 0 and new_hp + hp_change > 0 then
|
||||
self:on_death(source)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,89 @@
|
|||
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, hp)
|
||||
local source = MCLDamageSource():punch(nil, hitter)
|
||||
|
||||
hp = self:damage_modifier(hp, source) or hp
|
||||
|
||||
self.damage_info = {
|
||||
hp = hp,
|
||||
source = source,
|
||||
info = {
|
||||
tool_capabilities = tool_capabilities,
|
||||
},
|
||||
}
|
||||
|
||||
return hp
|
||||
end
|
||||
|
||||
-- use this function to deal regular damage to an object (do NOT use :punch() unless toolcaps need to be handled)
|
||||
function MCLObject:damage(hp, source)
|
||||
hp = self:damage_modifier(hp, source) or hp
|
||||
self:set_hp(self:get_hp() - hp)
|
||||
|
||||
self.damage_info = {
|
||||
hp = hp,
|
||||
source = source,
|
||||
}
|
||||
|
||||
return hp
|
||||
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:add_velocity(vel)
|
||||
self.object:add_velocity(vel)
|
||||
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(hp, source)
|
||||
if self.invulnerable and not source.bypasses_invulnerability then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
function MCLObject:on_damage(hp_change, source, info)
|
||||
end
|
||||
|
||||
function MCLObject:on_step()
|
||||
local damage_info = self.damage_info
|
||||
if damage_info then
|
||||
self.damage_info = nil
|
||||
self:on_damage(damage_info.hp, damage_info.source, damage_info.info)
|
||||
end
|
||||
end
|
|
@ -0,0 +1,112 @@
|
|||
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_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))
|
|
@ -0,0 +1,15 @@
|
|||
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)
|
|
@ -1055,6 +1055,12 @@ minetest.register_on_joinplayer(function(player)
|
|||
inv:set_size("enderchest", 9*3)
|
||||
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({
|
||||
output = 'mcl_chests:ender_chest',
|
||||
recipe = {
|
||||
|
|
|
@ -105,7 +105,6 @@ mcl_enchanting.enchantments.curse_of_binding = {
|
|||
inv_tool_tab = false,
|
||||
}
|
||||
|
||||
-- implemented in mcl_death_drop
|
||||
mcl_enchanting.enchantments.curse_of_vanishing = {
|
||||
name = S("Curse of Vanishing"),
|
||||
max_level = 1,
|
||||
|
@ -124,6 +123,14 @@ mcl_enchanting.enchantments.curse_of_vanishing = {
|
|||
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
|
||||
mcl_enchanting.enchantments.depth_strider = {
|
||||
name = S("Depth Strider"),
|
||||
|
|
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