forked from VoxeLibre/VoxeLibre
Compare commits
50 Commits
dependency
...
master
Author | SHA1 | Date |
---|---|---|
Tim7900 | e86b0692ae | |
Tim7900 | 3ec4c7294b | |
Tim7900 | b9c2cd72e5 | |
kno10 | 77382d930e | |
the-real-herowl | 5e366a8916 | |
WillConker | 6c614d8376 | |
WillConker | a785be59d9 | |
cora | f23014005f | |
cora | c2098d777f | |
cora | dc07eea590 | |
tacotexmex | 3460e27468 | |
OgelGames | 0012bdb71e | |
kno10 | b8d7139792 | |
WillConker | cddc1982be | |
kno10 | 9595b0df59 | |
WillConker | ff21d1eab1 | |
WillConker | 9d5b46c28a | |
kno10 | 80a6a6efb0 | |
the-real-herowl | 32148262e1 | |
Mikita Wiśniewski | 567d112942 | |
Mikita Wiśniewski | 347305eaea | |
Mikita Wiśniewski | 508bc19f6a | |
Mikita Wiśniewski | c1e9e4b1a2 | |
Mikita Wiśniewski | 7bf15642ca | |
Mikita Wiśniewski | f1fa6240bb | |
Mikita Wiśniewski | c5bc6ff189 | |
Mikita Wiśniewski | 70e903b716 | |
Mikita Wiśniewski | 209b24a2fb | |
cora | ac05f8bad6 | |
Mikita Wiśniewski | 76cff76d91 | |
Mikita Wiśniewski | 49b6d09985 | |
Mikita Wiśniewski | a66c35a9ea | |
Mikita Wiśniewski | a28e55160f | |
Mikita Wiśniewski | 6bbb6b8dec | |
Mikita Wiśniewski | b4b5bf8391 | |
Mikita Wiśniewski | 16dd8694a6 | |
Jürgen Rühle | 7a5ee4e6e2 | |
Jürgen Rühle | d0d9600709 | |
cora | 709b73295c | |
Mikita Wiśniewski | 1d77017ca9 | |
Mikita Wiśniewski | d6d64d8837 | |
Mikita Wiśniewski | e771f0e3ff | |
Mikita Wiśniewski | b10bfe27ce | |
Mikita Wiśniewski | d5b3a6f658 | |
JoseDouglas26 | f7ee3b59d7 | |
the-real-herowl | 1745b6d400 | |
teknomunk | bb9ed4498b | |
teknomunk | 94981d9c09 | |
teknomunk | 7ae05d9c06 | |
teknomunk | 177e8f4b9d |
|
@ -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>
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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.
|
|
@ -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
|
|
@ -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
|
|
@ -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,
|
||||
})
|
|
@ -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,
|
||||
})
|
|
@ -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
|
@ -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,
|
||||
})
|
|
@ -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",
|
||||
|
|
|
@ -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.
|
||||
|
|
|
@ -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.=
|
||||
|
|
|
@ -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"),
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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"},
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 },
|
||||
}
|
||||
},
|
||||
}
|
||||
|
|
|
@ -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 |
Loading…
Reference in New Issue