blockgame/mods/bg_api/leveled_node.lua

131 lines
4.0 KiB
Lua

local function is_same_kind (pos, kindname)
local node = minetest.get_node(pos)
local node_kindname = minetest.registered_items[node.name].kindname
return kindname and node_kindname and kindname == node_kindname
end
local function transfer_levels (source, target, max)
local max_diff = max - target
local diff = math.min(max_diff, source)
return source - diff, target + diff
end
local function stack_attempt (pos, source_level)
local node = minetest.get_node(pos)
local def = minetest.registered_nodes[node.name]
if def.level == level_max then return source_level end
local level_max = def.level_max
local kindname = def.kindname
local source_level, target_level = transfer_levels(source_level, def.level, level_max)
minetest.swap_node(pos, {name = kindname .. "_" .. (target_level)})
return source_level
end
function blockgame.check_for_stacking (pos)
local node = minetest.get_node(pos)
if not blockgame.item_matches(node.name, {"group:leveled"}) then return end
local def = minetest.registered_nodes[node.name]
local kindname = def.kindname
local level_max = def.level_max
local below = pos:offset(0, -1, 0)
if is_same_kind(below, kindname) then
local new_level = stack_attempt(below, def.level)
if new_level ~= def.level then
minetest.swap_node(pos, {name = kindname .. "_" .. new_level})
end
end
local above = pos:offset(0, 1, 0)
if is_same_kind(above, kindname) then
blockgame.check_for_stacking(above)
end
end
local function on_place (itemstack, placer, pointed)
local function fallback_to_default ()
return minetest.item_place(itemstack, placer, pointed)
end
local placed_def = minetest.registered_items[itemstack:get_name()]
if not placed_def.level_max then return fallback_to_default() end
if not is_same_kind(pointed.under, placed_def.kindname) then return fallback_to_default() end
if placer:is_player() and placer:get_player_control().sneak then return fallback_to_default() end
local above = pointed.under + vector.new(0, 1, 0)
local above_node = minetest.get_node(above)
local target_node = minetest.get_node(pointed.under)
local target_def = minetest.registered_items[target_node.name]
if target_def.level_max == target_def.level then return fallback_to_default() end
local source_level, target_level = transfer_levels(placed_def.level, target_def.level, placed_def.level_max)
if source_level > 0 and above_node.name ~= "air" then return fallback_to_default() end
local left_over = stack_attempt(pointed.under, placed_def.level)
if above_node.name == "air" then
minetest.set_node(pointed.above, {name = target_def.kindname .. "_" .. left_over})
end
itemstack:set_count(itemstack:get_count() - 1)
return itemstack
end
local function register_layer (kindname, def, level, modname)
local def = blockgame.naive_deep_copy(def)
def.level = level
if def.level ~= def.level_max then
def.description = def.description .. " (" .. level .. "/" .. def.level_max .. ")"
end
if level ~= def.level_max then
def.drawtype = "nodebox"
def.node_box = {
["type"] = "fixed",
fixed = { -0.5, -0.5, -0.5, 0.5, -0.5 + def.level/def.level_max, 0.5 },
}
end
def.groups = def.groups or {}
def.groups.leveled = level
local layer_modname = minetest.get_current_modname()
def.drop = modname .. ":" .. kindname .. "_1 " .. level
blockgame.register_node(def.kindname .. "_" .. level, def)
end
local events = blockgame.events.namespace("api")
function blockgame.register_leveled_node (name, def)
if not def.level_max then return false end
local basename, modname, fullname = blockgame.extract_id_parts(name)
def.kindname = fullname
def.on_construct = blockgame.check_for_stacking
def.on_place = on_place
def.after_place_node = blockgame.check_for_stacking
def.after_land = blockgame.check_for_stacking
def.node_placement_prediction = ""
for level=1, def.level_max do
register_layer(basename, def, level, modname)
end
minetest.register_alias(fullname, fullname .. "_" .. def.level_max)
minetest.register_alias(fullname .. "_" .. 0, "air")
end