Compare commits

..

14 Commits

212 changed files with 2915 additions and 1675 deletions

4
API.md
View File

@ -17,10 +17,6 @@ Items can have these fields:
anvil.
See `mcl_banners` for an example.
Tools can have these fields:
* `_mcl_diggroups`: Specifies the digging groups that a tool can dig and how
efficiently. See `_mcl_autogroup` for more information.
All nodes can have these fields:
* `_mcl_hardness`: Hardness of the block, ranges from 0 to infinity (represented by -1). Determines digging times. Default: 0

View File

@ -33,10 +33,10 @@ mgvalleys_spflags = noaltitude_chill,noaltitude_dry,nohumid_rivers,vary_river_de
keepInventory = false
# Performance settings
# dedicated_server_step = 0.001
# abm_interval = 0.25
# max_objects_per_block = 4096
# max_packets_per_iteration = 10096
dedicated_server_step = 0.001
abm_interval = 0.25
max_objects_per_block = 4096
max_packets_per_iteration = 10096
# Clientmodding to support official client
enable_client_modding = true

View File

@ -4,11 +4,6 @@ Specifically, this mod has 2 purposes:
1) Automatically adding the group “solid” for blocks considered “solid” in Minecraft.
2) Generating digging time group for all nodes based on node metadata (it's complicated)
This mod also requires another mod called “mcl_autogroup” to function properly.
“mcl_autogroup” exposes the API used to register digging groups, while this mod
uses those digging groups to set the digging time groups for all the nodes and
tools.
See init.lua for more infos.
The leading underscore in the name “_mcl_autogroup” was added to force Minetest to load this mod as late as possible.

View File

@ -1,358 +1,169 @@
--[[
This mod implements a HACK to make 100% sure the digging times of all tools
match Minecraft's perfectly. The digging times system of Minetest is very
different, so this weird group trickery has to be used. In Minecraft, each
block has a hardness and the actual Minecraft digging time is determined by
this:
--[[ Mining times. Yeah, mining times … Alright, this is going to be FUN!
This mod does include a HACK to make 100% sure the digging times of all tools match Minecraft's perfectly.
The digging times system of Minetest is very different, so this weird group trickery has to be used.
In Minecraft, each block has a hardness and the actual Minecraft digging time is determined by this:
1) The block's hardness
2) The tool being used (the tool speed and its efficiency level)
3) Whether the tool is considered as "eligible" for the block
2) The tool being used
3) Whether the tool is considered as eligible for the block
(e.g. only diamond pick eligible for obsidian)
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more information.
See Minecraft Wiki <http://minecraft.gamepedia.com/Minecraft_Wiki> for more
information.
In MineClone 2, all diggable node have the hardness set in the custom field _mcl_hardness (0 by default).
The nodes are also required to specify the eligible tools in groups like pickaxey, shovely, etc.
This mod then calculates the real digging time based on the node meta data. The real digging times
are then added into mcl_autogroup.digtimes where the table indices are group rating and the values are the
digging times in seconds. These digging times can be then added verbatim into the tool definitions.
How the mod is used
===================
Example:
mcl_autogroup.digtimes.pickaxey_dig_diamond[1] = 0.2
In MineClone 2, all diggable nodes have the hardness set in the custom field
"_mcl_hardness" (0 by default). These values are used together with digging
groups by this mod to create the correct digging times for nodes. Digging
groups are registered using the following code:
This means that when a node has been assigned the group pickaxey_dig_diamond=1, it can be dug by the
diamond pickaxe in 0.2 seconds.
mcl_autogroup.register_diggroup("shovely")
mcl_autogroup.register_diggroup("pickaxey", {
levels = { "wood", "gold", "stone", "iron", "diamond" }
})
The first line registers a simple digging group. The second line registers a
digging group with 5 different levels (in this case one for each material of a
pickaxes).
Nodes indicate that they belong to a particular digging group by being member of
the digging group in their node definition. "mcl_core:dirt" for example has
shovely=1 in its groups. If the digging group has multiple levels the value of
the group indicates which digging level the node requires.
"mcl_core:stone_with_gold" for example has pickaxey=4 because it requires a
pickaxe of level 4 be mined.
This strange setup with mcl_autogroup has been done to minimize the amount of required digging times
a single tool needs to use. If this is not being done, the loading time will increase considerably
(>10s).
For tools to be able to dig nodes of digging groups they need to use the have
the custom field "_mcl_diggroups" function to get the groupcaps. The value of
this field is a table which defines which groups the tool can dig and how
efficiently.
]]
_mcl_diggroups = {
handy = { speed = 1, level = 1, uses = 0 },
pickaxey = { speed = 1, level = 0, uses = 0 },
}
local materials = { "wood", "gold", "stone", "iron", "diamond" }
local basegroups = { "pickaxey", "axey", "shovely" }
local minigroups = { "handy", "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
local divisors = {
["wood"] = 2,
["gold"] = 12,
["stone"] = 4,
["iron"] = 6,
["diamond"] = 8,
["handy"] = 1,
["shearsy"] = 15,
["swordy"] = 1.5,
["shearsy_wool"] = 5,
["swordy_cobweb"] = 15,
}
local max_efficiency_level = 5
The "uses" field indicate how many uses (0 for infinite) a tool has when used on
the specified digging group. The "speed" field is a multiplier to the dig speed
on that digging group.
mcl_autogroup = {}
mcl_autogroup.digtimes = {}
mcl_autogroup.creativetimes = {} -- Copy of digtimes, except that all values are 0. Used for creative mode
The "level" field indicates which levels of the group the tool can harvest. A
level of 0 means that the tool cannot harvest blocks of that node. A level of 1
or above means that the tool can harvest nodes with that level or below. See
"mcl_tools/init.lua" for examples on how "_mcl_diggroups" is used in practice.
Information about the mod
=========================
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
mcl_autogroup contains the API functions used to register custom digging groups.
_mcl_autogroup contains most of the code. The leading underscore in the name
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
as possible. Minetest loads mods in reverse alphabetical order.
This also means that it is very important that no mod adds _mcl_autogroup as a
dependency.
--]]
assert(minetest.get_modpath("mcl_autogroup"), "This mod requires the mod mcl_autogroup to function")
-- Returns a table containing the unique "_mcl_hardness" for nodes belonging to
-- each diggroup.
local function get_hardness_values_for_groups()
local maps = {}
local values = {}
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
maps[g] = {}
values[g] = {}
end
for _, ndef in pairs(minetest.registered_nodes) do
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
if ndef.groups[g] ~= nil then
maps[g][ndef._mcl_hardness or 0] = true
end
for m=1, #materials do
for g=1, #basegroups do
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m]] = {}
mcl_autogroup.creativetimes[basegroups[g].."_dig_"..materials[m]] = {}
for e=1, max_efficiency_level do
mcl_autogroup.digtimes[basegroups[g].."_dig_"..materials[m].."_efficiency_"..e] = {}
end
end
for g, map in pairs(maps) do
for k, _ in pairs(map) do
table.insert(values[g], k)
end
end
for g, _ in pairs(mcl_autogroup.registered_diggroups) do
table.sort(values[g])
end
return values
end
-- Returns a table containing a table indexed by "_mcl_hardness_value" to get
-- its index in the list of unique hardnesses for each diggroup.
local function get_hardness_lookup_for_groups(hardness_values)
local map = {}
for g, values in pairs(hardness_values) do
map[g] = {}
for k, v in pairs(values) do
map[g][v] = k
end
for g=1, #minigroups do
mcl_autogroup.digtimes[minigroups[g].."_dig"] = {}
mcl_autogroup.creativetimes[minigroups[g].."_dig"] = {}
for e=1, max_efficiency_level do
mcl_autogroup.digtimes[minigroups[g].."_dig_efficiency_"..e] = {}
mcl_autogroup.creativetimes[minigroups[g].."_dig_efficiency_"..e] = {}
end
return map
end
-- Array of unique hardness values for each group which affects dig time.
local hardness_values = get_hardness_values_for_groups()
-- Map indexed by hardness values which return the index of that value in
-- hardness_value. Used for quick lookup.
local hardness_lookup = get_hardness_lookup_for_groups(hardness_values)
local function compute_creativetimes(group)
local creativetimes = {}
for index, hardness in pairs(hardness_values[group]) do
table.insert(creativetimes, 0)
end
return creativetimes
end
-- Get the list of digging times for using a specific tool on a specific
-- diggroup.
--
-- Parameters:
-- group - the group which it is digging
-- can_harvest - if the tool can harvest the block
-- speed - dig speed multiplier for tool (default 1)
-- efficiency - efficiency level for the tool if applicable
local function get_digtimes(group, can_harvest, speed, efficiency)
local speed = speed or 1
if efficiency then
speed = speed + efficiency * efficiency + 1
end
local digtimes = {}
for index, hardness in pairs(hardness_values[group]) do
local digtime = (hardness or 0) / speed
if can_harvest then
digtime = digtime * 1.5
else
digtime = digtime * 5
end
if digtime <= 0.05 then
digtime = 0
else
digtime = math.ceil(digtime * 20) / 20
end
table.insert(digtimes, digtime)
end
return digtimes
end
-- Get one groupcap field for using a specific tool on a specific group.
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
return {
times = get_digtimes(group, can_harvest, multiplier, efficiency),
uses = uses,
maxlevel = 0,
}
end
-- Add the groupcaps from a field in "_mcl_diggroups" to the groupcaps of a
-- tool.
local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
for g, capsdef in pairs(groupcaps_def) do
local mult = capsdef.speed or 1
local uses = capsdef.uses
local def = mcl_autogroup.registered_diggroups[g]
local max_level = def.levels and #def.levels or 1
assert(capsdef.level, toolname .. ' is missing level for ' .. g)
local level = math.min(capsdef.level, max_level)
if def.levels then
groupcaps[g .. "_dig_default"] = get_groupcap(g, false, mult, efficiency, uses)
if level > 0 then
groupcaps[g .. "_dig_" .. def.levels[level]] = get_groupcap(g, true, mult, efficiency, uses)
end
else
groupcaps[g .. "_dig"] = get_groupcap(g, level > 0, mult, efficiency, uses)
end
end
return groupcaps
end
-- Checks if the given node would drop its useful drop if dug by a given tool.
-- Returns true if it will yield its useful drop, false otherwise.
function mcl_autogroup.can_harvest(nodename, toolname)
local ndef = minetest.registered_nodes[nodename]
if minetest.get_item_group(nodename, "dig_immediate") >= 2 then
return true
end
-- Check if it can be dug by tool
local tdef = minetest.registered_tools[toolname]
if tdef and tdef._mcl_diggroups then
for g, gdef in pairs(tdef._mcl_diggroups) do
if ndef.groups[g] then
if ndef.groups[g] <= gdef.level then
return true
end
end
end
end
-- Check if it can be dug by hand
local tdef = minetest.registered_tools[""]
if tdef then
for g, gdef in pairs(tdef._mcl_diggroups) do
if ndef.groups[g] then
if ndef.groups[g] <= gdef.level then
return true
end
end
end
end
return false
end
-- Get one groupcap field for using a specific tool on a specific group.
local function get_groupcap(group, can_harvest, multiplier, efficiency, uses)
return {
times = get_digtimes(group, can_harvest, multiplier, efficiency),
uses = uses,
maxlevel = 0,
}
end
-- Returns the tool_capabilities from a tool definition or a default set of
-- tool_capabilities
local function get_tool_capabilities(tdef)
if tdef.tool_capabilities then
return tdef.tool_capabilities
end
-- If the damage group and punch interval from hand is not included,
-- then the user will not be able to attack with the tool.
local hand_toolcaps = minetest.registered_tools[""].tool_capabilities
return {
full_punch_interval = hand_toolcaps.full_punch_interval,
damage_groups = hand_toolcaps.damage_groups
}
end
-- Get the groupcaps for a tool. This function returns "groupcaps" table of
-- digging which should be put in the "tool_capabilities" of the tool definition
-- or in the metadata of an enchanted tool.
--
-- Parameters:
-- toolname - Name of the tool being enchanted (like "mcl_tools:diamond_pickaxe")
-- efficiency - The efficiency level the tool is enchanted with (default 0)
--
-- NOTE:
-- This function can only be called after mod initialization. Otherwise a mod
-- would have to add _mcl_autogroup as a dependency which would break the mod
-- loading order.
function mcl_autogroup.get_groupcaps(toolname, efficiency)
local tdef = minetest.registered_tools[toolname]
local groupcaps = table.copy(get_tool_capabilities(tdef).groupcaps or {})
add_groupcaps(toolname, groupcaps, tdef._mcl_diggroups, efficiency)
return groupcaps
end
-- Get the wear from using a tool on a digging group.
--
-- Parameters
-- toolname - Name of the tool used
-- diggroup - The name of the diggroup the tool is used on
--
-- NOTE:
-- This function can only be called after mod initialization. Otherwise a mod
-- would have to add _mcl_autogroup as a dependency which would break the mod
-- loading order.
function mcl_autogroup.get_wear(toolname, diggroup)
local tdef = minetest.registered_tools[toolname]
local uses = tdef._mcl_diggroups[diggroup].uses
return math.ceil(65535 / uses)
end
local overwrite = function()
for nname, ndef in pairs(minetest.registered_nodes) do
local groups_changed = false
local newgroups = table.copy(ndef.groups)
if (nname ~= "ignore" and ndef.diggable) then
-- Automatically assign the "solid" group for solid nodes
-- Automatically assign the “solid” group for solid nodes
if (ndef.walkable == nil or ndef.walkable == true)
and (ndef.collision_box == nil or ndef.collision_box.type == "regular")
and (ndef.node_box == nil or ndef.node_box.type == "regular")
and (ndef.groups.not_solid == 0 or ndef.groups.not_solid == nil) then
newgroups.solid = 1
groups_changed = true
end
-- Automatically assign the "opaque" group for opaque nodes
-- Automatically assign the “opaque” group for opaque nodes
if (not (ndef.paramtype == "light" or ndef.sunlight_propagates)) and
(ndef.groups.not_opaque == 0 or ndef.groups.not_opaque == nil) then
newgroups.opaque = 1
groups_changed = true
end
local creative_breakable = false
local function calculate_group(hardness, material, diggroup, newgroups, actual_rating, expected_rating, efficiency)
local time, validity_factor
if actual_rating >= expected_rating then
-- Valid tool
validity_factor = 1.5
else
-- Wrong tool (higher digging time)
validity_factor = 5
end
local speed_multiplier = divisors[material]
if efficiency then
speed_multiplier = speed_multiplier + efficiency * efficiency + 1
end
time = (hardness * validity_factor) / speed_multiplier
if time <= 0.05 then
time = 0
else
time = math.ceil(time * 20) / 20
end
table.insert(mcl_autogroup.digtimes[diggroup], time)
if not efficiency then
table.insert(mcl_autogroup.creativetimes[diggroup], 0)
end
newgroups[diggroup] = #mcl_autogroup.digtimes[diggroup]
return newgroups
end
-- Assign groups used for digging this node depending on
-- the registered digging groups
for g, gdef in pairs(mcl_autogroup.registered_diggroups) do
creative_breakable = true
local index = hardness_lookup[g][ndef._mcl_hardness or 0]
if ndef.groups[g] then
if gdef.levels then
newgroups[g .. "_dig_default"] = index
-- Hack in digging times
local hardness = ndef._mcl_hardness
if not hardness then
hardness = 0
end
for i = ndef.groups[g], #gdef.levels do
newgroups[g .. "_dig_" .. gdef.levels[i]] = index
-- Handle pickaxey, axey and shovely
for _, basegroup in pairs(basegroups) do
if (hardness ~= -1 and ndef.groups[basegroup]) then
for g=1,#materials do
local diggroup = basegroup.."_dig_"..materials[g]
newgroups = calculate_group(hardness, materials[g], diggroup, newgroups, g, ndef.groups[basegroup])
for e=1,max_efficiency_level do
newgroups = calculate_group(hardness, materials[g], diggroup .. "_efficiency_" .. e, newgroups, g, ndef.groups[basegroup], e)
end
else
newgroups[g .. "_dig"] = index
groups_changed = true
end
end
end
for m=1, #minigroups do
local minigroup = minigroups[m]
if hardness ~= -1 then
local diggroup = minigroup.."_dig"
-- actual rating
local ar = ndef.groups[minigroup]
if ar == nil then
ar = 0
end
if (minigroup == "handy")
or
(ndef.groups.shearsy_wool and minigroup == "shearsy_wool" and ndef.groups.wool)
or
(ndef.groups.swordy_cobweb and minigroup == "swordy_cobweb" and nname == "mcl_core:cobweb")
or
(ndef.groups[minigroup] and minigroup ~= "swordy_cobweb" and minigroup ~= "shearsy_wool") then
newgroups = calculate_group(hardness, minigroup, diggroup, newgroups, ar, 1)
for e=1,max_efficiency_level do
newgroups = calculate_group(hardness, minigroup, diggroup .. "_efficiency_" .. e, newgroups, ar, 1, e)
end
groups_changed = true
end
end
end
-- Automatically assign the node to the
-- creative_breakable group if it belongs to any digging
-- group.
newgroups["creative_breakable"] = 1
minetest.override_item(nname, {
groups = newgroups
})
end
end
for tname, tdef in pairs(minetest.registered_tools) do
-- Assign groupcaps for digging the registered digging groups
-- depending on the _mcl_diggroups in the tool definition
if tdef._mcl_diggroups then
local toolcaps = table.copy(get_tool_capabilities(tdef))
toolcaps.groupcaps = mcl_autogroup.get_groupcaps(tname)
minetest.override_item(tname, {
tool_capabilities = toolcaps
})
if groups_changed then
minetest.override_item(nname, {
groups = newgroups
})
end
end
end
end

View File

@ -1,3 +1,3 @@
name = _mcl_autogroup
author = ryvnf
author = Wuzzy
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.

159
mods/CORE/class/init.lua Normal file
View File

@ -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
}

3
mods/CORE/class/mod.conf Normal file
View File

@ -0,0 +1,3 @@
name = class
author = Fleckenstein
description = A class system for MineClone2

View File

@ -1,28 +0,0 @@
--[[
This is one part of a mod to replicate the digging times from Minecraft. This
part only exposes a function to register digging groups. The rest of the mod is
implemented and documented in the _mcl_autogroup.
The mod is split up into two parts, mcl_autogroup and _mcl_autogroup.
mcl_autogroup contains the API functions used to register custom digging groups.
_mcl_autogroup contains most of the code. The leading underscore in the name
"_mcl_autogroup" is used to force Minetest to load that part of the mod as late
as possible. Minetest loads mods in reverse alphabetical order.
--]]
mcl_autogroup = {}
mcl_autogroup.registered_diggroups = {}
assert(minetest.get_modpath("_mcl_autogroup"), "This mod requires the mod _mcl_autogroup to function")
-- Register a group as a digging group.
--
-- Parameters:
-- group - Name of the group to register as a digging group
-- def - Table with information about the diggroup (defaults to {} if unspecified)
--
-- Values in def:
-- level - If specified it is an array containing the names of the different
-- digging levels the digging group supports.
function mcl_autogroup.register_diggroup(group, def)
mcl_autogroup.registered_diggroups[group] = def or {}
end

View File

@ -1,3 +0,0 @@
name = mcl_autogroup
author = ryvnf
description = MineClone 2 core mod which automatically adds groups to all items. Very important for digging times.

View File

@ -117,10 +117,6 @@ function mcl_burning.damage(obj)
end
function mcl_burning.set_on_fire(obj, burn_time, reason)
if obj:get_hp() < 0 then
return
end
local luaentity = obj:get_luaentity()
if luaentity and luaentity.fire_resistant then
return
@ -149,7 +145,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
end
if old_burn_time <= burn_time then
--[[local sound_id = mcl_burning.get(obj, "int", "sound_id")
local sound_id = mcl_burning.get(obj, "int", "sound_id")
if sound_id == 0 then
sound_id = minetest.sound_play("fire_fire", {
object = obj,
@ -157,7 +153,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
max_hear_distance = 16,
loop = true,
}) + 1
end]]--
end
local hud_id
if obj:is_player() then
@ -167,7 +163,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
hud_elem_type = "image",
position = {x = 0.5, y = 0.5},
scale = {x = -100, y = -100},
text = "mcl_burning_hud_flame_animated.png",
text = "fire_basic_flame.png",
z_index = 1000,
}) + 1
end
@ -175,7 +171,7 @@ function mcl_burning.set_on_fire(obj, burn_time, reason)
mcl_burning.set(obj, "float", "burn_time", burn_time)
mcl_burning.set(obj, "string", "reason", reason)
mcl_burning.set(obj, "int", "hud_id", hud_id)
--mcl_burning.set(obj, "int", "sound_id", sound_id)
mcl_burning.set(obj, "int", "sound_id", sound_id)
local fire_entity = minetest.add_entity(obj:get_pos(), "mcl_burning:fire")
local minp, maxp = mcl_burning.get_collisionbox(obj)
@ -198,8 +194,8 @@ end
function mcl_burning.extinguish(obj)
if mcl_burning.is_burning(obj) then
--local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
--minetest.sound_stop(sound_id)
local sound_id = mcl_burning.get(obj, "int", "sound_id") - 1
minetest.sound_stop(sound_id)
if obj:is_player() then
local hud_id = mcl_burning.get(obj, "int", "hud_id") - 1
@ -210,7 +206,7 @@ function mcl_burning.extinguish(obj)
mcl_burning.set(obj, "float", "burn_time")
mcl_burning.set(obj, "float", "damage_timer")
mcl_burning.set(obj, "int", "hud_id")
--mcl_burning.set(obj, "int", "sound_id")
mcl_burning.set(obj, "int", "sound_id")
end
end

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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)

View File

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

View File

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

View File

@ -33,26 +33,25 @@ mcl_vars.MAP_BLOCKSIZE = math.max(1, core.MAP_BLOCKSIZE or 16)
mcl_vars.mapgen_limit = math.max(1, tonumber(minetest.get_mapgen_setting("mapgen_limit")) or 31000)
mcl_vars.MAX_MAP_GENERATION_LIMIT = math.max(1, core.MAX_MAP_GENERATION_LIMIT or 31000)
local central_chunk_offset = -math.floor(mcl_vars.chunksize / 2)
mcl_vars.central_chunk_offset_in_nodes = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
mcl_vars.chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
local chunk_size_in_nodes = mcl_vars.chunksize * mcl_vars.MAP_BLOCKSIZE
local central_chunk_min_pos = central_chunk_offset * mcl_vars.MAP_BLOCKSIZE
local central_chunk_max_pos = central_chunk_min_pos + mcl_vars.chunk_size_in_nodes - 1
local central_chunk_max_pos = central_chunk_min_pos + chunk_size_in_nodes - 1
local ccfmin = central_chunk_min_pos - mcl_vars.MAP_BLOCKSIZE -- Fullminp/fullmaxp of central chunk, in nodes
local ccfmax = central_chunk_max_pos + mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_b = math.floor(math.min(mcl_vars.mapgen_limit, mcl_vars.MAX_MAP_GENERATION_LIMIT) / mcl_vars.MAP_BLOCKSIZE)
local mapgen_limit_min = -mapgen_limit_b * mcl_vars.MAP_BLOCKSIZE
local mapgen_limit_max = (mapgen_limit_b + 1) * mcl_vars.MAP_BLOCKSIZE - 1
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / mcl_vars.chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / mcl_vars.chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * mcl_vars.chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * mcl_vars.chunk_size_in_nodes
local numcmin = math.max(math.floor((ccfmin - mapgen_limit_min) / chunk_size_in_nodes), 0) -- Number of complete chunks from central chunk
local numcmax = math.max(math.floor((mapgen_limit_max - ccfmax) / chunk_size_in_nodes), 0) -- fullminp/fullmaxp to effective mapgen limits.
mcl_vars.mapgen_edge_min = central_chunk_min_pos - numcmin * chunk_size_in_nodes
mcl_vars.mapgen_edge_max = central_chunk_max_pos + numcmax * chunk_size_in_nodes
local function coordinate_to_block(x)
return math.floor(x / mcl_vars.MAP_BLOCKSIZE)
end
local function coordinate_to_chunk(x)
return math.floor((coordinate_to_block(x) - central_chunk_offset) / mcl_vars.chunksize)
return math.floor((coordinate_to_block(x) + central_chunk_offset) / mcl_vars.chunksize)
end
function mcl_vars.pos_to_block(pos)
@ -71,7 +70,7 @@ function mcl_vars.pos_to_chunk(pos)
}
end
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / mcl_vars.chunk_size_in_nodes)
local k_positive = math.ceil(mcl_vars.MAX_MAP_GENERATION_LIMIT / chunk_size_in_nodes)
local k_positive_z = k_positive * 2
local k_positive_y = k_positive_z * k_positive_z

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,105 @@
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
-- 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

View File

@ -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)

View File

@ -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

View File

@ -418,3 +418,31 @@ function mcl_util.get_color(colorstr)
return colorstr, hex
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

View File

@ -6,7 +6,7 @@ mcl_worlds = {}
function mcl_worlds.is_in_void(pos)
local void =
not ((pos.y < mcl_vars.mg_overworld_max and pos.y > mcl_vars.mg_overworld_min) or
(pos.y < mcl_vars.mg_nether_max+128 and pos.y > mcl_vars.mg_nether_min) or
(pos.y < mcl_vars.mg_nether_max and pos.y > mcl_vars.mg_nether_min) or
(pos.y < mcl_vars.mg_end_max and pos.y > mcl_vars.mg_end_min))
local void_deadly = false
@ -15,11 +15,11 @@ function mcl_worlds.is_in_void(pos)
-- Overworld → Void → End → Void → Nether → Void
if pos.y < mcl_vars.mg_overworld_min and pos.y > mcl_vars.mg_end_max then
void_deadly = pos.y < mcl_vars.mg_overworld_min - deadly_tolerance
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max+128 then
elseif pos.y < mcl_vars.mg_end_min and pos.y > mcl_vars.mg_nether_max then
-- The void between End and Nether. Like usual, but here, the void
-- *above* the Nether also has a small tolerance area, so player
-- can fly above the Nether without getting hurt instantly.
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max+128 + deadly_tolerance)
void_deadly = (pos.y < mcl_vars.mg_end_min - deadly_tolerance) and (pos.y > mcl_vars.mg_nether_max + deadly_tolerance)
elseif pos.y < mcl_vars.mg_nether_min then
void_deadly = pos.y < mcl_vars.mg_nether_min - deadly_tolerance
end
@ -35,7 +35,7 @@ end
function mcl_worlds.y_to_layer(y)
if y >= mcl_vars.mg_overworld_min then
return y - mcl_vars.mg_overworld_min, "overworld"
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max+128 then
elseif y >= mcl_vars.mg_nether_min and y <= mcl_vars.mg_nether_max then
return y - mcl_vars.mg_nether_min, "nether"
elseif y >= mcl_vars.mg_end_min and y <= mcl_vars.mg_end_max then
return y - mcl_vars.mg_end_min, "end"
@ -73,7 +73,7 @@ end
-- Takes a position and returns true if this position can have Nether dust
function mcl_worlds.has_dust(pos)
-- Weather in the Overworld and the high part of the void below
return pos.y <= mcl_vars.mg_nether_max + 138 and pos.y >= mcl_vars.mg_nether_min - 10
return pos.y <= mcl_vars.mg_nether_max + 64 and pos.y >= mcl_vars.mg_nether_min - 64
end
-- Takes a position (pos) and returns true if compasses are working here

View File

@ -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

View File

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

View File

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

View File

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

View File

@ -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

View File

@ -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)

View File

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

View File

@ -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

View File

@ -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)

View File

@ -0,0 +1,4 @@
name = mcl_predicates
author = Fleckenstein
description = Provides MC-like predicates
depends = mcl_util, mcl_numbers

View File

@ -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

View File

@ -0,0 +1,4 @@
name = mcl_types
author = Fleckenstein
description = Offers compare functions for many types, used for MineClone2 datapacks
depends =

2
mods/DATA/modpack.conf Normal file
View File

@ -0,0 +1,2 @@
name = DATA
description = Meta-modpack containing the datapack system for MineClone 2

View File

@ -165,6 +165,66 @@ minetest.register_globalstep(function(dtime)
end
end)
local minigroups = { "shearsy", "swordy", "shearsy_wool", "swordy_cobweb" }
local basegroups = { "pickaxey", "axey", "shovely" }
local materials = { "wood", "gold", "stone", "iron", "diamond" }
-- Checks if the given node would drop its useful drop if dug by a tool
-- with the given tool capabilities. Returns true if it will yield its useful
-- drop, false otherwise.
local check_can_drop = function(node_name, tool_capabilities)
local handy = minetest.get_item_group(node_name, "handy")
local dig_immediate = minetest.get_item_group(node_name, "dig_immediate")
if handy == 1 or dig_immediate == 2 or dig_immediate == 3 then
return true
else
local toolgroupcaps
if tool_capabilities then
toolgroupcaps = tool_capabilities.groupcaps
else
return false
end
-- Compare node groups with tool capabilities
for m=1, #minigroups do
local minigroup = minigroups[m]
local g = minetest.get_item_group(node_name, minigroup)
if g ~= 0 then
local plus = minigroup .. "_dig"
if toolgroupcaps[plus] then
return true
end
for e=1,5 do
local effplus = plus .. "_efficiency_" .. e
if toolgroupcaps[effplus] then
return true
end
end
end
end
for b=1, #basegroups do
local basegroup = basegroups[b]
local g = minetest.get_item_group(node_name, basegroup)
if g ~= 0 then
for m=g, #materials do
local plus = basegroup .. "_dig_"..materials[m]
if toolgroupcaps[plus] then
return true
end
for e=1,5 do
local effplus = plus .. "_efficiency_" .. e
if toolgroupcaps[effplus] then
return true
end
end
end
end
end
return false
end
end
-- Stupid workaround to get drops from a drop table:
-- Create a temporary table in minetest.registered_nodes that contains the proper drops,
-- because unfortunately minetest.get_node_drops needs the drop table to be inside a registered node definition
@ -221,20 +281,17 @@ function minetest.handle_node_drops(pos, drops, digger)
-- Check if node will yield its useful drop by the digger's tool
local dug_node = minetest.get_node(pos)
local tooldef
local toolcaps
local tool
if digger ~= nil then
tool = digger:get_wielded_item()
tooldef = minetest.registered_tools[tool:get_name()]
toolcaps = tool:get_tool_capabilities()
if not mcl_autogroup.can_harvest(dug_node.name, tool:get_name()) then
if not check_can_drop(dug_node.name, toolcaps) then
return
end
end
local diggroups = tooldef and tooldef._mcl_diggroups
local shearsy_level = diggroups and diggroups.shearsy and diggroups.shearsy.level
--[[ Special node drops when dug by shears by reading _mcl_shears_drop or with a silk touch tool reading _mcl_silk_touch_drop
from the node definition.
Definition of _mcl_shears_drop / _mcl_silk_touch_drop:
@ -246,7 +303,7 @@ function minetest.handle_node_drops(pos, drops, digger)
local silk_touch_drop = false
local nodedef = minetest.registered_nodes[dug_node.name]
if shearsy_level and shearsy_level > 0 and nodedef._mcl_shears_drop then
if toolcaps ~= nil and toolcaps.groupcaps and toolcaps.groupcaps.shearsy_dig and nodedef._mcl_shears_drop then
if nodedef._mcl_shears_drop == true then
drops = { dug_node.name }
else

View File

@ -2826,7 +2826,7 @@ local falling = function(self, pos)
end
if mcl_portals ~= nil then
if mcl_portals.nether_portal_cooloff(self.object) then
if mcl_portals.nether_portal_cooloff[self.object] then
return false -- mob has teleported through Nether portal - it's 99% not falling
end
end
@ -2856,18 +2856,6 @@ local falling = function(self, pos)
self.object:set_acceleration({x = 0, y = 0, z = 0})
end
if minetest.registered_nodes[node_ok(pos).name].groups.lava then
if self.floats_on_lava == 1 then
self.object:set_acceleration({
x = 0,
y = -self.fall_speed / (max(1, v.y) ^ 2),
z = 0
})
end
end
-- in water then float up
if minetest.registered_nodes[node_ok(pos).name].groups.water then
@ -3785,7 +3773,6 @@ minetest.register_entity(name, {
knock_back = def.knock_back ~= false,
shoot_offset = def.shoot_offset or 0,
floats = def.floats or 1, -- floats in water by default
floats_on_lava = def.floats_on_lava or 0,
replace_rate = def.replace_rate,
replace_what = def.replace_what,
replace_with = def.replace_with,

View File

@ -450,7 +450,7 @@ mobs:spawn_specific("mobs_mc:donkey", mobs_mc.spawn.grassland_savanna, {"air"},
-- spawn eggs
mobs:register_egg("mobs_mc:horse", S("Horse"), "mobs_mc_spawn_icon_horse.png", 0)
mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "mobs_mc_spawn_icon_horse_skeleton.png", 0)
--mobs:register_egg("mobs_mc:skeleton_horse", S("Skeleton Horse"), "mobs_mc_spawn_icon_horse_skeleton.png", 0)
--mobs:register_egg("mobs_mc:zombie_horse", S("Zombie Horse"), "mobs_mc_spawn_icon_horse_zombie.png", 0)
mobs:register_egg("mobs_mc:donkey", S("Donkey"), "mobs_mc_spawn_icon_donkey.png", 0)
mobs:register_egg("mobs_mc:mule", S("Mule"), "mobs_mc_spawn_icon_mule.png", 0)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.0 KiB

View File

@ -960,7 +960,7 @@ mobs:register_mob("mobs_mc:villager", {
"mobs_mc_villager_smith.png", --hat
},
},
visual_size = {x=2.75, y=2.75},
visual_size = {x=3, y=3},
makes_footstep_sound = true,
walk_velocity = 1.2,
run_velocity = 2.4,

View File

@ -28,7 +28,7 @@ mobs:register_mob("mobs_mc:evoker", {
"blank.png", --no hat
-- TODO: Attack glow
} },
visual_size = {x=2.75, y=2.75},
visual_size = {x=3, y=3},
makes_footstep_sound = true,
damage = 6,
walk_velocity = 0.2,

View File

@ -36,7 +36,7 @@ mobs:register_mob("mobs_mc:illusioner", {
-- TODO: more sounds
distance = 16,
},
visual_size = {x=2.75, y=2.75},
visual_size = {x=3, y=3},
walk_velocity = 0.6,
run_velocity = 2,
jump = true,

View File

@ -30,7 +30,7 @@ mobs:register_mob("mobs_mc:vindicator", {
-- TODO: Glow when attacking (mobs_mc_vindicator.png)
},
},
visual_size = {x=2.75, y=2.75},
visual_size = {x=3, y=3},
makes_footstep_sound = true,
damage = 13,
reach = 2,

View File

@ -45,7 +45,7 @@ mobs:register_mob("mobs_mc:villager_zombie", {
{"mobs_mc_zombie_smith.png"},
{"mobs_mc_zombie_villager.png"}
},
visual_size = {x=2.75, y=2.75},
visual_size = {x=3, y=3},
makes_footstep_sound = true,
damage = 3,
reach = 2,

View File

@ -25,7 +25,7 @@ mobs:register_mob("mobs_mc:witch", {
textures = {
{"mobs_mc_witch.png"},
},
visual_size = {x=2.75, y=2.75},
visual_size = {x=3, y=3},
makes_footstep_sound = true,
damage = 2,
reach = 2,

View File

@ -38,7 +38,6 @@ mcl_weather.reg_weathers["none"] = {
local storage = minetest.get_mod_storage()
-- Save weather into mod storage, so it can be loaded after restarting the server
local save_weather = function()
if not mcl_weather.end_time then return end
storage:set_string("mcl_weather_state", mcl_weather.state)
storage:set_int("mcl_weather_end_time", mcl_weather.end_time)
minetest.log("verbose", "[mcl_weather] Weather data saved: state="..mcl_weather.state.." end_time="..mcl_weather.end_time)

View File

@ -0,0 +1,4 @@
mesecons
mcl_sounds
doc?
screwdriver?

View File

@ -1,3 +1,2 @@
name = mcl_comparators
depends = mcl_wip, mesecons, mcl_sounds
optional_depends = doc, screwdriver
depends = mcl_wip

View File

@ -0,0 +1,12 @@
mcl_init
mcl_formspec
mesecons
mcl_sounds
mcl_tnt
mcl_worlds
mcl_core
mcl_nether
mcl_armor_stand
mcl_armor
doc?
screwdriver?

View File

@ -1,3 +0,0 @@
name = mcl_dispensers
depends = mcl_init, mcl_formspec, mesecons, mcl_sounds, mcl_tnt, mcl_worlds, mcl_core, mcl_nether, mcl_armor_stand, mcl_armor
optional_depends = doc, screwdriver

View File

@ -0,0 +1,6 @@
mcl_init
mcl_formspec
mesecons
mcl_util
doc?
screwdriver?

View File

@ -1,3 +0,0 @@
name = mcl_droppers
depends = mcl_init, mcl_formspec, mesecons, mcl_util
optional_depends = doc, screwdriver

View File

@ -0,0 +1,2 @@
mesecons
mcl_util

View File

@ -1,2 +0,0 @@
name = mcl_observers
depends = mesecons, mcl_util

View File

@ -0,0 +1,3 @@
mcl_sounds
mcl_core
doc?

View File

@ -1,3 +0,0 @@
name = mesecons
depends = mcl_sounds, mcl_core
optional_depends = doc

View File

@ -0,0 +1 @@
mesecons

View File

@ -1,2 +0,0 @@
name = mesecons_alias
depends = mesecons

View File

@ -0,0 +1,2 @@
mesecons
doc?

View File

@ -1,3 +0,0 @@
name = mesecons_button
depends = mesecons
optional_depends = doc

View File

@ -0,0 +1,3 @@
mesecons
doc?
doc_items?

View File

@ -1,3 +0,0 @@
name = mesecons_commandblock
depends = mesecons
optional_depends = doc, doc_items

View File

@ -0,0 +1,3 @@
mesecons
doc?
screwdriver?

View File

@ -1,3 +0,0 @@
name = mesecons_delayer
depends = mesecons
optional_depends = doc, screwdriver

View File

@ -0,0 +1,2 @@
mesecons
doc?

View File

@ -1,3 +0,0 @@
name = mesecons_lightstone
depends = mesecons
optional_depends = doc

View File

@ -0,0 +1 @@
mesecons

View File

@ -1,2 +0,0 @@
name = mesecons_mvps
depends = mesecons

View File

@ -0,0 +1,2 @@
mesecons
mcl_particles

View File

@ -1,2 +0,0 @@
name = mesecons_noteblock
depends = mesecons, mcl_particles

View File

@ -0,0 +1,5 @@
mesecons
mesecons_mvps
mcl_mobitems
doc?
screwdriver?

View File

@ -1,3 +0,0 @@
name = mesecons_pistons
depends = mesecons, mesecons_mvps, mcl_mobitems
optional_depends = doc, screwdriver

View File

@ -0,0 +1,2 @@
mesecons
doc?

View File

@ -1,3 +0,0 @@
name = mesecons_pressureplates
depends = mesecons
optional_depends = doc

View File

@ -0,0 +1,2 @@
mesecons
doc?

View File

@ -1,3 +0,0 @@
name = mesecons_solarpanel
depends = mesecons
optional_depends = doc

View File

@ -0,0 +1,3 @@
mesecons
mcl_torches
doc?

View File

@ -1,3 +0,0 @@
name = mesecons_torch
depends = mesecons, mcl_torches
optional_depends = doc

View File

@ -0,0 +1,2 @@
mesecons
doc?

View File

@ -1,3 +0,0 @@
name = mesecons_walllever
depends = mesecons
optional_depends = doc

View File

@ -0,0 +1,2 @@
mesecons
doc?

View File

@ -1,3 +0,0 @@
name = mesecons_wires
depends = mesecons
optional_depends = doc

View File

@ -9,7 +9,6 @@ local modpath = minetest.get_modpath(minetest.get_current_modname())
armor = {
timer = 0,
elements = {"head", "torso", "legs", "feet"},
physics = {"jump","speed","gravity"},
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;craft;4,1;3,3;]"
@ -18,7 +17,6 @@ armor = {
.."listring[current_player;craft]",
textures = {},
default_skin = "character",
last_damage_types = {},
}
if minetest.get_modpath("mcl_skins") then
@ -33,7 +31,7 @@ elseif minetest.get_modpath("wardrobe") then
skin_mod = "wardrobe"
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
return itemstack
end
@ -149,8 +147,8 @@ armor.set_player_armor = function(self, player)
if level then
local texture = def.texture or item:gsub("%:", "_")
local enchanted_addition = (mcl_enchanting.is_enchanted(item) and mcl_enchanting.overlay or "")
table.insert(textures, "("..texture..".png"..enchanted_addition..")")
preview = "(player.png^[opacity:0^"..texture.."_preview.png"..enchanted_addition..")"..(preview and "^"..preview or "")
table.insert(textures, texture..".png"..enchanted_addition)
preview = "player.png^[opacity:0^"..texture.."_preview.png"..enchanted_addition..""..(preview and "^"..preview or "")
armor_level = armor_level + level
items = items + 1
mcl_armor_points = mcl_armor_points + (def.groups["mcl_armor_points"] or 0)

View File

@ -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

View File

@ -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)

View File

@ -1,13 +1,68 @@
local S = minetest.get_translator("mcl_armor")
dofile(minetest.get_modpath(minetest.get_current_modname()).."/armor.lua")
dofile(minetest.get_modpath(minetest.get_current_modname()).."/alias.lua")
mcl_armor = {
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
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", {
description = S("Leather Cap"),
_doc_items_longdesc = longdesc,
@ -322,71 +377,4 @@ local craft_ingreds = {
gold = { "mcl_core:gold_ingot", "mcl_core:gold_nugget" },
diamond = { "mcl_core:diamond" },
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
}

View File

@ -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",
}

View File

@ -16,17 +16,23 @@ local dir_to_pitch = function(dir)
end
local random_arrow_positions = function(positions, placement)
local min = 0
local max = 0
if positions == 'x' then
return math.random(-4, 4)
min = -4
max = 4
elseif positions == 'y' then
return math.random(0, 10)
min = 0
max = 10
end
if placement == 'front' and positions == 'z' then
return 3
min = 3
max = 3
elseif placement == 'back' and positions == 'z' then
return -3
min = -3
max = -3
end
return 0
return math.random(max, min)
end
local mod_awards = minetest.get_modpath("awards") and minetest.get_modpath("mcl_achievements")
@ -298,8 +304,8 @@ ARROW_ENTITY.on_step = function(self, dtime)
else
self._attach_parent = 'Body'
end
self._z_rotation = math.random(-30, 30)
self._y_rotation = math.random( -30, 30)
self._z_rotation = math.random(30, -30)
self._y_rotation = math.random(30, -30)
self.object:set_attach(obj, self._attach_parent, {x=self._x_position,y=self._y_position,z=random_arrow_positions('z', placement)}, {x=0,y=self._rotation_station + self._y_rotation,z=self._z_rotation})
minetest.after(150, function()
self.object:remove()
@ -331,8 +337,8 @@ ARROW_ENTITY.on_step = function(self, dtime)
minetest.sound_play({name="mcl_bows_hit_other", gain=0.3}, {pos=self.object:get_pos(), max_hear_distance=16}, true)
end
end
mcl_burning.extinguish(self.object)
if not obj:is_player() then
mcl_burning.extinguish(self.object)
self.object:remove()
end
return

View File

@ -0,0 +1,4 @@
mcl_core
mclx_core?
mcl_sounds
doc?

View File

@ -1,3 +1 @@
name = mcl_cauldrons
depends = mcl_core, mcl_sounds
optional_depends = mclx_core, doc

View File

@ -0,0 +1,8 @@
mcl_init
mcl_formspec
mcl_core
mcl_sounds
mcl_end
mesecons
doc?
screwdriver?

View File

@ -1050,6 +1050,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 = {

View File

@ -1,3 +0,0 @@
name = mcl_chests
depends = mcl_init, mcl_formspec, mcl_core, mcl_sounds, mcl_end, mesecons
optional_depends = doc, screwdriver

View File

@ -0,0 +1,4 @@
mcl_init
mcl_worlds
mesecons
doc?

View File

@ -0,0 +1 @@
A fantasy clock item roughly shows the time of day.

Some files were not shown because too many files have changed in this diff Show More