From 018fc64a2707b3a142a1f2aec9cb1f56ad3c0bfc Mon Sep 17 00:00:00 2001 From: Elias Fleckenstein Date: Wed, 17 Mar 2021 11:08:14 +0100 Subject: [PATCH] Entity / Player abstraction (WIP) --- mods/CORE/class/init.lua | 172 +++++++++++++++--- mods/CORE/mcl_armor/init.lua | 118 ++++++++++++ mods/{ENTITIES => CORE}/mcl_burning/api.lua | 0 mods/{ENTITIES => CORE}/mcl_burning/init.lua | 0 mods/{ENTITIES => CORE}/mcl_burning/mod.conf | 0 mods/CORE/mcl_damage_source/init.lua | 24 +++ mods/CORE/mcl_entity/init.lua | 62 +++++++ mods/CORE/mcl_equipment/init.lua | 103 +++++++++++ mods/CORE/mcl_gamerules/init.lua | 66 +++++++ mods/CORE/mcl_itemstack/init.lua | 31 ++++ mods/CORE/mcl_metadata/init.lua | 25 +++ mods/CORE/mcl_mob/init.lua | 18 ++ mods/CORE/mcl_object/init.lua | 89 +++++++++ mods/CORE/mcl_object_mgr/init.lua | 112 ++++++++++++ mods/CORE/mcl_player/init.lua | 15 ++ mods/ITEMS/mcl_chests/init.lua | 6 + mods/ITEMS/mcl_enchanting/enchantments.lua | 9 +- .../README.txt | 0 .../game_api.txt | 0 .../{mcl_player => mcl_player_model}/init.lua | 0 .../{mcl_player => mcl_player_model}/mod.conf | 0 .../models/character.b3d | Bin .../models/character.blend | Bin .../models/character.png | Bin .../textures/player.png | Bin .../textures/player_back.png | Bin 26 files changed, 820 insertions(+), 30 deletions(-) create mode 100644 mods/CORE/mcl_armor/init.lua rename mods/{ENTITIES => CORE}/mcl_burning/api.lua (100%) rename mods/{ENTITIES => CORE}/mcl_burning/init.lua (100%) rename mods/{ENTITIES => CORE}/mcl_burning/mod.conf (100%) create mode 100644 mods/CORE/mcl_damage_source/init.lua create mode 100644 mods/CORE/mcl_entity/init.lua create mode 100644 mods/CORE/mcl_equipment/init.lua create mode 100644 mods/CORE/mcl_gamerules/init.lua create mode 100644 mods/CORE/mcl_itemstack/init.lua create mode 100644 mods/CORE/mcl_metadata/init.lua create mode 100644 mods/CORE/mcl_mob/init.lua create mode 100644 mods/CORE/mcl_object/init.lua create mode 100644 mods/CORE/mcl_object_mgr/init.lua create mode 100644 mods/CORE/mcl_player/init.lua rename mods/PLAYER/{mcl_player => mcl_player_model}/README.txt (100%) rename mods/PLAYER/{mcl_player => mcl_player_model}/game_api.txt (100%) rename mods/PLAYER/{mcl_player => mcl_player_model}/init.lua (100%) rename mods/PLAYER/{mcl_player => mcl_player_model}/mod.conf (100%) rename mods/PLAYER/{mcl_player => mcl_player_model}/models/character.b3d (100%) rename mods/PLAYER/{mcl_player => mcl_player_model}/models/character.blend (100%) rename mods/PLAYER/{mcl_player => mcl_player_model}/models/character.png (100%) rename mods/PLAYER/{mcl_player => mcl_player_model}/textures/player.png (100%) rename mods/PLAYER/{mcl_player => mcl_player_model}/textures/player_back.png (100%) diff --git a/mods/CORE/class/init.lua b/mods/CORE/class/init.lua index 907342374..8903546da 100644 --- a/mods/CORE/class/init.lua +++ b/mods/CORE/class/init.lua @@ -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 +} + diff --git a/mods/CORE/mcl_armor/init.lua b/mods/CORE/mcl_armor/init.lua new file mode 100644 index 000000000..e332fcda3 --- /dev/null +++ b/mods/CORE/mcl_armor/init.lua @@ -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 ) + 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 diff --git a/mods/ENTITIES/mcl_burning/api.lua b/mods/CORE/mcl_burning/api.lua similarity index 100% rename from mods/ENTITIES/mcl_burning/api.lua rename to mods/CORE/mcl_burning/api.lua diff --git a/mods/ENTITIES/mcl_burning/init.lua b/mods/CORE/mcl_burning/init.lua similarity index 100% rename from mods/ENTITIES/mcl_burning/init.lua rename to mods/CORE/mcl_burning/init.lua diff --git a/mods/ENTITIES/mcl_burning/mod.conf b/mods/CORE/mcl_burning/mod.conf similarity index 100% rename from mods/ENTITIES/mcl_burning/mod.conf rename to mods/CORE/mcl_burning/mod.conf diff --git a/mods/CORE/mcl_damage_source/init.lua b/mods/CORE/mcl_damage_source/init.lua new file mode 100644 index 000000000..c0fbc9dfe --- /dev/null +++ b/mods/CORE/mcl_damage_source/init.lua @@ -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) diff --git a/mods/CORE/mcl_entity/init.lua b/mods/CORE/mcl_entity/init.lua new file mode 100644 index 000000000..0c74f8fba --- /dev/null +++ b/mods/CORE/mcl_entity/init.lua @@ -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 diff --git a/mods/CORE/mcl_equipment/init.lua b/mods/CORE/mcl_equipment/init.lua new file mode 100644 index 000000000..03e1cd808 --- /dev/null +++ b/mods/CORE/mcl_equipment/init.lua @@ -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 + diff --git a/mods/CORE/mcl_gamerules/init.lua b/mods/CORE/mcl_gamerules/init.lua new file mode 100644 index 000000000..832f1a4c2 --- /dev/null +++ b/mods/CORE/mcl_gamerules/init.lua @@ -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) diff --git a/mods/CORE/mcl_itemstack/init.lua b/mods/CORE/mcl_itemstack/init.lua new file mode 100644 index 000000000..38111d174 --- /dev/null +++ b/mods/CORE/mcl_itemstack/init.lua @@ -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 diff --git a/mods/CORE/mcl_metadata/init.lua b/mods/CORE/mcl_metadata/init.lua new file mode 100644 index 000000000..ee614e766 --- /dev/null +++ b/mods/CORE/mcl_metadata/init.lua @@ -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 diff --git a/mods/CORE/mcl_mob/init.lua b/mods/CORE/mcl_mob/init.lua new file mode 100644 index 000000000..169db4c9f --- /dev/null +++ b/mods/CORE/mcl_mob/init.lua @@ -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 diff --git a/mods/CORE/mcl_object/init.lua b/mods/CORE/mcl_object/init.lua new file mode 100644 index 000000000..9666a436b --- /dev/null +++ b/mods/CORE/mcl_object/init.lua @@ -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 diff --git a/mods/CORE/mcl_object_mgr/init.lua b/mods/CORE/mcl_object_mgr/init.lua new file mode 100644 index 000000000..623ac341e --- /dev/null +++ b/mods/CORE/mcl_object_mgr/init.lua @@ -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)) diff --git a/mods/CORE/mcl_player/init.lua b/mods/CORE/mcl_player/init.lua new file mode 100644 index 000000000..db93933cd --- /dev/null +++ b/mods/CORE/mcl_player/init.lua @@ -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) diff --git a/mods/ITEMS/mcl_chests/init.lua b/mods/ITEMS/mcl_chests/init.lua index 852a47fec..ec65ad120 100644 --- a/mods/ITEMS/mcl_chests/init.lua +++ b/mods/ITEMS/mcl_chests/init.lua @@ -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 = { diff --git a/mods/ITEMS/mcl_enchanting/enchantments.lua b/mods/ITEMS/mcl_enchanting/enchantments.lua index 893ce58d4..2f26b8eac 100644 --- a/mods/ITEMS/mcl_enchanting/enchantments.lua +++ b/mods/ITEMS/mcl_enchanting/enchantments.lua @@ -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"), diff --git a/mods/PLAYER/mcl_player/README.txt b/mods/PLAYER/mcl_player_model/README.txt similarity index 100% rename from mods/PLAYER/mcl_player/README.txt rename to mods/PLAYER/mcl_player_model/README.txt diff --git a/mods/PLAYER/mcl_player/game_api.txt b/mods/PLAYER/mcl_player_model/game_api.txt similarity index 100% rename from mods/PLAYER/mcl_player/game_api.txt rename to mods/PLAYER/mcl_player_model/game_api.txt diff --git a/mods/PLAYER/mcl_player/init.lua b/mods/PLAYER/mcl_player_model/init.lua similarity index 100% rename from mods/PLAYER/mcl_player/init.lua rename to mods/PLAYER/mcl_player_model/init.lua diff --git a/mods/PLAYER/mcl_player/mod.conf b/mods/PLAYER/mcl_player_model/mod.conf similarity index 100% rename from mods/PLAYER/mcl_player/mod.conf rename to mods/PLAYER/mcl_player_model/mod.conf diff --git a/mods/PLAYER/mcl_player/models/character.b3d b/mods/PLAYER/mcl_player_model/models/character.b3d similarity index 100% rename from mods/PLAYER/mcl_player/models/character.b3d rename to mods/PLAYER/mcl_player_model/models/character.b3d diff --git a/mods/PLAYER/mcl_player/models/character.blend b/mods/PLAYER/mcl_player_model/models/character.blend similarity index 100% rename from mods/PLAYER/mcl_player/models/character.blend rename to mods/PLAYER/mcl_player_model/models/character.blend diff --git a/mods/PLAYER/mcl_player/models/character.png b/mods/PLAYER/mcl_player_model/models/character.png similarity index 100% rename from mods/PLAYER/mcl_player/models/character.png rename to mods/PLAYER/mcl_player_model/models/character.png diff --git a/mods/PLAYER/mcl_player/textures/player.png b/mods/PLAYER/mcl_player_model/textures/player.png similarity index 100% rename from mods/PLAYER/mcl_player/textures/player.png rename to mods/PLAYER/mcl_player_model/textures/player.png diff --git a/mods/PLAYER/mcl_player/textures/player_back.png b/mods/PLAYER/mcl_player_model/textures/player_back.png similarity index 100% rename from mods/PLAYER/mcl_player/textures/player_back.png rename to mods/PLAYER/mcl_player_model/textures/player_back.png