1
0
Fork 0

Compare commits

...

50 Commits

Author SHA1 Message Date
Tim7900 e86b0692ae Brick Translations (Pt. 1)
Yeah, pretty much self explanitory.
2024-08-03 23:09:52 -08:00
Tim7900 3ec4c7294b Merge branch 'master' of https://git.minetest.land/Tim7/VoxeLibre 2024-08-03 17:18:08 -08:00
Tim7900 b9c2cd72e5 Bricks for Andesite, Diorite and Granite.
Nice unique textures for each of them, stairs, no translations yet.
2024-08-03 13:00:26 -08:00
kno10 77382d930e Typo in setting name, default should be false (#4492)
Reviewed-on: VoxeLibre/VoxeLibre#4492
Reviewed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
Co-authored-by: kno10 <kno10@noreply.git.minetest.land>
Co-committed-by: kno10 <kno10@noreply.git.minetest.land>
2024-08-03 17:52:51 +02:00
the-real-herowl 5e366a8916 Merge pull request 'Implement water freezing (mostly taken from mcla)' (#4478) from WillConker/VoxeLibre:water_freezing into master
Reviewed-on: VoxeLibre/VoxeLibre#4478
Reviewed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
2024-08-03 15:13:41 +02:00
WillConker 6c614d8376 Added line breaks in water freezing condition 2024-08-03 14:21:33 +02:00
WillConker a785be59d9 Changed node light check to artificial light so that water can freeze in the day 2024-08-03 14:21:33 +02:00
cora f23014005f Only freeze water under open air
and if light level < 10
2024-08-03 14:21:33 +02:00
cora c2098d777f Prevent water freezing and ice melting ABMs from fighting 2024-08-03 14:21:33 +02:00
cora dc07eea590 Freeze water in cold areas 2024-08-03 14:21:33 +02:00
tacotexmex 3460e27468 Update OpenCollective link (#4465)
Reviewed-on: VoxeLibre/VoxeLibre#4465
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: tacotexmex <tacotexmex@noreply.git.minetest.land>
Co-committed-by: tacotexmex <tacotexmex@noreply.git.minetest.land>
2024-08-03 12:47:37 +02:00
OgelGames 0012bdb71e Clear itemstring when picking up or merging items (#4530)
Prevents possible item duplication with other mods. Matches the behavior of the default `__builtin:item`.

See also https://github.com/mt-mods/pipeworks/issues/130

Reviewed-on: VoxeLibre/VoxeLibre#4530
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: OgelGames <ogelgames@noreply.git.minetest.land>
Co-committed-by: OgelGames <ogelgames@noreply.git.minetest.land>
2024-07-31 03:26:28 +02:00
kno10 b8d7139792 Fix iron golem attack animation (#4542)
C.f. MCLA

Reviewed-on: VoxeLibre/VoxeLibre#4542
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
Co-authored-by: kno10 <erich.schubert@gmail.com>
Co-committed-by: kno10 <erich.schubert@gmail.com>
2024-07-31 03:18:53 +02:00
WillConker cddc1982be Fix first loot pool not being used in shipwrecks or ocean temples (#4523)
Reviewed-on: VoxeLibre/VoxeLibre#4523
Reviewed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
Co-authored-by: WillConker <willconker@noreply.git.minetest.land>
Co-committed-by: WillConker <willconker@noreply.git.minetest.land>
2024-07-31 03:00:27 +02:00
kno10 9595b0df59 Inconsistent bed bouncyness, make top match bottom (#4495)
Reviewed-on: VoxeLibre/VoxeLibre#4495
Reviewed-by: teknomunk <teknomunk@protonmail.com>
Co-authored-by: kno10 <kno10@noreply.git.minetest.land>
Co-committed-by: kno10 <kno10@noreply.git.minetest.land>
2024-07-31 02:58:38 +02:00
WillConker ff21d1eab1 Make eggs spawn chicks when hitting entities (#4484)
Reviewed-on: VoxeLibre/VoxeLibre#4484
Reviewed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
Co-authored-by: WillConker <willconker@noreply.git.minetest.land>
Co-committed-by: WillConker <willconker@noreply.git.minetest.land>
2024-07-31 02:57:20 +02:00
WillConker 9d5b46c28a Slow ender dragon regeneration (#4481)
Reviewed-on: VoxeLibre/VoxeLibre#4481
Reviewed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
Co-authored-by: WillConker <waj01@joanes.net>
Co-committed-by: WillConker <waj01@joanes.net>
2024-07-31 02:56:33 +02:00
kno10 80a6a6efb0 Choose direction vectors uniformly for spawning (#4467)
The previous code was biased towards placing mobs on top or below the
player, because it chose the theta inclination angle uniformly,
but the sphere is more narrow at the top and bottom.

This code is also simpler.

Reviewed-on: VoxeLibre/VoxeLibre#4467
Reviewed-by: teknomunk <teknomunk@protonmail.com>
Co-authored-by: kno10 <erich.schubert@gmail.com>
Co-committed-by: kno10 <erich.schubert@gmail.com>
2024-07-31 02:30:29 +02:00
the-real-herowl 32148262e1 Merge pull request 'Grand Chests Refactor (fixes #281)' (#4453) from mcl_chests_redo into master
Reviewed-on: VoxeLibre/VoxeLibre#4453
Reviewed-by: teknomunk <teknomunk@protonmail.com>
Reviewed-by: the-real-herowl <the-real-herowl@noreply.git.minetest.land>
2024-07-31 02:28:18 +02:00
Mikita Wiśniewski 567d112942 Fix deprecated get_metadata() usage
Items are instead written as a serialized string into ItemStackMetaRef, and read from there as well. Old itemstacks get converted to the new format automatically.
2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 347305eaea Fix ender chests rotate crash 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 508bc19f6a Remove nil fields from trapped chest definition 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski c1e9e4b1a2 Fix typos in API.md 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 7bf15642ca Resolve teknomunk's comments 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski f1fa6240bb Fix shift-clicking and a few luacheck warnings 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski c5bc6ff189 Cleanup comments (don't use \=, it's annoying) 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 70e903b716 Simplify double inventory inv logic 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 209b24a2fb Move LBMs out of init.lua and fix API.md 2024-07-31 02:27:26 +02:00
cora ac05f8bad6 Remove unused variables in chests example.lua 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 76cff76d91 Add an introduction text to API.md 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 49b6d09985 Add documentation (README.md, API.md) 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski a66c35a9ea Fix double chests once more (hoppers this time) 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski a28e55160f Make the chest opening/closing sound customizable + fix double chests 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 6bbb6b8dec Add title field for mcl_chests.register_chest 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski b4b5bf8391 Move some groups (api.lua → chests.lua) 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 16dd8694a6 Refactor mcl_chests.register_chest AGAIN + cleanup
Now you can define custom groups for the chests, as shown in the trapped
chests example (now they show up under "Mechanisms" tab in creative!).

I'm a bit suspicious of the new return-wrapped functions, as in *they
might break under some circumstances I didn't observe*. It will require
some heavy testing to make sure nothing crashes in the future.

Also, `on_rightclick` for double chests is something I really want to
return-wrap as well, but failed to do so. 🤷
2024-07-31 02:27:26 +02:00
Jürgen Rühle 7a5ee4e6e2 Better detection of properly serialized data
Remove entity if initialization data is missing. Downgrade message in that case
to a warning.
2024-07-31 02:27:26 +02:00
Jürgen Rühle d0d9600709 Fix invisible chests
Pass chest entity initialization data to on_activate as staticdata so
initialization is atomic, preventing premature deletion of chest entity
by concurrent server steps.
2024-07-31 02:27:26 +02:00
cora 709b73295c Fix meta:set_string being called with nil 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski 1d77017ca9 Refactor mcl_chests.register_chest and clean-up 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski d6d64d8837 Fix long doc strings 2024-07-31 02:27:26 +02:00
Mikita Wiśniewski e771f0e3ff Split ITEMS/mcl_chests/init.lua (fixes #281)
Shulker boxes can now be rotated.

The TODO will now be transferred to PR.
2024-07-31 02:27:26 +02:00
Mikita Wiśniewski b10bfe27ce Clean-up ITEMS/mcl_chests code, part 2
Chest tile management has been reorganized to use postfixes, some slight
formatting fixes applied here and there, and roughly marked down where
the new files should (ideally) begin and end.
2024-07-31 02:27:26 +02:00
Mikita Wiśniewski d5b3a6f658 Clean-up ITEMS/mcl_chests code, part 1
Amongst other changes:
- mcl_chests.register_chest function has been exposed. The API is still
  too terrible to call it quits though, I definitely want all these
  parameters passed to be part of a key-value table.
- Added a TODO list at the top of the file. Don't worry, I'll remove it
  once I'm done. It's more just for my convenience than anything.
2024-07-31 02:27:26 +02:00
JoseDouglas26 f7ee3b59d7 Changes on slab placement checks (#4317)
Fixed slab placement being not allowed when it should be allowed.

Reviewed-on: VoxeLibre/VoxeLibre#4317
Reviewed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
Co-authored-by: JoseDouglas26 <josedouglas20002014@gmail.com>
Co-committed-by: JoseDouglas26 <josedouglas20002014@gmail.com>
2024-07-31 00:11:22 +02:00
the-real-herowl 1745b6d400 Merge pull request 'Add persistence to XP Orbs' (#4265) from teknomunk/MineClone2:persist-xp-orbs into master
Reviewed-on: VoxeLibre/VoxeLibre#4265
Reviewed-by: Mikita Wiśniewski <rudzik8@protonmail.com>
2024-07-31 00:03:05 +02:00
teknomunk bb9ed4498b Address review comments 2024-06-26 06:52:26 -05:00
teknomunk 94981d9c09 Add return for get_staticdata 2024-06-26 12:23:30 +02:00
teknomunk 7ae05d9c06 Add persistence to XP orbs, cleanup xp_to_size 2024-06-26 12:23:30 +02:00
teknomunk 177e8f4b9d Code cleanup 2024-06-26 12:23:30 +02:00
28 changed files with 2320 additions and 1851 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/mineclone2>
* OpenCollective: <https://opencollective.com/voxelibre>
* Mastodon: <https://fosstodon.org/@VoxeLibre>
* Lemmy: <https://lemm.ee/c/voxelibre>
* Matrix space: <https://app.element.io/#/room/#voxelibre:matrix.org>

View File

@ -136,6 +136,7 @@ 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
@ -957,6 +958,7 @@ 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",true)
local spawn_logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
local MAPGEN_LIMIT = mcl_vars.mapgen_limit
local MAPGEN_MOB_LIMIT = MAPGEN_LIMIT - 90

View File

@ -17,12 +17,13 @@ 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
@ -33,9 +34,9 @@ local table_copy = table.copy
local table_remove = table.remove
local pairs = pairs
local LOGGING_ON = minetest.settings:get_bool("mcl_logging_mobs_spawning", false)
local logging = minetest.settings:get_bool("mcl_logging_mobs_spawn", false)
local function mcl_log (message, property)
if LOGGING_ON then
if logging then
if property then
message = message .. ": " .. dump(property)
end
@ -54,8 +55,10 @@ 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
@ -95,7 +98,6 @@ 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
@ -601,71 +603,40 @@ 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.
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
-- 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
--print("Using spawn distance of "..tostring(distance).." fx="..tostring(fx)..",x="..tostring(x))
-- 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))
-- 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
local goal_pos = vector.offset(pos, xoff, yoff, zoff)
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
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
mcl_log("Pos outside mapgen limits: " .. minetest.pos_to_string(goal_pos))
return nil
end
-- Calculate upper/lower y limits
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 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 y_min
local y_max
if d >= MOB_SPAWN_ZONE_INNER then
local y_min, y_max
if d2 >= MOB_SPAWN_ZONE_INNER_SQ 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 R2 = MOB_SPAWN_ZONE_INNER
local y2 = math_sqrt( R2*R2 - d*d )
if goal_pos.y > pos. y then
local y2 = math_sqrt( MOB_SPAWN_ZONE_INNER_SQ - d2 )
if goal_pos.y > pos.y then
-- Upper hemisphere
y_min = pos.y + y2
y_max = pos.y + y1
@ -675,16 +646,9 @@ 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)
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
y_min = math_max(math_floor(y_min), goal_pos.y - 16)
y_max = math_min(math_ceil(y_max), goal_pos.y + 16)
-- Ask engine for valid spawn locations
local spawning_position_list = find_nodes_in_area_under_air(
@ -997,7 +961,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)
@ -1005,10 +969,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]
@ -1017,8 +981,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)
@ -1145,7 +1109,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
@ -1196,8 +1160,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,21 +4,17 @@
local S = minetest.get_translator("mobs_mc")
local BEAM_CHECK_FREQUENCY = 2
local BEAM_CHECK_FREQUENCY = 1
local POS_CHECK_FREQUENCY = 15
local HEAL_AMMOUNT = 37
local HEAL_INTERVAL = 1
local HEAL_AMOUNT = 2
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
@ -106,7 +102,6 @@ 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)
@ -115,8 +110,20 @@ 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 = 0, run_end = 40,
punch_start = 40, punch_end = 50,
run_start = 40, run_end = 80,
punch_start = 80, punch_end = 90,
},
jump = true,
do_custom = function(self, dtime)

View File

@ -1,7 +1,12 @@
local size_min, size_max = 20, 59
local delta_size = size_max - size_min
-- 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_to_xp = {
-- min and max XP amount for a given size
{-32768, 2}, -- 1
{ 3, 6}, -- 2
{ 7, 16}, -- 3
@ -16,24 +21,20 @@ local size_to_xp = {
}
local function xp_to_size(xp)
local i, l = 1, #size_to_xp
xp = xp or 0
while xp > size_to_xp[i][1] and i < l do
i = i + 1
-- 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
end
return ((i - 1) / (l - 1) * delta_size + size_min) / 100
-- Fallback is the minimum size
return size_min
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
@ -41,33 +42,32 @@ local function xp_step(self, dtime)
self.collected = false
return
end
collector = minetest.get_player_by_name(self.collector)
local 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
pos = self.object:get_pos()
pos2 = collector:get_pos()
local pos = self.object:get_pos()
local pos2 = collector:get_pos()
player_velocity = collector:get_velocity() or collector:get_player_velocity()
local player_velocity = collector:get_velocity() or collector:get_player_velocity()
pos2.y = pos2.y + 0.8
direction = vector.direction(pos,pos2)
distance = vector.distance(pos2,pos)
multiplier = distance
local direction = vector.direction(pos,pos2)
local distance = vector.distance(pos2,pos)
local multiplier = distance
if multiplier < 1 then
multiplier = 1
end
goal = vector.multiply(direction,multiplier)
currentvel = self.object:get_velocity()
local currentvel = self.object:get_velocity()
if distance > 1 then
multiplier = 20 - distance
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))
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))
elseif distance < 0.8 then
mcl_experience.add_xp(collector, self._xp)
self.object:remove()
@ -75,28 +75,26 @@ local function xp_step(self, dtime)
return
else
self.collector = nil
self.enable_physics(self)
self:enable_physics()
end
end
-- Age orbs
self.age = self.age + dtime
if self.age > max_orb_age then
self.object:remove()
return
end
pos = self.object:get_pos()
local pos = self.object:get_pos()
if not pos then return end
if pos then
node = minetest.get_node_or_nil({
x = pos.x,
y = pos.y -0.25,
z = pos.z
})
else
return
end
-- 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
})
-- Remove nodes in 'ignore'
if node and node.name == "ignore" then
@ -109,18 +107,18 @@ local function xp_step(self, dtime)
end
-- Slide on slippery nodes
vel = self.object:get_velocity()
def = node and minetest.registered_nodes[node.name]
is_moving = (def and not def.walkable) or
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.x ~= 0 or vel.y ~= 0 or vel.z ~= 0
is_slippery = false
local is_slippery = false
if def and def.walkable then
slippery = minetest.get_item_group(node.name, "slippery")
local 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
slip_factor = 4.0 / (slippery + 4)
local slip_factor = 4.0 / (slippery + 4)
self.object:set_acceleration({
x = -vel.x * slip_factor,
y = 0,
@ -160,7 +158,6 @@ 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,
@ -191,7 +188,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
size = xp_to_size(xp)
local size = xp_to_size(xp)
self.object:set_properties({
visual_size = {x = size, y = size},
@ -199,6 +196,9 @@ 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

@ -286,8 +286,7 @@ function mcl_beds.register_bed(name, def)
paramtype = "light",
paramtype2 = "facedir",
is_ground_content = false,
-- 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},
groups = {handy = 1, flammable = -1, bed = 2, dig_by_piston=1, bouncy=66, 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

@ -0,0 +1,233 @@
# `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

@ -0,0 +1,19 @@
# `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

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

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

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

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

@ -0,0 +1,358 @@
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,6 +80,15 @@ 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 = {
@ -88,6 +97,14 @@ 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 = {
@ -96,6 +113,14 @@ 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,6 +1451,22 @@ 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,6 +20,8 @@ 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.=
@ -89,6 +91,8 @@ 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.=
@ -110,6 +114,9 @@ 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,6 +315,18 @@ 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."),
@ -339,6 +351,18 @@ 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."),
@ -363,6 +387,18 @@ 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

@ -237,30 +237,43 @@ 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)
local under = minetest.get_node(pointed_thing.under)
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 wield_item = itemstack:get_name()
local creative_enabled = minetest.is_creative_enabled(placer:get_player_name())
local player_name = placer:get_player_name()
local creative_enabled = minetest.is_creative_enabled(player_name)
-- place slab using under node orientation
local dir = vector.subtract(pointed_thing.above, pointed_thing.under)
local dir = vector.subtract(above, under)
local p2 = unode.param2
local p2 = under.param2
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
-- 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 == 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
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
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
minetest.set_node(under, {name = double_slab, param2 = p2})
if not creative_enabled then
itemstack:take_item()
end
local newnode = double_slab
minetest.set_node(pointed_thing.under, {name = newnode, param2 = p2})
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})
if not creative_enabled then
itemstack:take_item()
end

View File

@ -333,6 +333,45 @@ 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={x=pos.x, y=pos.y, z=pos.z} -- Set _lastpos-->Node will be added at last pos outside the node
self._lastpos = pos -- Set _lastpos-->Node will be added at last pos outside the node
end
-- Movement function of egg
@ -133,47 +133,39 @@ 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 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)
-- 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")
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
-- 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")
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={x=pos.x, y=pos.y, z=pos.z} -- Set lastpos-->Node will be added at last pos outside the node
self._lastpos = pos -- Set lastpos-->Node will be added at last pos outside the node
end
-- Movement function of ender pearl
@ -272,7 +264,7 @@ local function pearl_on_step(self, dtime)
return
end
end
self._lastpos={x=pos.x, y=pos.y, z=pos.z} -- Set lastpos-->Node will be added at last pos outside the node
self._lastpos = pos -- Set lastpos-->Node will be added at last pos outside the node
end
snowball_ENTITY.on_step = snowball_on_step

View File

@ -116,57 +116,58 @@ 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 = "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 = 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 = 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},
}
},
}
@ -207,35 +208,37 @@ 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 = 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 = 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 = 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 = 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 },
}
},
}

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 true
mcl_logging_mobs_spawn (Log Mob Spawning) bool false
# 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 true
mcl_logging_structures (Structure generation logging) bool false
#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.

After

Width:  |  Height:  |  Size: 220 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 430 B