1
0
Fork 0

Compare commits

..

2 Commits

37 changed files with 1872 additions and 2335 deletions

View File

@ -83,7 +83,7 @@ The VoxeLibre repository is hosted at Mesehub. To contribute or report issues, h
* Discord: <https://discord.gg/xE4z8EEpDC>
* YouTube: <https://www.youtube.com/channel/UClI_YcsXMF3KNeJtoBfnk9A>
* ContentDB: <https://content.minetest.net/packages/wuzzy/mineclone2/>
* OpenCollective: <https://opencollective.com/voxelibre>
* OpenCollective: <https://opencollective.com/mineclone2>
* Mastodon: <https://fosstodon.org/@VoxeLibre>
* Lemmy: <https://lemm.ee/c/voxelibre>
* Matrix space: <https://app.element.io/#/room/#voxelibre:matrix.org>

View File

@ -117,10 +117,6 @@ 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 = {}
@ -186,6 +182,7 @@ local function add_groupcaps(toolname, groupcaps, groupcaps_def, efficiency)
local mult = capsdef.speed or 1
local uses = capsdef.uses
local def = mcl_autogroup.registered_diggroups[g]
assert(def, toolname .. " has unknown diggroup '" .. dump(g) .. "'")
local max_level = def.levels and #def.levels or 1
assert(capsdef.level, toolname .. ' is missing level for ' .. g)
@ -313,6 +310,13 @@ function mcl_autogroup.get_wear(toolname, diggroup)
end
local function overwrite()
-- Refresh, now that all groups are known.
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)
for nname, ndef in pairs(minetest.registered_nodes) do
local newgroups = table.copy(ndef.groups)
if (nname ~= "ignore" and ndef.diggable) then
@ -374,4 +378,5 @@ local function overwrite()
end
end
overwrite()
-- Make sure all tools and groups are registered
minetest.register_on_mods_loaded(overwrite)

View File

@ -1,3 +1,4 @@
name = _mcl_autogroup
depends = mcl_autogroup
author = ryvnf
description = VoxeLibre core mod which automatically adds groups to all items. Very important for digging times.

View File

@ -136,7 +136,6 @@ local function try_object_pickup(player, inv, object, checkpos)
-- Destroy entity
-- This just prevents this section to be run again because object:remove() doesn't remove the item immediately.
le.target = checkpos
le.itemstring = ""
le._removed = true
-- Stop the object
@ -958,7 +957,6 @@ minetest.register_entity(":__builtin:item", {
self.random_velocity = 0
self:set_item(own_stack:to_string())
entity.itemstring = ""
entity._removed = true
object:remove()
return true

View File

@ -18,7 +18,7 @@ mcl_mobs.invis = {}
local remove_far = true
local mobs_debug = minetest.settings:get_bool("mobs_debug", false) -- Shows helpful debug info above each mob
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn",true)
local MAPGEN_LIMIT = mcl_vars.mapgen_limit
local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90

View File

@ -17,13 +17,12 @@ local mt_get_biome_name = minetest.get_biome_name
local get_objects_inside_radius = minetest.get_objects_inside_radius
local get_connected_players = minetest.get_connected_players
local math_min = math.min
local math_max = math.max
local math_random = math.random
local math_floor = math.floor
local math_ceil = math.ceil
local math_cos = math.cos
local math_sin = math.sin
local math_round = function(x) return (x > 0) and math_floor(x + 0.5) or math_ceil(x - 0.5) end
local math_sqrt = math.sqrt
local vector_distance = vector.distance
@ -34,9 +33,9 @@ local table_copy = table.copy
local table_remove = table.remove
local pairs = pairs
local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_spawning", false)
local function mcl_log (message, property)
if logging then
if LOGGING_ON then
if property then
message = message .. ": " .. dump(property)
end
@ -55,10 +54,8 @@ local FIND_SPAWN_POS_RETRIES = 16
local FIND_SPAWN_POS_RETRIES_SUCCESS_RESPIN = 8
local MOB_SPAWN_ZONE_INNER = 24
local MOB_SPAWN_ZONE_INNER_SQ = MOB_SPAWN_ZONE_INNER^2 -- squared
local MOB_SPAWN_ZONE_MIDDLE = 32
local MOB_SPAWN_ZONE_OUTER = 128
local MOB_SPAWN_ZONE_OUTER_SQ = MOB_SPAWN_ZONE_OUTER^2 -- squared
-- range for mob count
local MOB_CAP_INNER_RADIUS = 32
@ -98,6 +95,7 @@ mcl_log("Percentage of hostile spawns are group: " .. hostile_group_percentage_s
--do mobs spawn?
local mobs_spawn = minetest.settings:get_bool("mobs_spawn", true) ~= false
local spawn_protected = minetest.settings:get_bool("mobs_spawn_protected") ~= false
local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn",true)
-- THIS IS THE BIG LIST OF ALL BIOMES - used for programming/updating mobs
-- Also used for missing parameter
@ -603,40 +601,71 @@ function mcl_mobs:spawn_specific(name, dimension, type_of_spawning, biomes, min_
spawn_dictionary[key]["check_position"] = check_position
end
-- Calculate the inverse of a piecewise linear function f(x). Line segments are represented as two
-- adjacent points specified as { x, f(x) }. At least 2 points are required. If there are most solutions,
-- the one with a lower x value will be chosen.
local function inverse_pwl(fx, f)
if fx < f[1][2] then
return f[1][1]
end
for i=2,#f do
local x0,fx0 = unpack(f[i-1])
local x1,fx1 = unpack(f[i ])
if fx < fx1 then
return (fx - fx0) * (x1 - x0) / (fx1 - fx0) + x0
end
end
return f[#f][1]
end
local SPAWN_DISTANCE_CDF_PWL = {
{0.000,0.00},
{0.083,0.40},
{0.416,0.75},
{1.000,1.00},
}
local two_pi = 2 * math.pi
local function get_next_mob_spawn_pos(pos)
-- Select a distance such that distances closer to the player are selected much more often than
-- those further away from the player. This does produce a concentration at INNER (24 blocks)
local distance = math_random()^2 * (MOB_SPAWN_ZONE_OUTER - MOB_SPAWN_ZONE_INNER) + MOB_SPAWN_ZONE_INNER
-- those further away from the player.
local fx = (math_random(1,10000)-1) / 10000
local x = inverse_pwl(fx, SPAWN_DISTANCE_CDF_PWL)
local distance = x * (MOB_SPAWN_ZONE_OUTER - MOB_SPAWN_ZONE_INNER) + MOB_SPAWN_ZONE_INNER
--print("Using spawn distance of "..tostring(distance).." fx="..tostring(fx)..",x="..tostring(x))
-- Choose a random direction. Rejection sampling is simple and fast (1-2 tries usually)
local xoff, yoff, zoff, dd
repeat
xoff, yoff, zoff = math_random() * 2 - 1, math_random() * 2 - 1, math_random() * 2 - 1
dd = xoff*xoff + yoff*yoff + zoff*zoff
until (dd <= 1 and dd >= 1e-6) -- outside of uniform ball, retry
dd = distance / math_sqrt(dd) -- distance scaling factor
xoff, yoff, zoff = xoff * dd, yoff * dd, zoff * dd
-- TODO Floor xoff and zoff and add 0.5 so it tries to spawn in the middle of the square. Less failed attempts.
-- Use spherical coordinates https://en.wikipedia.org/wiki/Spherical_coordinate_system#Cartesian_coordinates
local theta = math_random() * two_pi
local phi = math_random() * two_pi
local xoff = math_round(distance * math_sin(theta) * math_cos(phi))
local yoff = math_round(distance * math_cos(theta))
local zoff = math_round(distance * math_sin(theta) * math_sin(phi))
local goal_pos = vector.offset(pos, xoff, yoff, zoff)
if not ( math.abs(goal_pos.x) <= SPAWN_MAPGEN_LIMIT and math.abs(goal_pos.y) <= SPAWN_MAPGEN_LIMIT and math.abs(goal_pos.z) <= SPAWN_MAPGEN_LIMIT ) then
if not ( math.abs(goal_pos.x) <= SPAWN_MAPGEN_LIMIT and math.abs(pos.y) <= SPAWN_MAPGEN_LIMIT and math.abs(goal_pos.z) <= SPAWN_MAPGEN_LIMIT ) then
mcl_log("Pos outside mapgen limits: " .. minetest.pos_to_string(goal_pos))
return nil
end
-- Calculate upper/lower y limits
local d2 = xoff*xoff + zoff*zoff -- squared distance in x,z plane only
local y1 = math_sqrt( MOB_SPAWN_ZONE_OUTER_SQ - d2 ) -- absolue value of distance to outer sphere
local R1 = MOB_SPAWN_ZONE_OUTER
local d = vector_distance( pos, vector.new( goal_pos.x, pos.y, goal_pos.z ) ) -- distance from player to projected point on horizontal plane
local y1 = math_sqrt( R1*R1 - d*d ) -- absolue value of distance to outer sphere
local y_min, y_max
if d2 >= MOB_SPAWN_ZONE_INNER_SQ then
local y_min
local y_max
if d >= MOB_SPAWN_ZONE_INNER then
-- Outer region, y range has both ends on the outer sphere
y_min = pos.y - y1
y_max = pos.y + y1
else
-- Inner region, y range spans between inner and outer spheres
local y2 = math_sqrt( MOB_SPAWN_ZONE_INNER_SQ - d2 )
if goal_pos.y > pos.y then
local R2 = MOB_SPAWN_ZONE_INNER
local y2 = math_sqrt( R2*R2 - d*d )
if goal_pos.y > pos. y then
-- Upper hemisphere
y_min = pos.y + y2
y_max = pos.y + y1
@ -646,9 +675,16 @@ local function get_next_mob_spawn_pos(pos)
y_max = pos.y - y2
end
end
y_min = math_round(y_min)
y_max = math_round(y_max)
-- Limit total range of check to 32 nodes (maximum of 3 map blocks)
y_min = math_max(math_floor(y_min), goal_pos.y - 16)
y_max = math_min(math_ceil(y_max), goal_pos.y + 16)
if y_max > goal_pos.y + 16 then
y_max = goal_pos.y + 16
end
if y_min < goal_pos.y - 16 then
y_min = goal_pos.y - 16
end
-- Ask engine for valid spawn locations
local spawning_position_list = find_nodes_in_area_under_air(
@ -961,7 +997,7 @@ if mobs_spawn then
mob_total_wide = 0
end
local cap_space_wide = math_max(type_cap - mob_total_wide, 0)
local cap_space_wide = math.max(type_cap - mob_total_wide, 0)
mcl_log("mob_type", mob_type)
mcl_log("cap_space_wide", cap_space_wide)
@ -969,10 +1005,10 @@ if mobs_spawn then
local cap_space_available = 0
if mob_type == "hostile" then
mcl_log("cap_space_global", cap_space_hostile)
cap_space_available = math_min(cap_space_hostile, cap_space_wide)
cap_space_available = math.min(cap_space_hostile, cap_space_wide)
else
mcl_log("cap_space_global", cap_space_non_hostile)
cap_space_available = math_min(cap_space_non_hostile, cap_space_wide)
cap_space_available = math.min(cap_space_non_hostile, cap_space_wide)
end
local mob_total_close = mob_counts_close[mob_type]
@ -981,8 +1017,8 @@ if mobs_spawn then
mob_total_close = 0
end
local cap_space_close = math_max(close_zone_cap - mob_total_close, 0)
cap_space_available = math_min(cap_space_available, cap_space_close)
local cap_space_close = math.max(close_zone_cap - mob_total_close, 0)
cap_space_available = math.min(cap_space_available, cap_space_close)
mcl_log("cap_space_close", cap_space_close)
mcl_log("cap_space_available", cap_space_available)
@ -1109,7 +1145,7 @@ if mobs_spawn then
local amount_to_spawn = math.random(group_min, spawn_in_group)
mcl_log("Spawning quantity: " .. amount_to_spawn)
amount_to_spawn = math_min(amount_to_spawn, cap_space_available)
amount_to_spawn = math.min(amount_to_spawn, cap_space_available)
mcl_log("throttled spawning quantity: " .. amount_to_spawn)
if logging then
@ -1160,8 +1196,8 @@ if mobs_spawn then
local players = get_connected_players()
local total_mobs, total_non_hostile, total_hostile = count_mobs_total_cap()
local cap_space_hostile = math_max(mob_cap.global_hostile - total_hostile, 0)
local cap_space_non_hostile = math_max(mob_cap.global_non_hostile - total_non_hostile, 0)
local cap_space_hostile = math.max(mob_cap.global_hostile - total_hostile, 0)
local cap_space_non_hostile = math.max(mob_cap.global_non_hostile - total_non_hostile, 0)
mcl_log("global cap_space_hostile", cap_space_hostile)
mcl_log("global cap_space_non_hostile", cap_space_non_hostile)

View File

@ -4,17 +4,21 @@
local S = minetest.get_translator("mobs_mc")
local BEAM_CHECK_FREQUENCY = 1
local BEAM_CHECK_FREQUENCY = 2
local POS_CHECK_FREQUENCY = 15
local HEAL_INTERVAL = 1
local HEAL_AMOUNT = 2
local HEAL_AMMOUNT = 37
local function heal(self)
local o = self.object
self.health = math.min(self.hp_max,self.health + HEAL_AMMOUNT)
end
local function check_beam(self)
for _, obj in ipairs(minetest.get_objects_inside_radius(self.object:get_pos(), 80)) do
local luaentity = obj:get_luaentity()
if luaentity and luaentity.name == "mcl_end:crystal" then
if luaentity.beam then
if luaentity.beam == self.beam then
heal(self)
break
end
else
@ -102,6 +106,7 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
},
ignores_nametag = true,
do_custom = function(self,dtime)
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
if self._pos_timer == nil or self._pos_timer > POS_CHECK_FREQUENCY then
self._pos_timer = 0
check_pos(self)
@ -110,20 +115,8 @@ mcl_mobs.register_mob("mobs_mc:enderdragon", {
self._beam_timer = 0
check_beam(self)
end
self._beam_timer = self._beam_timer + dtime
self._pos_timer = self._pos_timer + dtime
if self.beam ~= nil then
-- heal
self._heal_timer = (self._heal_timer or 0) + dtime
if self._heal_timer > HEAL_INTERVAL then
self.health = math.min(self.hp_max,self.health + HEAL_AMOUNT)
self._heal_timer = self._heal_timer - HEAL_INTERVAL
end
end
mcl_bossbars.update_boss(self.object, "Ender Dragon", "light_purple")
end,
on_die = function(self, pos, cmi_cause)
if self._portal_pos then

View File

@ -83,8 +83,8 @@ mcl_mobs.register_mob("mobs_mc:iron_golem", {
stand_speed = 15, walk_speed = 15, run_speed = 25, punch_speed = 15,
stand_start = 0, stand_end = 0,
walk_start = 0, walk_end = 40,
run_start = 40, run_end = 80,
punch_start = 80, punch_end = 90,
run_start = 0, run_end = 40,
punch_start = 40, punch_end = 50,
},
jump = true,
do_custom = function(self, dtime)

View File

@ -1,4 +1,4 @@
name = mcl_void_damage
author = Wuzzy
description = Deal damage to entities stuck in the deep void
depends = mcl_worlds
depends = mcl_worlds, mcl_spawn

View File

@ -1,4 +1,4 @@
name = mcl_death_messages
author = 4Evergreen4
description = Shows messages in chat when a player dies.
depends = mcl_colors
depends = mcl_colors, mcl_damage

View File

@ -1,12 +1,7 @@
-- Constants
local size_min = 20 / 100 -- minimum size, prescaled
local size_max = 59 / 100 -- maximum size, prescaled
local delta_size = (size_max - size_min) / 10 -- Size change for each XP size level
local max_orb_age = 300 -- seconds
local gravity = vector.new(0, -((tonumber(minetest.settings:get("movement_gravity"))) or 9.81), 0)
local size_min, size_max = 20, 59
local delta_size = size_max - size_min
local size_to_xp = {
-- min and max XP amount for a given size
{-32768, 2}, -- 1
{ 3, 6}, -- 2
{ 7, 16}, -- 3
@ -21,20 +16,24 @@ local size_to_xp = {
}
local function xp_to_size(xp)
xp = xp or 0
local i, l = 1, #size_to_xp
-- Find the size for the xp amount
for i=1,11 do
local bucket = size_to_xp[i]
if xp >= bucket[1] and xp <= bucket[2] then
return (i - 1) * delta_size + size_min
end
while xp > size_to_xp[i][1] and i < l do
i = i + 1
end
-- Fallback is the minimum size
return size_min
return ((i - 1) / (l - 1) * delta_size + size_min) / 100
end
local max_orb_age = 300 -- seconds
local gravity = vector.new(0, -((tonumber(minetest.settings:get("movement_gravity"))) or 9.81), 0)
local collector, pos, pos2
local direction, distance, player_velocity, goal
local currentvel, acceleration, multiplier, velocity
local node, vel, def
local is_moving, is_slippery, slippery, slip_factor
local size
local function xp_step(self, dtime)
--if item set to be collected then only execute go to player
if self.collected == true then
@ -42,32 +41,33 @@ local function xp_step(self, dtime)
self.collected = false
return
end
local collector = minetest.get_player_by_name(self.collector)
collector = minetest.get_player_by_name(self.collector)
if collector and collector:get_hp() > 0 and vector.distance(self.object:get_pos(),collector:get_pos()) < 7.25 then
self.object:set_acceleration(vector.new(0,0,0))
self.disable_physics(self)
--get the variables
local pos = self.object:get_pos()
local pos2 = collector:get_pos()
pos = self.object:get_pos()
pos2 = collector:get_pos()
local player_velocity = collector:get_velocity() or collector:get_player_velocity()
player_velocity = collector:get_velocity() or collector:get_player_velocity()
pos2.y = pos2.y + 0.8
local direction = vector.direction(pos,pos2)
local distance = vector.distance(pos2,pos)
local multiplier = distance
direction = vector.direction(pos,pos2)
distance = vector.distance(pos2,pos)
multiplier = distance
if multiplier < 1 then
multiplier = 1
end
local currentvel = self.object:get_velocity()
goal = vector.multiply(direction,multiplier)
currentvel = self.object:get_velocity()
if distance > 1 then
multiplier = 20 - distance
local velocity = vector.multiply(direction, multiplier)
local acceleration = vector.new(velocity.x - currentvel.x, velocity.y - currentvel.y, velocity.z - currentvel.z)
self.object:add_velocity(vector.add(acceleration, player_velocity))
velocity = vector.multiply(direction,multiplier)
goal = velocity
acceleration = vector.new(goal.x-currentvel.x,goal.y-currentvel.y,goal.z-currentvel.z)
self.object:add_velocity(vector.add(acceleration,player_velocity))
elseif distance < 0.8 then
mcl_experience.add_xp(collector, self._xp)
self.object:remove()
@ -75,26 +75,28 @@ local function xp_step(self, dtime)
return
else
self.collector = nil
self:enable_physics()
self.enable_physics(self)
end
end
-- Age orbs
self.age = self.age + dtime
if self.age > max_orb_age then
self.object:remove()
return
end
local pos = self.object:get_pos()
if not pos then return end
pos = self.object:get_pos()
-- Get the node directly below the XP orb
local node = minetest.get_node_or_nil({
x = pos.x,
y = pos.y - 0.25, -- Orb collision box is +/-0.2, so go a bit below that
z = pos.z
})
if pos then
node = minetest.get_node_or_nil({
x = pos.x,
y = pos.y -0.25,
z = pos.z
})
else
return
end
-- Remove nodes in 'ignore'
if node and node.name == "ignore" then
@ -107,18 +109,18 @@ local function xp_step(self, dtime)
end
-- Slide on slippery nodes
local vel = self.object:get_velocity()
local def = node and minetest.registered_nodes[node.name]
local is_moving = (def and not def.walkable) or
vel = self.object:get_velocity()
def = node and minetest.registered_nodes[node.name]
is_moving = (def and not def.walkable) or
vel.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
local is_slippery = false
is_slippery = false
if def and def.walkable then
local slippery = minetest.get_item_group(node.name, "slippery")
slippery = minetest.get_item_group(node.name, "slippery")
is_slippery = slippery ~= 0
if is_slippery and (math.abs(vel.x) > 0.2 or math.abs(vel.z) > 0.2) then
-- Horizontal deceleration
local slip_factor = 4.0 / (slippery + 4)
slip_factor = 4.0 / (slippery + 4)
self.object:set_acceleration({
x = -vel.x * slip_factor,
y = 0,
@ -158,6 +160,7 @@ minetest.register_entity("mcl_experience:orb", {
initial_sprite_basepos = {x = 0, y = 0},
is_visible = true,
pointable = false,
static_save = false,
},
moving_state = true,
slippery_state = false,
@ -188,7 +191,7 @@ minetest.register_entity("mcl_experience:orb", {
-- This was a minetest bug for a while: https://github.com/minetest/minetest/issues/14420
local xp = tonumber(staticdata) or 0
self._xp = xp
local size = xp_to_size(xp)
size = xp_to_size(xp)
self.object:set_properties({
visual_size = {x = size, y = size},
@ -196,9 +199,6 @@ minetest.register_entity("mcl_experience:orb", {
})
self.object:set_sprite({x=1,y=math.random(1,14)}, 14, 0.05, false)
end,
get_staticdata = function(self)
return tostring(self._xp or 0)
end,
enable_physics = function(self)
if not self.physical_state then

View File

@ -1,4 +1,4 @@
name = mcl_title
description = Add an API to add in HUD title
depends = mcl_colors
description = Add an API to add in HUD title
depends = mcl_colors, mcl_util
author = AFCMS

View File

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

View File

@ -286,7 +286,8 @@ function mcl_beds.register_bed(name, def)
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
groups = {handy = 1, flammable = -1, bed = 2, dig_by_piston=1, bouncy=66, fall_damage_add_percent=-50, not_in_creative_inventory = 1},
-- FIXME: Should be bouncy=66, but this would be a higher bounciness than slime blocks!
groups = {handy = 1, flammable = -1, bed = 2, dig_by_piston=1, bouncy=33, fall_damage_add_percent=-50, not_in_creative_inventory = 1},
_mcl_hardness = 0.2,
_mcl_blast_resistance = 1,
sounds = def.sounds or default_sounds,

View File

@ -1,233 +0,0 @@
# `mcl_chests` API
When reading through this documentation, please keep in mind that the chest
animations are achieved by giving each chest node an entity, as Minetest (as of
5.8.1) doesn't support giving nodes animated meshes, only static ones.
Because of that, a lot of parameters passed through the exposed functions are
be related to nodes and entities.
Please refer to [Minetest documentation](http://api.minetest.net/) and the code
comments in `api.lua`.
## `mcl_chests.register_chest(basename, definition)`
This function allows for simple chest registration, used by both regular and
trapped chests.
* `basename` is a string that will be concatenated to form full nodenames for
chests, for example `"mcl_chests:basename_small"`.
* `definition` is a key-value table, with the following fields:
```lua
{
desc = S("Stone Chest"),
-- Equivalent to `description` field of Item/Node definition.
-- Will be shown as chest's name in the inventory.
title = {
small = S("Stone Chest") -- the same as `desc` if not specified
double = S("Large Stone Chest") -- defaults to `"Large " .. desc`
}
-- These will be shown when opening the chest (in formspecs).
longdesc = S(
"Stone Chests are containers which provide 27 inventory slots. Stone Chests can be turned into" ..
"large stone chests with double the capacity by placing two stone chests next to each other."
),
usagehelp = S("To access its inventory, rightclick it. When broken, the items will drop out."),
tt_help = S("27 inventory slots") .. "\n" .. S("Can be combined to a large stone chest"),
-- Equivalent to `_doc_items_longdesc`, `_doc_items_usagehelp` and
-- `_tt_help` fields of Item/Node definition. Shown in the tooltip and wiki.
tiles = {
small = { "vl_stone_chests_small.png" },
double = { "vl_stone_chests_double.png" },
inv = {
"vl_stone_chests_top.png",
"vl_stone_chests_bottom.png",
"vl_stone_chests_right.png",
"vl_stone_chests_left.png",
"vl_stone_chests_back.png",
"vl_stone_chests_front.png"
},
},
-- `small` and `double` fields contain the textures that will be applied to
-- chest entities.
-- `inv` field contains table of textures (6 in total, for each cube side),
-- that will be used to render the chest "node" in the inventory.
groups = {
pickaxey = 1,
stone = 1,
material_stone = 1,
},
-- Equivalent to `groups` field of Item/Node definition. There is some table
-- merging occuring internally, but it is purely for entity rendering.
sounds = {
mcl_sounds.node_sound_stone_defaults(), -- defaults to `nil`
"vl_stone_chests_sound" -- defaults to `"default_chest"`
},
-- First value is equivalent to `sounds` field of Item/Node definition.
-- Second value is a sound prefix, from which the actual sounds will be
-- concatenated (e.g. `vl_stone_chests_sound_open.ogg`). See `api.lua`.
hardness = 4.0,
-- Equivalent to `_mcl_blast_resistance` and `_mcl_hardness` fields of
-- Item/Node definition. They are always equal for chests.
hidden = false,
-- Equivalent to `_doc_items_hidden` field of Item/Node definition.
mesecons = {
receptor = {
state = mesecon.state.on,
rules = mesecon.rules.pplate,
},
},
-- Equivalent to `mesecons` field of Item/Node definition.
on_rightclick = function(pos, node, clicker)
mcl_util.deal_damage(clicker, 2)
end,
-- If provided, will be executed at the end of the actual `on_rightclick`
-- function of the chest node.
-- If `on_rightclick_left` or `on_rightclick_right` are not provided, this
-- will also be what is executed for left and right double chest nodes,
-- respectively.
drop = "chest",
-- If provided, the chest will not drop itself, but the item of the chest
-- with that basename.
canonical_basename = "chest",
-- If provided, the chest will turn into chest with that basename in
-- `on_construct`.
}
```
For usage examples, see `chests.lua` and `example.lua`.
## `mcl_chests.create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos)`
This function creates a chest entity based on the parameters:
* `pos` is the position vector.
* `node_name` is a string used in initialization data for the entity.
* `textures` is the entity textures.
* `param2` is a node param2, which then will be converted to entity direction.
* `double` is a boolean value for whether the chest is double or not.
* `sound_prefix` is a string, from which the actual sounds for the entity will
be concatenated.
* `mesh_prefix` is the same thing as `sound_prefix`, but for meshes.
* `animation_type` is a string that will be used in `set_animation` method of
chest entity.
* `dir` and `entity_pos` are number and vector values used to get entity info.
Returned value is either a luaentity, or `nil` if failed (in which case a
warning message gets written into the console).
## `mcl_chests.find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir, entity_pos)`
This function finds an existing entity, or creates one if failed. Parameters:
* `pos` is the position vector.
* `node_name` is a string used in initialization data for the entity.
* `textures` is the entity textures.
* `param2` is a node param2, which then will be converted to entity direction.
* `double` is a boolean value for whether the chest is double or not.
* `sound_prefix` is a string, from which the actual sounds for the entity will
be concatenated.
* `mesh_prefix` is the same thing as `sound_prefix`, but for meshes.
* `animation_type` is a string that will be used in `set_animation` method of
chest entity.
* `dir` and `entity_pos` are number and vector values used to get entity info.
Returned value is either a luaentity, or `nil` if failed (in which case a
warning message gets written into the console).
## `mcl_chests.select_and_spawn_entity(pos, node)`
This function is a simple wrapper for `mcl_chests.find_or_create_entity`,
getting most of the fields from node definition.
* `pos` is the position vector.
* `node` is a NodeRef.
Returned value is either a luaentity, or `nil` if failed (in which case a
warning message gets written into the console).
## `mcl_chests.no_rotate`
This function is equivalent to `screwdriver.disallow` and is used when a chest
can't be rotated, and is applied in `on_rotate` field of Node definition.
## `mcl_chests.simple_rotate(pos, node, user, mode, new_param2)`
This function allows for simple rotation with the entity being affected as well,
and is applied in `on_rotate` field of Node definition.
## `mcl_chests.open_chests`
This table contains all currently open chests, indexed by player name.
`nil` if player is not using a chest, and `{ pos = <chest node position> }`
otherwise (where position is a vector value).
## `mcl_chests.protection_check_move(pos, from_list, from_index, to_list, to_index, count, player)`
This function is called in `allow_metadata_inventory_move` field of Node
definition.
## `mcl_chests.protection_check_put_take(pos, listname, index, stack, player)`
This function is called in `allow_metadata_inventory_put` and
`allow_metadata_inventory_take` fields of Node definition.
## `mcl_chests.player_chest_open(player, pos, node_name, textures, param2, double, sound, mesh, shulker)`
This function opens a chest based on the parameters:
* `player` is an ObjectRef.
* `pos` is the position vector.
* `node_name` is a string used in initialization data for the entity.
* `textures` is the entity textures.
* `param2` is a node param2, which then will be converted to entity direction.
* `double` is a boolean value for whether the chest is double or not.
* `sound` is a prefix string, from which the actual sounds for the entity will
be concatenated.
* `mesh` is the same thing as `sound`, but for meshes.
* `shulker` is a boolean value for whether the chest is a shulker or not.
## `mcl_chests.player_chest_close(player)`
This function has to be called when a player closes a chest.
* `player` is an ObjectRef.
## `mcl_chests.chest_update_after_close(pos)`
This function is called when a chest is closed by `player_chest_close`.
* `pos` is the chest's position vector.
## `mcl_chests.is_not_shulker_box(stack)`
This function checks for whether `stack` is a shulker box, and returns `false`
if it is. Used internally to disallow putting shulker boxes into shulker boxes.
* `stack` is an ItemStack.

View File

@ -1,19 +0,0 @@
# `mcl_chests`
This mod adds normal and large chests, trapped chests, ender chests and
shulkers, providing an API for mods to register their own chests.
The API is documented in `API.md`.
## License of source code
Copyright (C) 2011-2012 celeron55, Perttu Ahola <celeron55@gmail.com>\
Copyright (C) 2024 rudzik8, Mikita Wiśniewski <rudzik8@protonmail.com>
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU Lesser General Public License as published by
the Free Software Foundation; either version 2.1 of the License, or
(at your option) any later version.
http://www.gnu.org/licenses/lgpl-2.1.html

View File

@ -1,965 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
local C = minetest.colorize
local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos
local string = string
local table = table
local sf = string.format
-- Recursively merge tables with eachother
local function table_merge(tbl, ...)
local t = table.copy(tbl)
for k,v in pairs(...) do
if type(t[k]) == "table" and type(v) == "table" then
table_merge(t[k], v)
else
t[k] = v
end
end
return t
end
-- Chest Entity
-- ------------
-- This is necessary to show the chest as an animated mesh, as Minetest doesn't support assigning animated meshes to
-- nodes directly. We're bypassing this limitation by giving each chest its own entity, and making the chest node
-- itself fully transparent.
local animated_chests = (minetest.settings:get_bool("animated_chests") ~= false)
local entity_animations = {
shulker = {
speed = 50,
open = { x = 45, y = 95 },
close = { x = 95, y = 145 },
},
chest = {
speed = 25,
open = { x = 0, y = 7 },
close = { x = 13, y = 20 },
},
}
minetest.register_entity("mcl_chests:chest", {
initial_properties = {
visual = "mesh",
pointable = false,
physical = false,
static_save = false,
},
set_animation = function(self, animname)
local anim_table = entity_animations[self.animation_type]
local anim = anim_table[animname]
if not anim then return end
self.object:set_animation(anim, anim_table.speed, 0, false)
end,
open = function(self, playername)
self.players[playername] = true
if not self.is_open then
self:set_animation("open")
minetest.sound_play(self.sound_prefix .. "_open", { pos = self.node_pos, gain = 0.5, max_hear_distance = 16 },
true)
self.is_open = true
end
end,
close = function(self, playername)
local playerlist = self.players
playerlist[playername] = nil
if self.is_open then
if next(playerlist) then
return
end
self:set_animation("close")
minetest.sound_play(self.sound_prefix .. "_close",
{ pos = self.node_pos, gain = 0.3, max_hear_distance = 16 },
true)
self.is_open = false
end
end,
initialize = function(self, node_pos, node_name, textures, dir, double, sound_prefix, mesh_prefix, animation_type)
self.node_pos = node_pos
self.node_name = node_name
self.sound_prefix = sound_prefix
self.animation_type = animation_type
local obj = self.object
obj:set_armor_groups({ immortal = 1 })
obj:set_properties({
textures = textures,
mesh = mesh_prefix .. (double and "_double" or "") .. ".b3d",
})
self:set_yaw(dir)
self.players = {}
end,
reinitialize = function(self, node_name)
self.node_name = node_name
end,
set_yaw = function(self, dir)
self.object:set_yaw(minetest.dir_to_yaw(dir))
end,
check = function(self)
local node_pos, node_name = self.node_pos, self.node_name
if not node_pos or not node_name then
return false
end
local node = minetest.get_node(node_pos)
if node.name ~= node_name then
return false
end
return true
end,
on_activate = function(self, initialization_data)
if initialization_data and initialization_data:find("\"###mcl_chests:chest###\"") then
self:initialize(unpack(minetest.deserialize(initialization_data)))
else
minetest.log("warning",
"[mcl_chests] on_activate called without proper initialization_data ... removing entity")
self.object:remove()
end
end,
on_step = function(self, dtime)
if not self:check() then
self.object:remove()
end
end
})
local function get_entity_pos(pos, dir, double)
pos = vector.copy(pos)
if double then
local add, mul, vec, cross = vector.add, vector.multiply, vector.new, vector.cross
pos = add(pos, mul(cross(dir, vec(0, 1, 0)), -0.5))
end
return pos
end
local function find_entity(pos)
for _, obj in pairs(minetest.get_objects_inside_radius(pos, 0)) do
local luaentity = obj:get_luaentity()
if luaentity and luaentity.name == "mcl_chests:chest" then
return luaentity
end
end
end
local function get_entity_info(pos, param2, double, dir, entity_pos)
dir = dir or minetest.facedir_to_dir(param2)
return dir, get_entity_pos(pos, dir, double)
end
local function create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir,
entity_pos)
dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos)
local initialization_data = minetest.serialize({pos, node_name, textures, dir, double, sound_prefix,
mesh_prefix, animation_type, "###mcl_chests:chest###"})
local obj = minetest.add_entity(entity_pos, "mcl_chests:chest", initialization_data)
if obj and obj:get_pos() then
return obj:get_luaentity()
else
minetest.log("warning", "[mcl_chests] Failed to create entity at " ..
(entity_pos and minetest.pos_to_string(entity_pos, 1) or "nil"))
end
end
mcl_chests.create_entity = create_entity
local function find_or_create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix,
animation_type, dir, entity_pos)
dir, entity_pos = get_entity_info(pos, param2, double, dir, entity_pos)
return find_entity(entity_pos) or
create_entity(pos, node_name, textures, param2, double, sound_prefix, mesh_prefix, animation_type, dir,
entity_pos)
end
mcl_chests.find_or_create_entity = find_or_create_entity
local function select_and_spawn_entity(pos, node)
local node_name = node.name
local node_def = minetest.registered_nodes[node_name]
local double_chest = minetest.get_item_group(node_name, "double_chest") > 0
find_or_create_entity(pos, node_name, node_def._chest_entity_textures, node.param2, double_chest,
node_def._chest_entity_sound, node_def._chest_entity_mesh, node_def._chest_entity_animation_type)
end
mcl_chests.select_and_spawn_entity = select_and_spawn_entity
local no_rotate, simple_rotate
if screwdriver then
no_rotate = screwdriver.disallow
simple_rotate = function(pos, node, user, mode, new_param2)
if screwdriver.rotate_simple(pos, node, user, mode, new_param2) ~= false then
local nodename = node.name
local nodedef = minetest.registered_nodes[nodename]
local dir = minetest.facedir_to_dir(new_param2)
find_or_create_entity(pos, nodename, nodedef._chest_entity_textures, new_param2, false,
nodedef._chest_entity_sound,
nodedef._chest_entity_mesh, nodedef._chest_entity_animation_type, dir):set_yaw(dir)
else
return false
end
end
end
mcl_chests.no_rotate, mcl_chests.simple_rotate = no_rotate, simple_rotate
-- List of open chests
-- -------------------
-- Key: Player name
-- Value:
-- If player is using a chest: { pos = <chest node position> }
-- Otherwise: nil
local open_chests = {}
mcl_chests.open_chests = open_chests
-- To be called if a player opened a chest
local function player_chest_open(player, pos, node_name, textures, param2, double, sound, mesh, shulker)
local name = player:get_player_name()
open_chests[name] = {
pos = pos,
node_name = node_name,
textures = textures,
param2 = param2,
double = double,
sound = sound,
mesh = mesh,
shulker = shulker
}
if animated_chests then
local dir = minetest.facedir_to_dir(param2)
find_or_create_entity(pos, node_name, textures, param2, double, sound, mesh,
shulker and "shulker" or "chest", dir):open(name)
end
end
mcl_chests.player_chest_open = player_chest_open
-- Simple protection checking functions
local function protection_check_move(pos, from_list, from_index, to_list, to_index, count, player)
local name = player:get_player_name()
if minetest.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return 0
else
return count
end
end
mcl_chests.protection_check_move = protection_check_move
local function protection_check_take(pos, listname, index, stack, player)
local name = player:get_player_name()
if minetest.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return 0
else
return stack:get_count()
end
end
mcl_chests.protection_check_put_take = protection_check_take
-- Logging functions
local function log_inventory_move(pos, from_list, from_index, to_list, to_index, count, player)
minetest.log("action", player:get_player_name() ..
" moves stuff to chest at " .. minetest.pos_to_string(pos))
end
local function log_inventory_put(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name() ..
" moves stuff to chest at " .. minetest.pos_to_string(pos))
-- BEGIN OF LISTRING WORKAROUND
if listname == "input" then
local inv = minetest.get_inventory({ type = "node", pos = pos })
inv:add_item("main", stack)
end
-- END OF LISTRING WORKAROUND
end
local function log_inventory_take(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name() ..
" takes stuff from chest at " .. minetest.pos_to_string(pos))
end
-- To be called when a chest is closed (only relevant for trapped chest atm)
local function chest_update_after_close(pos)
local node = minetest.get_node(pos)
if node.name == "mcl_chests:trapped_chest_on_small" then
minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_small", param2 = node.param2 })
find_or_create_entity(pos, "mcl_chests:trapped_chest_small", { "mcl_chests_trapped.png" }, node.param2, false,
"default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_small")
mesecon.receptor_off(pos, mesecon.rules.pplate)
elseif node.name == "mcl_chests:trapped_chest_on_left" then
minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 })
find_or_create_entity(pos, "mcl_chests:trapped_chest_left", mcl_chests.tiles.chest_trapped_double, node.param2, true,
"default_chest", "mcl_chests_chest", "chest"):reinitialize("mcl_chests:trapped_chest_left")
mesecon.receptor_off(pos, mesecon.rules.pplate)
local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left")
minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 })
mesecon.receptor_off(pos_other, mesecon.rules.pplate)
elseif node.name == "mcl_chests:trapped_chest_on_right" then
minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_right", param2 = node.param2 })
mesecon.receptor_off(pos, mesecon.rules.pplate)
local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right")
minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_left", param2 = node.param2 })
find_or_create_entity(pos_other, "mcl_chests:trapped_chest_left", mcl_chests.tiles.chest_trapped_double,
node.param2, true, "default_chest", "mcl_chests_chest", "chest")
:reinitialize("mcl_chests:trapped_chest_left")
mesecon.receptor_off(pos_other, mesecon.rules.pplate)
end
end
mcl_chests.chest_update_after_close = chest_update_after_close
-- To be called if a player closed a chest
local function player_chest_close(player)
local name = player:get_player_name()
local open_chest = open_chests[name]
if open_chest == nil then
return
end
if animated_chests then
find_or_create_entity(open_chest.pos, open_chest.node_name, open_chest.textures, open_chest.param2,
open_chest.double, open_chest.sound, open_chest.mesh, open_chest.shulker and "shulker" or "chest")
:close(name)
end
chest_update_after_close(open_chest.pos)
open_chests[name] = nil
end
mcl_chests.player_chest_close = player_chest_close
local function double_chest_add_item(top_inv, bottom_inv, listname, stack)
if not stack or stack:is_empty() then return end
local name = stack:get_name()
local function top_off(inv, stack)
for c, chest_stack in ipairs(inv:get_list(listname)) do
if stack:is_empty() then
break
end
if chest_stack:get_name() == name and chest_stack:get_free_space() > 0 then
stack = chest_stack:add_item(stack)
inv:set_stack(listname, c, chest_stack)
end
end
return stack
end
stack = top_off(top_inv, stack)
stack = top_off(bottom_inv, stack)
if not stack:is_empty() then
stack = top_inv:add_item(listname, stack)
if not stack:is_empty() then
bottom_inv:add_item(listname, stack)
end
end
end
local function on_chest_blast(pos)
local node = minetest.get_node(pos)
drop_items_chest(pos, node)
minetest.remove_node(pos)
end
local function limit_put_list(stack, list)
for _, other in ipairs(list) do
stack = other:add_item(stack)
if stack:is_empty() then
break
end
end
return stack
end
local function limit_put(stack, top_inv, bottom_inv)
local leftover = ItemStack(stack)
leftover = limit_put_list(leftover, top_inv:get_list("main"))
leftover = limit_put_list(leftover, bottom_inv:get_list("main"))
return stack:get_count() - leftover:get_count()
end
local function close_forms(canonical_basename, pos)
local players = minetest.get_connected_players()
for p = 1, #players do
if vector.distance(players[p]:get_pos(), pos) <= 30 then
minetest.close_formspec(players[p]:get_player_name(),
"mcl_chests:" .. canonical_basename .. "_" .. pos.x .. "_" .. pos.y .. "_" .. pos.z)
end
end
end
local function get_chest_inventories(pos, side)
local inv = minetest.get_inventory({ type = "node", pos = pos })
local node = minetest.get_node(pos)
local pos_other = get_double_container_neighbor_pos(pos, node.param2, side)
local inv_other = minetest.get_inventory({ type = "node", pos = pos_other })
local top_inv, bottom_inv
if side == "left" then
top_inv = inv
bottom_inv = inv_other
else
top_inv = inv_other
bottom_inv = inv
end
return top_inv, bottom_inv
end
-- Functions used in double chest registration code
-- ------------------------------------------------
-- The `return function` wrapping is necessary to avoid stacking up parameters.
-- `side` is either "left" or "right".
local function hopper_pull_double(side) return function(pos, hop_pos, hop_inv, hop_list)
local top_inv, bottom_inv = get_chest_inventories(pos, side)
local stack_id = mcl_util.select_stack(top_inv, "main", hop_inv, hop_list)
if stack_id ~= nil then
return top_inv, "main", stack_id
end
stack_id = mcl_util.select_stack(bottom_inv, "main", hop_inv, hop_list)
return bottom_inv, "main", stack_id
end end
local function hopper_push_double(side) return function(pos, hop_pos, hop_inv, hop_list)
local top_inv, bottom_inv = get_chest_inventories(pos, side)
local stack_id = mcl_util.select_stack(hop_inv, hop_list, top_inv, "main", nil, 1)
if stack_id ~= nil then
return top_inv, "main", stack_id
end
stack_id = mcl_util.select_stack(hop_inv, hop_list, bottom_inv, "main", nil, 1)
return bottom_inv, "main", stack_id
end end
local function construct_double_chest(side, names) return function(pos)
local n = minetest.get_node(pos)
local param2 = n.param2
local p = get_double_container_neighbor_pos(pos, param2, side)
-- Turn into a small chest if the neighbor is gone
if not p or minetest.get_node(p).name ~= names[side].cr then
n.name = names.small.a
minetest.swap_node(pos, n)
end
end end
local function destruct_double_chest(side, names, canonical_basename, small_textures, sound_prefix) return function(pos)
local n = minetest.get_node(pos)
if n.name == names.small.a then
return
end
close_forms(canonical_basename, pos)
local param2 = n.param2
local p = get_double_container_neighbor_pos(pos, param2, side)
if not p or minetest.get_node(p).name ~= names[side].r then
return
end
close_forms(canonical_basename, p)
minetest.swap_node(p, { name = names.small.a, param2 = param2 })
create_entity(p, names.small.a, small_textures, param2, false, sound_prefix, "mcl_chests_chest", "chest")
end end
-- Small chests use `protection_check_take` for both put and take actions.
local function protection_check_put(side) return function(pos, listname, index, stack, player)
local name = player:get_player_name()
if minetest.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return 0
-- BEGIN OF LISTRING WORKAROUND
elseif listname == "input" then
local top_inv, bottom_inv = get_chest_inventories(pos, side)
return limit_put(stack, top_inv, bottom_inv)
-- END OF LISTRING WORKAROUND
else
return stack:get_count()
end
end end
local function log_inventory_put_double(side) return function(pos, listname, index, stack, player)
minetest.log("action", player:get_player_name() ..
" moves stuff to chest at " .. minetest.pos_to_string(pos))
-- BEGIN OF LISTRING WORKAROUND
if listname == "input" then
local top_inv, bottom_inv = get_chest_inventories(pos, side)
top_inv:set_stack("input", 1, nil)
bottom_inv:set_stack("input", 1, nil)
double_chest_add_item(top_inv, bottom_inv, "main", stack)
end
-- END OF LISTRING WORKAROUND
end end
-- This is a helper function to register regular chests (both small and double variants).
-- Some parameters here are only utilized by trapped chests.
function mcl_chests.register_chest(basename, d)
-- If this passes without crash, we know for a fact that d = {...}
assert((d and type(d) == "table"), "Second argument to mcl_chests.register_chest must be a table")
-- Fallback for when there is no `title` field
if not d.title then d.title = {} end
d.title.small = d.title.small or d.desc
d.title.double = d.title.double or ("Large " .. d.title.small)
if not d.drop then
d.drop = "mcl_chests:" .. basename
else
d.drop = "mcl_chests:" .. d.drop
end
local drop_items_chest = mcl_util.drop_items_from_meta_container("main")
if not d.groups then d.groups = {} end
if not d.on_rightclick_left then
d.on_rightclick_left = d.on_rightclick
end
if not d.on_rightclick_right then
d.on_rightclick_right = d.on_rightclick
end
--[[local on_rightclick_side = {
left = d.on_rightclick_left or d.on_rightclick,
right = d.on_rightclick_right or d.on_rightclick,
}]]
if not d.sounds or type(d.sounds) ~= "table" then
d.sounds = { nil, "default_chest" }
end
if not d.sounds[2] then
d.sounds[2] = "default_chest"
end
-- The basename of the "canonical" version of the node, if set (e.g.: trapped_chest_on → trapped_chest).
-- Used to get a shared formspec ID and to swap the node back to the canonical version in on_construct.
if not d.canonical_basename then
d.canonical_basename = basename
end
-- Names table
-- -----------
-- Accessed through names["kind"].x (names.kind.x), where x can be:
-- a = "actual"
-- c = canonical
-- r = reverse (only for double chests)
-- cr = canonical, reverse (only for double chests)
local names = {
small = {
a = "mcl_chests:" .. basename .. "_small",
c = "mcl_chests:" .. d.canonical_basename .. "_small",
},
left = {
a = "mcl_chests:" .. basename .. "_left",
c = "mcl_chests:" .. d.canonical_basename .. "_left",
},
right = {
a = "mcl_chests:" .. basename .. "_right",
c = "mcl_chests:" .. d.canonical_basename .. "_right",
},
}
names.left.r = names.right.a
names.right.r = names.left.a
names.left.cr = names.right.c
names.right.cr = names.left.c
local small_textures = d.tiles.small
local double_textures = d.tiles.double
-- Construct groups
local groups_inv = table_merge({ deco_block = 1 }, d.groups)
local groups_small = table_merge(groups_inv, {
container = 2,
deco_block = 1,
chest_entity = 1,
not_in_creative_inventory = 1
}, d.groups)
local groups_left = table_merge(groups_small, {
double_chest = 1
}, d.groups)
local groups_right = table_merge(groups_small, {
-- In a double chest, the entity is assigned to the left side, but not the right one.
chest_entity = 0,
double_chest = 2
}, d.groups)
-- Dummy inventory node
-- Will turn into names.small.a when placed down
minetest.register_node("mcl_chests:" .. basename, {
description = d.desc,
_tt_help = d.tt_help,
_doc_items_longdesc = d.longdesc,
_doc_items_usagehelp = d.usagehelp,
_doc_items_hidden = d.hidden,
drawtype = "mesh",
mesh = "mcl_chests_chest.b3d",
tiles = small_textures,
use_texture_alpha = "opaque",
paramtype = "light",
paramtype2 = "facedir",
sounds = d.sounds[1],
groups = groups_inv,
on_construct = function(pos, node)
local node = minetest.get_node(pos)
node.name = names.small.a
minetest.set_node(pos, node)
end,
after_place_node = function(pos, placer, itemstack, pointed_thing)
minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name"))
end,
})
minetest.register_node(names.small.a, {
description = d.desc,
_tt_help = d.tt_help,
_doc_items_longdesc = d.longdesc,
_doc_items_usagehelp = d.usagehelp,
_doc_items_hidden = d.hidden,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = { -0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375 },
},
tiles = { "blank.png^[resize:16x16" },
use_texture_alpha = "clip",
_chest_entity_textures = small_textures,
_chest_entity_sound = d.sounds[2],
_chest_entity_mesh = "mcl_chests_chest",
_chest_entity_animation_type = "chest",
paramtype = "light",
paramtype2 = "facedir",
drop = d.drop,
groups = groups_small,
is_ground_content = false,
sounds = d.sounds[1],
on_construct = function(pos)
local param2 = minetest.get_node(pos).param2
local meta = minetest.get_meta(pos)
--[[ This is a workaround for Minetest issue 5894
<https://github.com/minetest/minetest/issues/5894>.
Apparently if we don't do this, large chests initially don't work when
placed at chunk borders, and some chests randomly don't work after
placing. ]]
-- FIXME: Remove this workaround when the bug has been fixed.
-- BEGIN OF WORKAROUND --
meta:set_string("workaround", "ignore_me")
meta:set_string("workaround", "") -- Done to keep metadata clean
-- END OF WORKAROUND --
local inv = meta:get_inventory()
inv:set_size("main", 9 * 3)
--[[ The "input" list is *another* workaround (hahahaha!) around the fact that Minetest
does not support listrings to put items into an alternative list if the first one
happens to be full. See <https://github.com/minetest/minetest/issues/5343>.
This list is a hidden input-only list and immediately puts items into the appropriate chest.
It is only used for listrings and hoppers. This workaround is not that bad because it only
requires a simple inventory allows check for large chests.]]
-- FIXME: Refactor the listrings as soon Minetest supports alternative listrings
-- BEGIN OF LISTRING WORKAROUND
inv:set_size("input", 1)
-- END OF LISTRING WORKAROUND
-- Combine into a double chest if neighbouring another small chest
if minetest.get_node(get_double_container_neighbor_pos(pos, param2, "right")).name ==
names.small.a then
minetest.swap_node(pos, { name = names.right.a, param2 = param2 })
local p = get_double_container_neighbor_pos(pos, param2, "right")
minetest.swap_node(p, { name = names.left.a, param2 = param2 })
create_entity(p, names.left.a, double_textures, param2, true, d.sounds[2],
"mcl_chests_chest", "chest")
elseif minetest.get_node(get_double_container_neighbor_pos(pos, param2, "left")).name ==
names.small.a then
minetest.swap_node(pos, { name = names.left.a, param2 = param2 })
create_entity(pos, names.left.a, double_textures, param2, true, d.sounds[2],
"mcl_chests_chest", "chest")
local p = get_double_container_neighbor_pos(pos, param2, "left")
minetest.swap_node(p, { name = names.right.a, param2 = param2 })
else
minetest.swap_node(pos, { name = names.small.a, param2 = param2 })
create_entity(pos, names.small.a, small_textures, param2, false, d.sounds[2],
"mcl_chests_chest", "chest")
end
end,
after_place_node = function(pos, placer, itemstack, pointed_thing)
minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name"))
end,
after_dig_node = drop_items_chest,
on_blast = on_chest_blast,
allow_metadata_inventory_move = protection_check_move,
allow_metadata_inventory_take = protection_check_take,
allow_metadata_inventory_put = protection_check_take,
on_metadata_inventory_move = log_inventory_move,
on_metadata_inventory_put = log_inventory_put,
on_metadata_inventory_take = log_inventory_take,
_mcl_blast_resistance = d.hardness,
_mcl_hardness = d.hardness,
on_rightclick = function(pos, node, clicker)
local topnode = minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z })
if topnode and topnode.name and minetest.registered_nodes[topnode.name] then
if minetest.registered_nodes[topnode.name].groups.opaque == 1 then
-- won't open if there is no space from the top
return false
end
end
local name = minetest.get_meta(pos):get_string("name")
if name == "" then
name = d.title.small
end
minetest.show_formspec(clicker:get_player_name(),
sf("mcl_chests:%s_%s_%s_%s", d.canonical_basename, pos.x, pos.y, pos.z),
table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3),
sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos.x, pos.y, pos.z),
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z),
"listring[current_player;main]",
})
)
if d.on_rightclick then
d.on_rightclick(pos, node, clicker)
end
player_chest_open(clicker, pos, names.small.a, small_textures, node.param2, false, d.sounds[2],
"mcl_chests_chest")
end,
on_destruct = function(pos)
close_forms(d.canonical_basename, pos)
end,
mesecons = d.mesecons,
on_rotate = simple_rotate,
})
minetest.register_node(names.left.a, {
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = { -0.4375, -0.5, -0.4375, 0.5, 0.375, 0.4375 },
},
tiles = { "blank.png^[resize:16x16" },
use_texture_alpha = "clip",
_chest_entity_textures = double_textures,
_chest_entity_sound = d.sounds[2],
_chest_entity_mesh = "mcl_chests_chest",
_chest_entity_animation_type = "chest",
paramtype = "light",
paramtype2 = "facedir",
groups = groups_left,
drop = d.drop,
is_ground_content = false,
sounds = d.sounds[1],
on_construct = construct_double_chest("left", names),
after_place_node = function(pos, placer, itemstack, pointed_thing)
minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name"))
end,
on_destruct = destruct_double_chest("left", names, d.canonical_basename, small_textures, d.sounds[2]),
after_dig_node = drop_items_chest,
on_blast = on_chest_blast,
allow_metadata_inventory_move = protection_check_move,
allow_metadata_inventory_take = protection_check_take,
allow_metadata_inventory_put = protection_check_put("left"),
on_metadata_inventory_move = log_inventory_move,
on_metadata_inventory_put = log_inventory_put_double("left"),
on_metadata_inventory_take = log_inventory_take,
_mcl_blast_resistance = d.hardness,
_mcl_hardness = d.hardness,
on_rightclick = function(pos, node, clicker)
local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left")
local above_def = minetest.registered_nodes[
minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name
]
local above_def_other = minetest.registered_nodes[
minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name
]
if (not above_def or above_def.groups.opaque == 1 or not above_def_other
or above_def_other.groups.opaque == 1) then
-- won't open if there is no space from the top
return false
end
local name = minetest.get_meta(pos):get_string("name")
if name == "" then -- if empty after that ^
name = minetest.get_meta(pos_other):get_string("name")
end if name == "" then -- if STILL empty after that ^
name = d.title.double
end
minetest.show_formspec(clicker:get_player_name(),
sf("mcl_chests:%s_%s_%s_%s", d.canonical_basename, pos.x, pos.y, pos.z),
table.concat({
"formspec_version[4]",
"size[11.75,14.15]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3),
sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos.x, pos.y, pos.z),
mcl_formspec.get_itemslot_bg_v4(0.375, 4.5, 9, 3),
sf("list[nodemeta:%s,%s,%s;main;0.375,4.5;9,3;]", pos_other.x, pos_other.y, pos_other.z),
"label[0.375,8.45;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 8.825, 9, 3),
"list[current_player;main;0.375,8.825;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 12.775, 9, 1),
"list[current_player;main;0.375,12.775;9,1;]",
--BEGIN OF LISTRING WORKAROUND
"listring[current_player;main]",
sf("listring[nodemeta:%s,%s,%s;input]", pos.x, pos.y, pos.z),
--END OF LISTRING WORKAROUND
"listring[current_player;main]" ..
sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z),
"listring[current_player;main]",
sf("listring[nodemeta:%s,%s,%s;main]", pos_other.x, pos_other.y, pos_other.z),
})
)
if d.on_rightclick_left then
d.on_rightclick_left(pos, node, clicker)
end
player_chest_open(clicker, pos, names.left.a, double_textures, node.param2, true, d.sounds[2],
"mcl_chests_chest")
end,
mesecons = d.mesecons,
on_rotate = no_rotate,
_mcl_hoppers_on_try_pull = hopper_pull_double("left"),
_mcl_hoppers_on_try_push = hopper_push_double("left"),
})
minetest.register_node(names.right.a, {
drawtype = "nodebox",
paramtype = "light",
paramtype2 = "facedir",
node_box = {
type = "fixed",
fixed = { -0.5, -0.5, -0.4375, 0.4375, 0.375, 0.4375 },
},
tiles = { "blank.png^[resize:16x16" },
use_texture_alpha = "clip",
groups = groups_right,
drop = d.drop,
is_ground_content = false,
sounds = d.sounds[1],
on_construct = construct_double_chest("right", names),
after_place_node = function(pos, placer, itemstack, pointed_thing)
minetest.get_meta(pos):set_string("name", itemstack:get_meta():get_string("name"))
end,
on_destruct = destruct_double_chest("right", names, d.canonical_basename, small_textures, d.sounds[2]),
after_dig_node = drop_items_chest,
on_blast = on_chest_blast,
allow_metadata_inventory_move = protection_check_move,
allow_metadata_inventory_take = protection_check_take,
allow_metadata_inventory_put = protection_check_put("right"),
on_metadata_inventory_move = log_inventory_move,
on_metadata_inventory_put = log_inventory_put_double("right"),
on_metadata_inventory_take = log_inventory_take,
_mcl_blast_resistance = d.hardness,
_mcl_hardness = d.hardness,
on_rightclick = function(pos, node, clicker)
local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right")
local above_def = minetest.registered_nodes[
minetest.get_node({ x = pos.x, y = pos.y + 1, z = pos.z }).name
]
local above_def_other = minetest.registered_nodes[
minetest.get_node({ x = pos_other.x, y = pos_other.y + 1, z = pos_other.z }).name
]
if (not above_def or above_def.groups.opaque == 1 or not above_def_other
or above_def_other.groups.opaque == 1) then
-- won't open if there is no space from the top
return false
end
local name = minetest.get_meta(pos):get_string("name")
if name == "" then -- if empty after that ^
name = minetest.get_meta(pos_other):get_string("name")
end if name == "" then -- if STILL empty after that ^
name = d.title.double
end
minetest.show_formspec(clicker:get_player_name(),
sf("mcl_chests:%s_%s_%s_%s", d.canonical_basename, pos.x, pos.y, pos.z),
table.concat({
"formspec_version[4]",
"size[11.75,14.15]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3),
sf("list[nodemeta:%s,%s,%s;main;0.375,0.75;9,3;]", pos_other.x, pos_other.y, pos_other.z),
mcl_formspec.get_itemslot_bg_v4(0.375, 4.5, 9, 3),
sf("list[nodemeta:%s,%s,%s;main;0.375,4.5;9,3;]", pos.x, pos.y, pos.z),
"label[0.375,8.45;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 8.825, 9, 3),
"list[current_player;main;0.375,8.825;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 12.775, 9, 1),
"list[current_player;main;0.375,12.775;9,1;]",
--BEGIN OF LISTRING WORKAROUND
"listring[current_player;main]",
sf("listring[nodemeta:%s,%s,%s;input]", pos.x, pos.y, pos.z),
--END OF LISTRING WORKAROUND
"listring[current_player;main]" ..
sf("listring[nodemeta:%s,%s,%s;main]", pos_other.x, pos_other.y, pos_other.z),
"listring[current_player;main]",
sf("listring[nodemeta:%s,%s,%s;main]", pos.x, pos.y, pos.z),
})
)
if d.on_rightclick_right then
d.on_rightclick_right(pos, node, clicker)
end
player_chest_open(clicker, pos_other, names.left.a, double_textures, node.param2, true, d.sounds[2],
"mcl_chests_chest")
end,
mesecons = d.mesecons,
on_rotate = no_rotate,
_mcl_hoppers_on_try_pull = hopper_pull_double("right"),
_mcl_hoppers_on_try_push = hopper_push_double("right"),
})
if doc then
doc.add_entry_alias("nodes", names.small.a, "nodes", names.left.a)
doc.add_entry_alias("nodes", names.small.a, "nodes", names.right.a)
end
end
-- Returns false if itemstack is a shulker box
function mcl_chests.is_not_shulker_box(stack)
local g = minetest.get_item_group(stack:get_name(), "shulker_box")
return g == 0 or g == nil
end

View File

@ -1,165 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
local get_double_container_neighbor_pos = mcl_util.get_double_container_neighbor_pos
local chestusage = S("To access its inventory, rightclick it. When broken, the items will drop out.")
mcl_chests.register_chest("chest", {
desc = S("Chest"),
longdesc = S(
"Chests are containers which provide 27 inventory slots. Chests can be turned into large chests with " ..
"double the capacity by placing two chests next to each other."
),
usagehelp = chestusage,
tt_help = S("27 inventory slots") .. "\n" .. S("Can be combined to a large chest"),
tiles = {
small = mcl_chests.tiles.chest_normal_small,
double = mcl_chests.tiles.chest_normal_double,
inv = { "default_chest_top.png", "mcl_chests_chest_bottom.png",
"mcl_chests_chest_right.png", "mcl_chests_chest_left.png",
"mcl_chests_chest_back.png", "default_chest_front.png" },
},
groups = {
handy = 1,
axey = 1,
material_wood = 1,
flammable = -1,
},
sounds = { mcl_sounds.node_sound_wood_defaults() },
hardness = 2.5,
hidden = false,
})
local traptiles = {
small = mcl_chests.tiles.chest_trapped_small,
double = mcl_chests.tiles.chest_trapped_double,
}
mcl_chests.register_chest("trapped_chest", {
desc = S("Trapped Chest"),
title = {
small = S("Chest"),
double = S("Large Chest")
},
longdesc = S(
"A trapped chest is a container which provides 27 inventory slots. When it is opened, it sends a redstone " ..
"signal to its adjacent blocks as long it stays open. Trapped chests can be turned into large trapped " ..
"chests with double the capacity by placing two trapped chests next to each other."
),
usagehelp = chestusage,
tt_help = S("27 inventory slots") ..
"\n" .. S("Can be combined to a large chest") .. "\n" .. S("Emits a redstone signal when opened"),
tiles = traptiles,
groups = {
handy = 1,
axey = 1,
material_wood = 1,
flammable = -1,
mesecon = 2,
},
sounds = { mcl_sounds.node_sound_wood_defaults() },
hardness = 2.5,
hidden = false,
mesecons = {
receptor = {
state = mesecon.state.off,
rules = mesecon.rules.pplate,
},
},
on_rightclick = function(pos, node, clicker)
minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_small", param2 = node.param2 })
mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_small", { "mcl_chests_trapped.png" },
node.param2, false, "default_chest", "mcl_chests_chest", "chest")
:reinitialize("mcl_chests:trapped_chest_on_small")
mesecon.receptor_on(pos, mesecon.rules.pplate)
end,
on_rightclick_left = function(pos, node, clicker)
local meta = minetest.get_meta(pos)
meta:set_int("players", 1)
minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 })
mcl_chests.find_or_create_entity(pos, "mcl_chests:trapped_chest_on_left",
mcl_chests.tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest",
"chest"):reinitialize("mcl_chests:trapped_chest_on_left")
mesecon.receptor_on(pos, mesecon.rules.pplate)
local pos_other = get_double_container_neighbor_pos(pos, node.param2, "left")
minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 })
mesecon.receptor_on(pos_other, mesecon.rules.pplate)
end,
on_rightclick_right = function(pos, node, clicker)
local pos_other = get_double_container_neighbor_pos(pos, node.param2, "right")
minetest.swap_node(pos, { name = "mcl_chests:trapped_chest_on_right", param2 = node.param2 })
mesecon.receptor_on(pos, mesecon.rules.pplate)
minetest.swap_node(pos_other, { name = "mcl_chests:trapped_chest_on_left", param2 = node.param2 })
mcl_chests.find_or_create_entity(pos_other, "mcl_chests:trapped_chest_on_left",
mcl_chests.tiles.chest_trapped_double, node.param2, true, "default_chest", "mcl_chests_chest",
"chest"):reinitialize("mcl_chests:trapped_chest_on_left")
mesecon.receptor_on(pos_other, mesecon.rules.pplate)
end
})
mcl_chests.register_chest("trapped_chest_on", {
title = {
small = S("Chest"),
double = S("Large Chest")
},
tiles = traptiles,
groups = {
handy = 1,
axey = 1,
material_wood = 1,
flammable = -1,
mesecon = 2,
},
sounds = { mcl_sounds.node_sound_wood_defaults() },
hardness = 2.5,
hidden = true,
mesecons = {
receptor = {
state = mesecon.state.on,
rules = mesecon.rules.pplate,
},
},
drop = "trapped_chest",
canonical_basename = "trapped_chest"
})
minetest.register_craft({
output = "mcl_chests:chest",
recipe = {
{ "group:wood", "group:wood", "group:wood" },
{ "group:wood", "", "group:wood" },
{ "group:wood", "group:wood", "group:wood" },
},
})
minetest.register_craft({
type = "fuel",
recipe = "mcl_chests:chest",
burntime = 15,
})
minetest.register_craft({
type = "fuel",
recipe = "mcl_chests:trapped_chest",
burntime = 15,
})
-- Disable active/open trapped chests when loaded because nobody could have them open at loading time.
-- Fixes redstone weirdness.
minetest.register_lbm({
label = "Disable active trapped chests",
name = "mcl_chests:reset_trapped_chests",
nodenames = {
"mcl_chests:trapped_chest_on_small",
"mcl_chests:trapped_chest_on_left",
"mcl_chests:trapped_chest_on_right"
},
run_at_every_load = true,
action = function(pos, node)
minetest.log("action", "[mcl_chests] Disabled active trapped chest on load: " .. minetest.pos_to_string(pos))
mcl_chests.chest_update_after_close(pos)
end,
})

View File

@ -1,138 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
local C = minetest.colorize
local longdesc = S(
"Ender chests grant you access to a single personal interdimensional inventory with 27 slots. This " ..
"inventory is the same no matter from which ender chest you access it from. If you put one item into one " ..
"ender chest, you will find it in all other ender chests. Each player will only see their own items, but " ..
"not the items of other players."
)
minetest.register_node("mcl_chests:ender_chest", {
description = S("Ender Chest"),
_tt_help = S("27 interdimensional inventory slots") ..
"\n" .. S("Put items inside, retrieve them from any ender chest"),
_doc_items_longdesc = longdesc,
_doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."),
drawtype = "mesh",
mesh = "mcl_chests_chest.b3d",
tiles = mcl_chests.tiles.chest_ender_small,
use_texture_alpha = "opaque",
paramtype = "light",
paramtype2 = "facedir",
groups = { deco_block = 1 },
sounds = mcl_sounds.node_sound_stone_defaults(),
on_construct = function(pos)
local node = minetest.get_node(pos)
node.name = "mcl_chests:ender_chest_small"
minetest.set_node(pos, node)
end,
})
local formspec_ender_chest = table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, S("Ender Chest"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3),
"list[current_player;enderchest;0.375,0.75;9,3;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[current_player;enderchest]",
"listring[current_player;main]",
})
minetest.register_node("mcl_chests:ender_chest_small", {
description = S("Ender Chest"),
_tt_help = S("27 interdimensional inventory slots") ..
"\n" .. S("Put items inside, retrieve them from any ender chest"),
_doc_items_longdesc = longdesc,
_doc_items_usagehelp = S("Rightclick the ender chest to access your personal interdimensional inventory."),
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = { -0.4375, -0.5, -0.4375, 0.4375, 0.375, 0.4375 },
},
_chest_entity_textures = mcl_chests.tiles.ender_chest_texture,
_chest_entity_sound = "mcl_chests_enderchest",
_chest_entity_mesh = "mcl_chests_chest",
_chest_entity_animation_type = "chest",
tiles = { "blank.png^[resize:16x16" },
use_texture_alpha = "clip",
-- Note: The “container” group is missing here because the ender chest does not
-- have an inventory on its own
groups = { pickaxey = 1, deco_block = 1, material_stone = 1, chest_entity = 1, not_in_creative_inventory = 1 },
is_ground_content = false,
paramtype = "light",
light_source = 7,
paramtype2 = "facedir",
sounds = mcl_sounds.node_sound_stone_defaults(),
drop = "mcl_core:obsidian 8",
on_construct = function(pos)
mcl_chests.create_entity(pos, "mcl_chests:ender_chest_small", mcl_chests.tiles.ender_chest_texture,
minetest.get_node(pos).param2, false, "mcl_chests_enderchest", "mcl_chests_chest", "chest")
end,
on_rightclick = function(pos, node, clicker)
if minetest.registered_nodes[minetest.get_node(vector.offset(pos, 0, 1, 0)).name].groups.opaque == 1 then
-- won't open if there is no space from the top
return false
end
minetest.show_formspec(clicker:get_player_name(), "mcl_chests:ender_chest_" .. clicker:get_player_name(),
formspec_ender_chest)
mcl_chests.player_chest_open(clicker, pos, "mcl_chests:ender_chest_small",
mcl_chests.tiles.ender_chest_texture, node.param2, false, "mcl_chests_enderchest",
"mcl_chests_chest")
end,
on_receive_fields = function(pos, formname, fields, sender)
if fields.quit then
mcl_chests.player_chest_close(sender)
end
end,
_mcl_blast_resistance = 3000,
_mcl_hardness = 22.5,
_mcl_silk_touch_drop = { "mcl_chests:ender_chest" },
on_rotate = mcl_chests.simple_rotate,
})
minetest.register_on_joinplayer(function(player)
local inv = player:get_inventory()
inv:set_size("enderchest", 9 * 3)
end)
minetest.register_allow_player_inventory_action(function(player, action, inv, info)
if inv:get_location().type == "player" and (
action == "move" and (info.from_list == "enderchest" or info.to_list == "enderchest")
or action == "put" and info.listname == "enderchest"
or action == "take" and info.listname == "enderchest") then
local def = player:get_wielded_item():get_definition()
local range = (def and def.range or player:get_inventory():get_stack("hand", 1):get_definition().range) + 1
if not minetest.find_node_near(player:get_pos(), range, "mcl_chests:ender_chest_small", true) then
return 0
end
end
end)
minetest.register_craft({
output = "mcl_chests:ender_chest",
recipe = {
{ "mcl_core:obsidian", "mcl_core:obsidian", "mcl_core:obsidian" },
{ "mcl_core:obsidian", "mcl_end:ender_eye", "mcl_core:obsidian" },
{ "mcl_core:obsidian", "mcl_core:obsidian", "mcl_core:obsidian" },
},
})
minetest.register_lbm({
label = "Upgrade old ender chest formspec",
name = "mcl_chests:replace_old_ender_form",
nodenames = { "mcl_chests:ender_chest_small" },
run_at_every_load = false,
action = function(pos, node)
minetest.get_meta(pos):set_string("formspec", "")
end,
})

View File

@ -1,50 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
mcl_chests.register_chest("stone_chest", {
desc = S("Stone Chest"),
title = {
small = S("Stone Chest"),
double = S("Large Stone Chest")
},
longdesc = S(
"Stone Chests are containers which provide 27 inventory slots. Stone Chests can be turned into" ..
"large stone chests with double the capacity by placing two stone chests next to each other."
),
usagehelp = S("To access its inventory, rightclick it. When broken, the items will drop out."),
tt_help = S("27 inventory slots") .. "\n" .. S("Can be combined to a large stone chest"),
tiles = {
small = { mcl_chests.tiles.chest_normal_small[1] .. "^[hsl:-15:-80:-20" },
double = { mcl_chests.tiles.chest_normal_double[1] .. "^[hsl:-15:-80:-20" },
inv = { "default_chest_top.png^[hsl:-15:-80:-20",
"mcl_chests_chest_bottom.png^[hsl:-15:-80:-20",
"mcl_chests_chest_right.png^[hsl:-15:-80:-20",
"mcl_chests_chest_left.png^[hsl:-15:-80:-20",
"mcl_chests_chest_back.png^[hsl:-15:-80:-20",
"default_chest_front.png^[hsl:-15:-80:-20"
},
},
groups = {
pickaxey = 1,
stone = 1,
material_stone = 1,
},
sounds = {
mcl_sounds.node_sound_stone_defaults(),
"mcl_chests_enderchest"
},
hardness = 4.0,
hidden = false,
-- It bites!
on_rightclick = function(pos, node, clicker)
mcl_util.deal_damage(clicker, 2)
end,
})
minetest.register_craft({
output = "mcl_chests:stone_chest",
recipe = {
{ "mcl_core:stone", "mcl_core:stone", "mcl_core:stone" },
{ "mcl_core:stone", "", "mcl_core:stone" },
{ "mcl_core:stone", "mcl_core:stone", "mcl_core:stone" },
},
})

File diff suppressed because it is too large Load Diff

View File

@ -1,358 +0,0 @@
local S = minetest.get_translator(minetest.get_current_modname())
local F = minetest.formspec_escape
local C = minetest.colorize
-- Shulker boxes
local boxtypes = {
white = S("White Shulker Box"),
grey = S("Light Grey Shulker Box"),
orange = S("Orange Shulker Box"),
cyan = S("Cyan Shulker Box"),
magenta = S("Magenta Shulker Box"),
violet = S("Purple Shulker Box"),
lightblue = S("Light Blue Shulker Box"),
blue = S("Blue Shulker Box"),
yellow = S("Yellow Shulker Box"),
brown = S("Brown Shulker Box"),
green = S("Lime Shulker Box"),
dark_green = S("Green Shulker Box"),
pink = S("Pink Shulker Box"),
red = S("Red Shulker Box"),
dark_grey = S("Grey Shulker Box"),
black = S("Black Shulker Box"),
}
local shulker_mob_textures = {
white = "mobs_mc_shulker_white.png",
grey = "mobs_mc_shulker_silver.png",
orange = "mobs_mc_shulker_orange.png",
cyan = "mobs_mc_shulker_cyan.png",
magenta = "mobs_mc_shulker_magenta.png",
violet = "mobs_mc_shulker_purple.png",
lightblue = "mobs_mc_shulker_light_blue.png",
blue = "mobs_mc_shulker_blue.png",
yellow = "mobs_mc_shulker_yellow.png",
brown = "mobs_mc_shulker_brown.png",
green = "mobs_mc_shulker_lime.png",
dark_green = "mobs_mc_shulker_green.png",
pink = "mobs_mc_shulker_pink.png",
red = "mobs_mc_shulker_red.png",
dark_grey = "mobs_mc_shulker_gray.png",
black = "mobs_mc_shulker_black.png",
}
local canonical_shulker_color = "violet"
local normal_canonical_name = "mcl_chests:" .. canonical_shulker_color .. "_shulker_box"
local small_canonical_name = normal_canonical_name .. "_small"
--WARNING: after formspec v4 update, old shulker boxes will need to be placed again to get the new formspec
local function formspec_shulker_box(name)
if not name or name == "" then
name = S("Shulker Box")
end
return table.concat({
"formspec_version[4]",
"size[11.75,10.425]",
"label[0.375,0.375;" .. F(C(mcl_formspec.label_color, name)) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 0.75, 9, 3),
"list[context;main;0.375,0.75;9,3;]",
"label[0.375,4.7;" .. F(C(mcl_formspec.label_color, S("Inventory"))) .. "]",
mcl_formspec.get_itemslot_bg_v4(0.375, 5.1, 9, 3),
"list[current_player;main;0.375,5.1;9,3;9]",
mcl_formspec.get_itemslot_bg_v4(0.375, 9.05, 9, 1),
"list[current_player;main;0.375,9.05;9,1;]",
"listring[context;main]",
"listring[current_player;main]",
})
end
local function set_shulkerbox_meta(nmeta, imeta)
local name = imeta:get_string("name")
nmeta:set_string("description", imeta:get_string("description"))
nmeta:set_string("name", name)
nmeta:set_string("formspec", formspec_shulker_box(name))
end
for color, desc in pairs(boxtypes) do
local mob_texture = shulker_mob_textures[color]
local is_canonical = color == canonical_shulker_color
local longdesc, usagehelp, create_entry, entry_name
if doc then
if is_canonical then
longdesc = S(
"A shulker box is a portable container which provides 27 inventory slots for any item " ..
"except shulker boxes. Shulker boxes keep their inventory when broken, so shulker boxes " ..
"as well as their contents can be taken as a single item. Shulker boxes come in many " ..
"different colors."
)
usagehelp = S(
"To access the inventory of a shulker box, place and right-click it. To take a shulker " ..
"box and its contents with you, just break and collect it, the items will not fall out. " ..
"Place the shulker box again to be able to retrieve its contents."
)
entry_name = S("Shulker Box")
else
create_entry = false
end
end
local normal_name = "mcl_chests:" .. color .. "_shulker_box"
local small_name = normal_name .. "_small"
minetest.register_node(normal_name, {
description = desc,
_tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"),
_doc_items_create_entry = create_entry,
_doc_items_entry_name = entry_name,
_doc_items_longdesc = longdesc,
_doc_items_usagehelp = usagehelp,
tiles = { mob_texture },
use_texture_alpha = "opaque",
drawtype = "mesh",
mesh = "mcl_chests_shulker.b3d",
groups = {
handy = 1,
pickaxey = 1,
container = 2,
deco_block = 1,
dig_by_piston = 1,
shulker_box = 1,
old_shulker_box_node = 1
},
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
stack_max = 1,
drop = "",
paramtype = "light",
paramtype2 = "facedir",
on_construct = function(pos)
local node = minetest.get_node(pos)
node.name = small_name
minetest.set_node(pos, node)
end,
after_place_node = function(pos, placer, itemstack, pointed_thing)
local nmeta = minetest.get_meta(pos)
local imeta = itemstack:get_meta()
-- Convert old itemstacks to not use get_metadata()
if not imeta:contains("inv") and
(itemstack:get_metadata() ~= "") then
imeta:set_string("inv", itemstack:get_metadata())
itemstack:set_metadata("") -- clear
end
local iinv_main = minetest.deserialize(imeta:get_string("inv"))
local ninv = nmeta:get_inventory()
ninv:set_list("main", iinv_main)
ninv:set_size("main", 9 * 3)
set_shulkerbox_meta(nmeta, imeta)
if minetest.is_creative_enabled(placer:get_player_name()) then
if not ninv:is_empty("main") then
return nil
else
return itemstack
end
else
return nil
end
end,
_on_dispense = function(stack, pos, droppos, dropnode, dropdir)
-- Place shulker box as node
if minetest.registered_nodes[dropnode.name].buildable_to then
minetest.set_node(droppos, { name = small_name, param2 = minetest.dir_to_facedir(dropdir) })
local ninv = minetest.get_inventory({ type = "node", pos = droppos })
local imeta = stack:get_meta()
local iinv_main = minetest.deserialize(imeta:get_string("inv"))
ninv:set_list("main", iinv_main)
ninv:set_size("main", 9 * 3)
set_shulkerbox_meta(minetest.get_meta(droppos), imeta)
stack:take_item()
end
return stack
end,
})
minetest.register_node(small_name, {
description = desc,
_tt_help = S("27 inventory slots") .. "\n" .. S("Can be carried around with its contents"),
_doc_items_create_entry = create_entry,
_doc_items_entry_name = entry_name,
_doc_items_longdesc = longdesc,
_doc_items_usagehelp = usagehelp,
drawtype = "nodebox",
node_box = {
type = "fixed",
fixed = { -0.48, -0.5, -0.48, 0.48, 0.489, 0.48 },
},
tiles = { "blank.png^[resize:16x16" },
use_texture_alpha = "clip",
_chest_entity_textures = { mob_texture },
_chest_entity_sound = "mcl_chests_shulker",
_chest_entity_mesh = "mcl_chests_shulker",
_chest_entity_animation_type = "shulker",
groups = {
handy = 1,
pickaxey = 1,
container = 2,
deco_block = 1,
dig_by_piston = 1,
shulker_box = 1,
chest_entity = 1,
not_in_creative_inventory = 1
},
is_ground_content = false,
sounds = mcl_sounds.node_sound_stone_defaults(),
stack_max = 1,
drop = "",
paramtype = "light",
paramtype2 = "facedir",
on_construct = function(pos)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", formspec_shulker_box(nil))
local inv = meta:get_inventory()
inv:set_size("main", 9 * 3)
mcl_chests.create_entity(pos, small_name, { mob_texture }, minetest.get_node(pos).param2, false,
"mcl_chests_shulker", "mcl_chests_shulker", "shulker")
end,
after_place_node = function(pos, placer, itemstack, pointed_thing)
local nmeta = minetest.get_meta(pos)
local imeta = itemstack:get_meta()
-- Convert old itemstacks to not use get_metadata()
if not imeta:contains("inv") and
(itemstack:get_metadata() ~= "") then
imeta:set_string("inv", itemstack:get_metadata())
itemstack:set_metadata("") -- clear
end
local iinv_main = minetest.deserialize(imeta:get_string("inv"))
local ninv = nmeta:get_inventory()
ninv:set_list("main", iinv_main)
ninv:set_size("main", 9 * 3)
set_shulkerbox_meta(nmeta, imeta)
if minetest.is_creative_enabled(placer:get_player_name()) then
if not ninv:is_empty("main") then
return nil
else
return itemstack
end
else
return nil
end
end,
on_rightclick = function(pos, node, clicker)
mcl_chests.player_chest_open(clicker, pos, small_name, { mob_texture }, node.param2, false,
"mcl_chests_shulker", "mcl_chests_shulker", true)
end,
on_receive_fields = function(pos, formname, fields, sender)
if fields.quit then
mcl_chests.player_chest_close(sender)
end
end,
on_destruct = function(pos)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
local items = {}
for i = 1, inv:get_size("main") do
local stack = inv:get_stack("main", i)
items[i] = stack:to_string()
end
local data = minetest.serialize(items)
local boxitem = ItemStack("mcl_chests:" .. color .. "_shulker_box")
local boxitem_meta = boxitem:get_meta()
boxitem_meta:set_string("description", meta:get_string("description"))
boxitem_meta:set_string("name", meta:get_string("name"))
boxitem_meta:set_string("inv", data)
if minetest.is_creative_enabled("") then
if not inv:is_empty("main") then
minetest.add_item(pos, boxitem)
end
else
minetest.add_item(pos, boxitem)
end
end,
allow_metadata_inventory_move = mcl_chests.protection_check_move,
allow_metadata_inventory_take = mcl_chests.protection_check_put_take,
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
local name = player:get_player_name()
if minetest.is_protected(pos, name) then
minetest.record_protection_violation(pos, name)
return 0
end
-- Do not allow to place shulker boxes into shulker boxes
local group = minetest.get_item_group(stack:get_name(), "shulker_box")
if group == 0 or group == nil then
return stack:get_count()
else
return 0
end
end,
on_rotate = mcl_chests.simple_rotate,
_mcl_blast_resistance = 6,
_mcl_hardness = 2,
_mcl_hoppers_on_try_push = function(pos, hop_pos, hop_inv, hop_list)
local meta = minetest.get_meta(pos)
local inv = meta:get_inventory()
return inv, "main",
mcl_util.select_stack(hop_inv, hop_list, inv, "main", mcl_chests.is_not_shulker_box, 1)
end,
})
if doc and not is_canonical then
doc.add_entry_alias("nodes", normal_canonical_name, "nodes", normal_name)
doc.add_entry_alias("nodes", small_canonical_name, "nodes", small_name)
end
minetest.register_craft({
type = "shapeless",
output = normal_name,
recipe = { "group:shulker_box", "mcl_dye:" .. color },
})
end
minetest.register_craft({
output = "mcl_chests:violet_shulker_box",
recipe = {
{ "mcl_mobitems:shulker_shell" },
{ "mcl_chests:chest" },
{ "mcl_mobitems:shulker_shell" },
},
})
-- Save metadata of shulker box when used in crafting
minetest.register_on_craft(function(itemstack, player, old_craft_grid, craft_inv)
if minetest.get_item_group(itemstack:get_name(), "shulker_box") ~= 1 then return end
local original
for i = 1, #old_craft_grid do
local item = old_craft_grid[i]:get_name()
if minetest.get_item_group(item, "shulker_box") == 1 then
original = old_craft_grid[i]
break
end
end
if original then
local ometa = original:get_meta():to_table()
local nmeta = itemstack:get_meta()
nmeta:from_table(ometa)
return itemstack
end
end)
minetest.register_lbm({
label = "Update shulker box formspecs (0.72.0)",
name = "mcl_chests:update_shulker_box_formspecs_0_72_0",
nodenames = { "group:shulker_box" },
run_at_every_load = false,
action = function(pos, node)
local meta = minetest.get_meta(pos)
meta:set_string("formspec", formspec_shulker_box(meta:get_string("name")))
end,
})

View File

@ -80,15 +80,6 @@ minetest.register_craft({
},
})
minetest.register_craft({
output = "mcl_core:granite_bricks 4",
recipe = {
{"mcl_core:granite_smooth", "mcl_core:granite_smooth"},
{"mcl_core:granite_smooth", "mcl_core:granite_smooth"}
},
})
minetest.register_craft({
output = "mcl_core:andesite_smooth 4",
recipe = {
@ -97,14 +88,6 @@ minetest.register_craft({
},
})
minetest.register_craft({
output = "mcl_core:andesite_bricks 4",
recipe = {
{"mcl_core:andesite_smooth", "mcl_core:andesite_smooth"},
{"mcl_core:andesite_smooth", "mcl_core:andesite_smooth"}
},
})
minetest.register_craft({
output = "mcl_core:diorite_smooth 4",
recipe = {
@ -113,14 +96,6 @@ minetest.register_craft({
},
})
minetest.register_craft({
output = "mcl_core:diorite_bricks 4",
recipe = {
{"mcl_core:diorite_smooth", "mcl_core:diorite_smooth"},
{"mcl_core:diorite_smooth", "mcl_core:diorite_smooth"}
},
})
minetest.register_craft({
type = "shapeless",
output = "mcl_core:granite",

View File

@ -1451,22 +1451,6 @@ minetest.register_abm({
end
})
-- Freeze water
minetest.register_abm({
label = "Freeze water in cold areas",
nodenames = {"mcl_core:water_source", "mclx_core:river_water_source"},
interval = 32,
chance = 8,
action = function(pos, node)
if mcl_weather.has_snow(pos)
and minetest.get_natural_light(vector.offset(pos,0,1,0), 0.5) == minetest.LIGHT_MAX + 1
and minetest.get_artificial_light(minetest.get_node(pos).param1) < 10 then
node.name = "mcl_core:ice"
minetest.swap_node(pos, node)
end
end
})
--[[ Call this for vines nodes only.
Given the pos and node of a vines node, this returns true if the vines are supported
and false if the vines are currently floating.

View File

@ -20,8 +20,6 @@ Acacia Wood=
Acacia Wood Planks=
Acacia leaves are grown from acacia trees.=
Andesite=
Andesite Bricks=
Andesite bricks are decorative bricks made from andesite.=
Andesite is an igneous rock.=
Apple=
Apples are food items which can be eaten.=
@ -91,8 +89,6 @@ Diamond Ore=
Diamond ore is rare and can be found in clusters near the bottom of the world.=
Diamonds are precious minerals and useful to create the highest tier of armor and tools.=
Diorite=
Diorite Bricks=
Diorite bricks are decorative bricks made from diorite.=
Diorite is an igneous rock.=
Dirt=
Dirt acts as a soil for a few plants. When in light, this block may grow a grass or mycelium cover if such blocks are nearby.=
@ -114,9 +110,6 @@ Gold nuggets are very small pieces of molten gold; the main purpose is to create
Golden Apple=
Golden apples are precious food items which can be eaten.=
Granite=
Granite Bricks=
Granite bricks are decorative bricks made from granite.=
Granite is an igneous rock.=
Grass Block=
Grass Path=
Grass paths are a decorative variant of grass blocks. Their top has a different color and they are a bit lower than grass blocks, making them useful to build footpaths. Grass paths can be created with a shovel. A grass path turns into dirt when it is below a solid block.=

View File

@ -315,18 +315,6 @@ minetest.register_node("mcl_core:granite_smooth", {
_mcl_hardness = 1.5,
})
minetest.register_node("mcl_core:granite_bricks", {
description = S("Granite Bricks"),
_doc_items_longdesc = S("Granite bricks are decorative bricks made from granite."),
tiles = {"mcl_core_granite_bricks.png"},
stack_max = 64,
is_ground_content = false,
groups = {pickaxey=1, stone=1, building_block=1, material_stone=1},
sounds = mcl_sounds.node_sound_stone_defaults(),
_mcl_blast_resistance = 6,
_mcl_hardness = 1.5,
})
minetest.register_node("mcl_core:andesite", {
description = S("Andesite"),
_doc_items_longdesc = S("Andesite is an igneous rock."),
@ -351,18 +339,6 @@ minetest.register_node("mcl_core:andesite_smooth", {
_mcl_hardness = 1.5,
})
minetest.register_node("mcl_core:andesite_bricks", {
description = S("Andesite Bricks"),
_doc_items_longdesc = S("Andesite bricks are decorative bricks made from andesite."),
tiles = {"mcl_core_andesite_bricks.png"},
is_ground_content = false,
stack_max = 64,
groups = {pickaxey=1, stone=1, building_block=1, material_stone=1},
sounds = mcl_sounds.node_sound_stone_defaults(),
_mcl_blast_resistance = 6,
_mcl_hardness = 1.5,
})
minetest.register_node("mcl_core:diorite", {
description = S("Diorite"),
_doc_items_longdesc = S("Diorite is an igneous rock."),
@ -387,18 +363,6 @@ minetest.register_node("mcl_core:diorite_smooth", {
_mcl_hardness = 1.5,
})
minetest.register_node("mcl_core:diorite_bricks", {
description = S("Diorite Bricks"),
_doc_items_longdesc = S("Diorite bricks are decorative bricks made from diorite."),
tiles = {"mcl_core_diorite_bricks.png"},
is_ground_content = false,
stack_max = 64,
groups = {pickaxey=1, stone=1, building_block=1, material_stone=1},
sounds = mcl_sounds.node_sound_stone_defaults(),
_mcl_blast_resistance = 6,
_mcl_hardness = 1.5,
})
-- Grass Block
minetest.register_node("mcl_core:dirt_with_grass", {
description = S("Grass Block"),

View File

@ -1,3 +1,3 @@
name = mcl_crimson
author = debiankaios, Exhale
depends = mcl_core, mcl_stairs, mobs_mc, mcl_util, mcl_dye, mcl_flowerpots
depends = mcl_core, mcl_fences, mcl_stairs, mobs_mc, mcl_util, mcl_dye, mcl_flowerpots

View File

@ -1,3 +1,3 @@
name = mcl_fences
depends = mcl_core, mcl_sounds
depends = mcl_core, mcl_nether, mcl_sounds
optional_depends = doc, screwdriver

View File

@ -1,4 +1,4 @@
name = mcl_grindstone
author = TheRandomLegoBrick, ChrisPHP
depends = mcl_experience, mcl_sounds
depends = mcl_experience, mcl_formspec, mcl_sounds
description = Add block that disenchants tools and armour except for curses, and repairs two items of the same type it is also the weapon smith's work station.

View File

@ -237,43 +237,30 @@ function mcl_stairs.register_slab(subname, recipeitem, groups, images, descripti
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
},
on_place = function(itemstack, placer, pointed_thing)
if not placer then return end
local above = pointed_thing.above
local under = pointed_thing.under
local anode = minetest.get_node(above)
local unode = minetest.get_node(under)
local adefs = minetest.registered_nodes[anode.name]
local udefs = minetest.registered_nodes[unode.name]
local under = minetest.get_node(pointed_thing.under)
local wield_item = itemstack:get_name()
local player_name = placer:get_player_name()
local creative_enabled = minetest.is_creative_enabled(player_name)
local creative_enabled = minetest.is_creative_enabled(placer:get_player_name())
-- place slab using under node orientation
local dir = vector.subtract(above, under)
local p2 = unode.param2
local dir = vector.subtract(pointed_thing.above, pointed_thing.under)
if minetest.is_protected(under, player_name) and not
minetest.check_player_privs(placer, "protection_bypass") then
minetest.record_protection_violation(under, player_name)
return
end
local p2 = under.param2
-- combine two slabs if possible
-- Requirements: Same slab material, must be placed on top of lower slab, or on bottom of upper slab
if (wield_item == unode.name or (udefs and wield_item == udefs._mcl_other_slab_half)) and
not ((dir.y >= 0 and minetest.get_item_group(unode.name, "slab_top") == 1) or
(dir.y <= 0 and minetest.get_item_group(unode.name, "slab_top") == 0)) then
if (wield_item == under.name or (minetest.registered_nodes[under.name] and wield_item == minetest.registered_nodes[under.name]._mcl_other_slab_half)) and
not ((dir.y >= 0 and minetest.get_item_group(under.name, "slab_top") == 1) or
(dir.y <= 0 and minetest.get_item_group(under.name, "slab_top") == 0)) then
minetest.set_node(under, {name = double_slab, param2 = p2})
if not creative_enabled then
itemstack:take_item()
local player_name = placer:get_player_name()
if minetest.is_protected(pointed_thing.under, player_name) and not
minetest.check_player_privs(placer, "protection_bypass") then
minetest.record_protection_violation(pointed_thing.under,
player_name)
return
end
return itemstack
elseif (wield_item == anode.name or (adefs and wield_item == adefs._mcl_other_slab_half)) then
minetest.set_node(above, {name = double_slab, param2 = p2})
local newnode = double_slab
minetest.set_node(pointed_thing.under, {name = newnode, param2 = p2})
if not creative_enabled then
itemstack:take_item()
end

View File

@ -333,45 +333,6 @@ mcl_stairs.register_stair("diorite_smooth", "mcl_core:diorite_smooth",
nil, 6, nil,
"woodlike")
mcl_stairs.register_slab("andesite_brick", "mcl_core:andesite_bricks", -- Add translations please.
{pickaxey=1},
{"mcl_core_andesite_bricks.png"},
("Andesite Brick Slab"),
nil, 6, nil,
("Double Andesite Brick Slab"))
mcl_stairs.register_stair("andesite_brick", "mcl_core:andesite_bricks",
{pickaxey=1},
{"mcl_core_andesite_bricks.png"},
("Polished Andesite Stairs"),
nil, 6, nil,
"woodlike")
mcl_stairs.register_slab("granite_brick", "mcl_core:granite_bricks", -- Add translations please.
{pickaxey=1},
{"mcl_core_granite_bricks.png"},
("Granite Brick Slab"),
nil, 6, nil,
("Double Granite Brick Slab"))
mcl_stairs.register_stair("granite_brick", "mcl_core:granite_bricks",
{pickaxey=1},
{"mcl_core_granite_bricks.png"},
("Granite Brick Stairs"),
nil, 6, nil,
"woodlike")
mcl_stairs.register_slab("diorite_brick", "mcl_core:diorite_bricks", -- Add translations please.
{pickaxey=1},
{"mcl_core_diorite_bricks.png"},
("Diorite Brick Slab"),
nil, 6, nil,
("Double Diorite Brick Slab"))
mcl_stairs.register_stair("diorite_brick", "mcl_core:diorite_bricks",
{pickaxey=1},
{"mcl_core_diorite_bricks.png"},
("Diorite Brick Stairs"),
nil, 6, nil,
"woodlike")
mcl_stairs.register_stair("stonebrickmossy", "mcl_core:stonebrickmossy",
{pickaxey=1},
{"mcl_core_stonebrick_mossy.png"},

View File

@ -123,7 +123,7 @@ local function snowball_on_step(self, dtime)
self.object:remove()
return
end
self._lastpos = pos -- Set _lastpos-->Node will be added at last pos outside the node
self._lastpos={x=pos.x, y=pos.y, z=pos.z} -- Set _lastpos-->Node will be added at last pos outside the node
end
-- Movement function of egg
@ -133,39 +133,47 @@ local function egg_on_step(self, dtime)
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
-- Destroy when hitting a solid node or entity, with chance to spawn chicks
if (def and def.walkable) or not def or check_object_hit(self, pos, 0) then
-- If egg has just been thrown, use current position
if not self._lastpos.x then
self._lastpos = pos
end
-- 1/8 chance to spawn a chick
-- FIXME: Chicks have a quite good chance to spawn in walls
if math.random(1,8) == 1 then
mcl_mobs.spawn_child(self._lastpos, "mobs_mc:chicken")
-- Destroy when hitting a solid node with chance to spawn chicks
if self._lastpos.x then
if (def and def.walkable) or not def then
-- 1/8 chance to spawn a chick
-- FIXME: Chicks have a quite good chance to spawn in walls
local r = math.random(1,8)
-- BONUS ROUND: 1/32 chance to spawn 3 additional chicks
if math.random(1,32) == 1 then
local offsets = {
{ x=0.7, y=0, z=0 },
{ x=-0.7, y=0, z=-0.7 },
{ x=-0.7, y=0, z=0.7 },
}
for o=1, 3 do
local pos = vector.add(self._lastpos, offsets[o])
mcl_mobs.spawn_child(pos, "mobs_mc:chicken")
if r == 1 then
mcl_mobs.spawn_child(self._lastpos, "mobs_mc:chicken")
-- BONUS ROUND: 1/32 chance to spawn 3 additional chicks
local r = math.random(1,32)
if r == 1 then
local offsets = {
{ x=0.7, y=0, z=0 },
{ x=-0.7, y=0, z=-0.7 },
{ x=-0.7, y=0, z=0.7 },
}
for o=1, 3 do
local pos = vector.add(self._lastpos, offsets[o])
mcl_mobs.spawn_child(pos, "mobs_mc:chicken")
end
end
end
minetest.sound_play("mcl_throwing_egg_impact", { pos = self.object:get_pos(), max_hear_distance=10, gain=0.5 }, true)
self.object:remove()
if mod_target and node.name == "mcl_target:target_off" then
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
end
return
end
end
-- Destroy when hitting a mob or player (no chick spawning)
if check_object_hit(self, pos, 0) then
minetest.sound_play("mcl_throwing_egg_impact", { pos = self.object:get_pos(), max_hear_distance=10, gain=0.5 }, true)
self.object:remove()
if mod_target and node.name == "mcl_target:target_off" then
mcl_target.hit(vector.round(pos), 0.4) --4 redstone ticks
end
return
end
self._lastpos = pos -- Set lastpos-->Node will be added at last pos outside the node
self._lastpos={x=pos.x, y=pos.y, z=pos.z} -- Set lastpos-->Node will be added at last pos outside the node
end
-- Movement function of ender pearl
@ -264,7 +272,7 @@ local function pearl_on_step(self, dtime)
return
end
end
self._lastpos = pos -- Set lastpos-->Node will be added at last pos outside the node
self._lastpos={x=pos.x, y=pos.y, z=pos.z} -- Set lastpos-->Node will be added at last pos outside the node
end
snowball_ENTITY.on_step = snowball_on_step

View File

@ -116,58 +116,57 @@ mcl_structures.register_structure("shipwreck",{
y_offset = function(pr) return pr:next(-4,-2) end,
loot = {
["mcl_chests:chest_small"] = {
{
stacks_min = 3,
stacks_max = 10,
items = {
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
{ itemstring = "mcl_farming:wheat_item", weight = 7, amount_min = 8, amount_max = 21 },
{ itemstring = "mcl_farming:carrot_item", weight = 7, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_farming:potato_item_poison", weight = 7, amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_farming:potato_item", weight = 7, amount_min = 2, amount_max = 6 },
--{ itemstring = "TODO:moss_block", weight = 7, amount_min = 1, amount_max = 4 },
{ itemstring = "mcl_core:coal_lump", weight = 6, amount_min = 2, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 },
{ itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_armor:helmet_leather_enchanted", weight = 3, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:chestplate_leather_enchanted", weight = 3, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:leggings_leather_enchanted", weight = 3, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:boots_leather_enchanted", weight = 3, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_bamboo:bamboo", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:pumpkin", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
}
stacks_min = 3,
stacks_max = 10,
items = {
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
{ itemstring = "mcl_farming:wheat_item", weight = 7, amount_min = 8, amount_max = 21 },
{ itemstring = "mcl_farming:carrot_item", weight = 7, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_farming:potato_item_poison", weight = 7, amount_min = 2, amount_max = 6 },
{ itemstring = "mcl_farming:potato_item", weight = 7, amount_min = 2, amount_max = 6 },
--{ itemstring = "TODO:moss_block", weight = 7, amount_min = 1, amount_max = 4 },
{ itemstring = "mcl_core:coal_lump", weight = 6, amount_min = 2, amount_max = 8 },
{ itemstring = "mcl_mobitems:rotten_flesh", weight = 5, amount_min = 5, amount_max = 24 },
{ itemstring = "mcl_farming:potato_item", weight = 3, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_armor:helmet_leather_enchanted", weight = 3, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:chestplate_leather_enchanted", weight = 3, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:leggings_leather_enchanted", weight = 3, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
{ itemstring = "mcl_armor:boots_leather_enchanted", weight = 3, func = function(stack, pr)
mcl_enchanting.enchant_uniform_randomly(stack, {"soul_speed"}) end },
--{ itemstring = "TODO:bamboo", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_farming:pumpkin", weight = 2, amount_min = 1, amount_max = 3 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
},
{
stacks_min = 2,
stacks_max = 6,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 90, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:iron_nugget", weight = 50, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:emerald", weight = 40, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:lapis", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:gold_ingot", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
stacks_min = 2,
stacks_max = 6,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 90, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:iron_nugget", weight = 50, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:emerald", weight = 40, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:lapis", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_core:gold_ingot", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:gold_nugget", weight = 10, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
}
},{
stacks_min = 3,
stacks_max = 3,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:paper", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_mobitems:feather", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_books:book", weight = 5, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_armor:coast", weight = 20, amount_min = 2, amount_max = 2},
stacks_min = 3,
stacks_max = 3,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:paper", weight = 20, amount_min = 1, amount_max = 10 },
{ itemstring = "mcl_mobitems:feather", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_books:book", weight = 5, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_armor:coast", weight = 20, amount_min = 2, amount_max = 2},
}
},
}
@ -208,37 +207,35 @@ mcl_structures.register_structure("ocean_temple",{
end,
loot = {
["mcl_chests:chest_small"] = {
stacks_min = 3,
stacks_max = 10,
items = {
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
{ itemstring = "mcl_fishing:fish_raw", weight = 5, amount_min = 8, amount_max = 21 },
{ itemstring = "mcl_fishing:salmon_raw", weight = 7, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
},
{
stacks_min = 3,
stacks_max = 10,
items = {
{ itemstring = "mcl_sus_stew:stew", weight = 10, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:paper", weight = 8, amount_min = 1, amount_max = 12 },
{ itemstring = "mcl_fishing:fish_raw", weight = 5, amount_min = 8, amount_max = 21 },
{ itemstring = "mcl_fishing:salmon_raw", weight = 7, amount_min = 4, amount_max = 8 },
{ itemstring = "mcl_tnt:tnt", weight = 1, amount_min = 1, amount_max = 2 },
stacks_min = 2,
stacks_max = 6,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:goldblock", weight = 1, amount_min = 1, amount_max = 2 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_fishing:fishing_rod", weight = 1, amount_min = 1, amount_max = 1 },
}
},
{
stacks_min = 2,
stacks_max = 6,
items = {
{ itemstring = "mcl_core:iron_ingot", weight = 10, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_core:goldblock", weight = 1, amount_min = 1, amount_max = 2 },
{ itemstring = "mcl_experience:bottle", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_core:diamond", weight = 5, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_fishing:fishing_rod", weight = 1, amount_min = 1, amount_max = 1 },
}
},
{
stacks_min = 4,
stacks_max = 4,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_books:book", weight = 1, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
stacks_min = 4,
stacks_max = 4,
items = {
--{ itemstring = "FIXME TREASURE MAP", weight = 8, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_books:book", weight = 1, amount_min = 1, amount_max = 5 },
{ itemstring = "mcl_clock:clock", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_compass:compass", weight = 1, amount_min = 1, amount_max = 1 },
{ itemstring = "mcl_maps:empty_map", weight = 1, amount_min = 1, amount_max = 1 },
}
},
}

View File

@ -329,13 +329,13 @@ give_starting_inv (Player Starter Pack) bool false
mcl_item_id_debug (Item ID Debug) bool false
#Log mob spawning and despawning events
mcl_logging_mobs_spawn (Log Mob Spawning) bool false
mcl_logging_mobs_spawn (Log Mob Spawning) bool true
# If enabled mapgen timings will be dumped to log
mcl_logging_mapgen (Chunk generation logging) bool false
# If enabled generated structures will be logged
mcl_logging_structures (Structure generation logging) bool false
mcl_logging_structures (Structure generation logging) bool true
#Complete debug logging for mcl_signs events. Use this if you have issues with signs.
mcl_logging_mcl_signs (Complete debug logging for mcl_signs) bool false

Binary file not shown.

Before

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 430 B