mirror of
https://github.com/minetest-mods/moreblocks.git
synced 2025-07-03 00:20:43 +02:00
checkpoint. stairsplus is done, excluding the circular saw, and testing
This commit is contained in:
24
stairsplus/api/alias.lua
Normal file
24
stairsplus/api/alias.lua
Normal file
@ -0,0 +1,24 @@
|
||||
|
||||
local api = stairsplus.api
|
||||
|
||||
function api.register_alias_all(old_node, new_node)
|
||||
local old_mod, old_name = old_node:match("^([^:]+:(.*)$")
|
||||
local new_mod, new_name = new_node:match("^([^:]+:(.*)$")
|
||||
for _, shape_def in pairs(stairsplus.api.registered_shapes) do
|
||||
minetest.register_alias(
|
||||
("%s:%s"):format(old_mod, shape_def.name_format:format(old_name)),
|
||||
("%s:%s"):format(new_mod, shape_def.name_format:format(new_name))
|
||||
)
|
||||
end
|
||||
end
|
||||
|
||||
function api.register_alias_force_all(old_node, new_node)
|
||||
local old_mod, old_name = old_node:match("^([^:]+:(.*)$")
|
||||
local new_mod, new_name = new_node:match("^([^:]+:(.*)$")
|
||||
for _, shape_def in pairs(stairsplus.api.registered_shapes) do
|
||||
minetest.register_alias_force(
|
||||
("%s:%s"):format(old_mod, shape_def.name_format:format(old_name)),
|
||||
("%s:%s"):format(new_mod, shape_def.name_format:format(new_name))
|
||||
)
|
||||
end
|
||||
end
|
@ -1 +1,100 @@
|
||||
-- for registering variants of a specific node
|
||||
--[[
|
||||
|
||||
]]
|
||||
local api = stairsplus.api
|
||||
|
||||
local legacy_mode = stairsplus.settings.legacy_mode
|
||||
local in_creative_inventory = stairsplus.settings.in_creative_inventory
|
||||
|
||||
local S = stairsplus.S
|
||||
|
||||
local function scale_light(light_source, shape_def)
|
||||
if not light_source or light_source == 0 then
|
||||
return 0
|
||||
elseif legacy_mode then
|
||||
return light_source - 1
|
||||
end
|
||||
|
||||
return math.max(1, math.min(math.round(light_source * shape_def.eighths / 4), light_source))
|
||||
end
|
||||
|
||||
function api.register_single(node, shape, overrides)
|
||||
local mod, name = node:match("^([^:]+):(.*)$")
|
||||
local node_def = table.copy(minetest.registered_nodes[node])
|
||||
local shape_def = api.registered_shapes[shape]
|
||||
|
||||
local groups = {
|
||||
[shape] = 1,
|
||||
not_in_creative_inventory = in_creative_inventory,
|
||||
}
|
||||
|
||||
for group, value in pairs(node_def.groups) do
|
||||
groups[shape_def.name_format:format(group)] = value
|
||||
end
|
||||
|
||||
local def = {
|
||||
description = S(shape_def.description, node_def.description or node),
|
||||
drawtype = shape_def.drawtype,
|
||||
mesh = shape_def.mesh,
|
||||
node_box = shape_def.node_box,
|
||||
collision_box = shape_def.collision_box,
|
||||
selection_box = shape_def.selection_box,
|
||||
groups = groups,
|
||||
use_texture_alpha = node_def.use_texture_alpha,
|
||||
paramtype = shape_def.paramtype or "light",
|
||||
paramtype2 = shape_def.paramtype2 or "facedir",
|
||||
light_source = scale_light(node_def.light_source, shape_def),
|
||||
}
|
||||
|
||||
if node_def.short_description then
|
||||
def.short_description = S(shape_def.description, node_def.short_description)
|
||||
end
|
||||
|
||||
if def.use_texture_alpha == nil then
|
||||
def.use_texture_alpha = node_def.use_texture_alpha
|
||||
end
|
||||
|
||||
if node_def.drop then
|
||||
local item = api.get_shaped_name(node_def.drop, shape)
|
||||
if item then
|
||||
def.drop = item
|
||||
end
|
||||
end
|
||||
|
||||
for k, v in pairs(overrides or {}) do
|
||||
def[k] = v
|
||||
end
|
||||
|
||||
minetest.register_node((":%s:%s"):format(mod, shape_def.name_format:format(name)), def)
|
||||
|
||||
if shape_def.aliases then
|
||||
for _, alias in ipairs(shape_def.aliases) do
|
||||
minetest.register_alias(
|
||||
("%s:%s"):format(mod, alias:format(name)),
|
||||
("%s:%s"):format(mod, shape_def.name_format:format(name))
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function api.register_all(node, overrides)
|
||||
for shape in pairs(api.registered_shapes) do
|
||||
api.register_single(node, shape, overrides)
|
||||
end
|
||||
api.register_schema_crafts_for_node(node)
|
||||
end
|
||||
|
||||
function api.register_custom(node, list, overrides)
|
||||
for _, shape in ipairs(list) do
|
||||
api.register_single(node, shape, overrides)
|
||||
end
|
||||
api.register_schema_crafts_for_node(node)
|
||||
end
|
||||
|
||||
function api.register_group(node, group, overrides)
|
||||
for _, shape in ipairs(api.shapes_by_group[group] or {}) do
|
||||
api.register_single(node, shape, overrides)
|
||||
end
|
||||
api.register_schema_crafts_for_node(node)
|
||||
end
|
||||
|
@ -1,3 +1,235 @@
|
||||
-- for registering recipe schemas
|
||||
-- should register schemas w/ unified_inventory and i3 and whatever else,
|
||||
-- and hide the recipes for the individual nodes (possibly a setting for such)
|
||||
--[[
|
||||
|
||||
api.register_craft_schema({
|
||||
output = "panel_8 6",
|
||||
input = {{"node", "node", "node"}},
|
||||
})
|
||||
|
||||
api.register_craft_schema({
|
||||
type = "shapeless",
|
||||
output = "micro_8 7",
|
||||
input = {"stair_inner"},
|
||||
})
|
||||
|
||||
api.register_schema_crafts_for_node("default:coalblock")
|
||||
|
||||
api.register_crafts_for_shapes({
|
||||
type = "cooking",
|
||||
output = "default:stone",
|
||||
recipe = "default:cobblestone",
|
||||
cooktime = function(eights) return 3 * eights / 8 end,
|
||||
})
|
||||
|
||||
api.register_crafts_for_shapes({
|
||||
type = "fuel",
|
||||
recipe = "default:coalblock",
|
||||
burntime = function(eights) return 370 * eights / 8 end,
|
||||
})
|
||||
|
||||
]]
|
||||
local api = stairsplus.api
|
||||
error("TODO: ugh, forgot to handle when output has > 1 item")
|
||||
|
||||
local function is_valid_shape(item)
|
||||
return api.registered_shapes[item]
|
||||
end
|
||||
|
||||
local function is_normal_item(item)
|
||||
return item == "" or item:match(":")
|
||||
end
|
||||
|
||||
local function is_valid_item(item)
|
||||
return is_valid_shape(item) or is_normal_item(item)
|
||||
end
|
||||
|
||||
local function verify_schema(schema)
|
||||
local problems = {}
|
||||
|
||||
if not (schema.type == "shaped" or schema.type == "shapeless" or schema.type == nil) then
|
||||
table.insert(problems, ("unimplemented schema type %q"):format(schema.type))
|
||||
end
|
||||
|
||||
if not is_valid_item(schema.output) then
|
||||
table.insert(problems, ("don't know how to handle output %q"):format(schema.output))
|
||||
end
|
||||
|
||||
if schema.replacements then
|
||||
for _, replacement in ipairs(schema.replacements) do
|
||||
for _, item in ipairs(replacement) do
|
||||
if not is_valid_item(schema.output) then
|
||||
table.insert(problems, ("don't know how to handle replacement item %q"):format(item))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if schema.type == "shapeless" then
|
||||
for _, item in ipairs(schema.input) do
|
||||
if not is_valid_item(schema.output) then
|
||||
table.insert(problems, ("don't know how to handle craft item %q"):format(item))
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
for _, row in ipairs(schema.input) do
|
||||
for _, item in ipairs(row) do
|
||||
if not is_valid_item(schema.output) then
|
||||
table.insert(problems, ("don't know how to handle craft item %q"):format(item))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if #problems > 0 then
|
||||
return table.concat(problems, ", ")
|
||||
end
|
||||
end
|
||||
|
||||
api.registered_recipe_schemas = {}
|
||||
function api.register_craft_schema(schema)
|
||||
local problems = verify_schema(schema)
|
||||
if problems then
|
||||
error(problems)
|
||||
end
|
||||
|
||||
table.insert(api.registered_recipe_schemas, schema)
|
||||
end
|
||||
|
||||
local function has_the_right_shape(item, shapes)
|
||||
return shapes[item] or item == "" or item:match(":")
|
||||
end
|
||||
|
||||
local function has_the_right_shapes(schema, shapes)
|
||||
if not has_the_right_shape(schema.output, shapes) then
|
||||
return false
|
||||
end
|
||||
|
||||
if schema.replacements then
|
||||
for _, replacement in ipairs(schema.replacements) do
|
||||
for _, item in ipairs(replacement) do
|
||||
if not has_the_right_shape(item, shapes) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if schema.type == "shapeless" then
|
||||
for _, item in ipairs(schema.input) do
|
||||
if not has_the_right_shape(item, shapes) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
elseif schema.type == "shaped" or schema.type == nil then
|
||||
for _, row in ipairs(schema.input) do
|
||||
for _, item in ipairs(row) do
|
||||
if not has_the_right_shape(item, shapes) then
|
||||
return false
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
local function register_for_schema(node, shapes, schema)
|
||||
local recipe = table.copy(schema)
|
||||
|
||||
if shapes[recipe.output] then
|
||||
recipe.output = api.get_shaped_name(node, recipe.output)
|
||||
end
|
||||
|
||||
if recipe.replacements then
|
||||
for _, replacement in ipairs(recipe.replacements) do
|
||||
for i, item in ipairs(replacement) do
|
||||
if shapes[item] then
|
||||
replacement[i] = api.get_shaped_name(node, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if recipe.type == "shapeless" then
|
||||
for i, item in ipairs(recipe.input) do
|
||||
if shapes[item] then
|
||||
recipe.input[i] = api.get_shaped_name(node, item)
|
||||
end
|
||||
end
|
||||
|
||||
elseif recipe.type == "shaped" or recipe.type == nil then
|
||||
for _, row in ipairs(schema.input) do
|
||||
for i, item in ipairs(row) do
|
||||
if shapes[item] then
|
||||
row[i] = api.get_shaped_name(node, item)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_craft(recipe)
|
||||
end
|
||||
|
||||
function api.register_schema_crafts_for_node(node)
|
||||
local shapes = api.get_shapes_hash(node)
|
||||
for _, schema in ipairs(api.registered_recipe_schemas) do
|
||||
if has_the_right_shapes(schema, shapes) then
|
||||
register_for_schema(node, shapes, schema)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local function shapes_match(a, b)
|
||||
local a_shapes = api.get_shapes(a)
|
||||
local b_shapes = api.get_shapes(b)
|
||||
|
||||
if #a_shapes ~= #b_shapes then
|
||||
return false
|
||||
end
|
||||
|
||||
for i = 1, #a_shapes do
|
||||
if a_shapes[i] ~= b_shapes[i] then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
function api.register_crafts_for_shapes(def)
|
||||
if def.type == "cooking" then
|
||||
assert(
|
||||
shapes_match(def.output, def.recipe),
|
||||
("error: shapes of %s and %s do not match"):format(def.output, def.recipe)
|
||||
)
|
||||
|
||||
local shapes = api.get_shapes(def.recipe)
|
||||
|
||||
for _, shape in ipairs(shapes) do
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
output = api.get_shaped_name(def.output, shape),
|
||||
recipe = api.get_shaped_name(def.recipe, shape),
|
||||
cooktime = def.cooktime(api.registered_shapes[shape].eigths),
|
||||
})
|
||||
end
|
||||
|
||||
elseif def.type == "fuel" then
|
||||
local shapes = api.get_shapes(def.recipe)
|
||||
|
||||
for _, shape in ipairs(shapes) do
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = api.get_shaped_name(def.recipe, shape),
|
||||
burntime = def.burntime(api.registered_shapes[shape].eigths),
|
||||
})
|
||||
end
|
||||
|
||||
else
|
||||
error(("unsupported recipe type %s"):format(def.type))
|
||||
end
|
||||
end
|
||||
|
@ -1,2 +1,14 @@
|
||||
-- register shapes (e.g. 1/16 slab, 1/8 slab, 1/4 slab, etc)
|
||||
-- register shape groups (e.g. slab, stair, etc.)
|
||||
|
||||
stairsplus.api.registered_shapes = {}
|
||||
stairsplus.api.shapes_by_group = {}
|
||||
|
||||
function stairsplus.api.register_shape(name , def)
|
||||
stairsplus.api.registered_shapes[name] = def
|
||||
|
||||
for group in pairs(def.shape_groups or {}) do
|
||||
local shapes = stairsplus.api.shapes_by_group[group] or {}
|
||||
table.insert(shapes, name)
|
||||
stairsplus.api.shapes_by_group[group] = shapes
|
||||
end
|
||||
end
|
||||
|
@ -1 +1,224 @@
|
||||
-- for creating the circular saw and similar nodes
|
||||
|
||||
local api = stairsplus.api
|
||||
|
||||
function api.register_station(name, shape_groups, def)
|
||||
minetest.register_node(name, def)
|
||||
end
|
||||
|
||||
|
||||
|
||||
local F = minetest.formspec_escape
|
||||
|
||||
local circular_saw = {}
|
||||
|
||||
|
||||
-- The amount of items offered per shape can be configured:
|
||||
function circular_saw.on_receive_fields(pos, formname, fields, sender)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local max = tonumber(fields.max_offered)
|
||||
if max and max > 0 then
|
||||
meta:set_string("max_offered", max)
|
||||
-- Update to show the correct number of items:
|
||||
circular_saw:update_inventory(pos, 0)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
-- Moving the inventory of the circular_saw around is not allowed because it
|
||||
-- is a fictional inventory. Moving inventory around would be rather
|
||||
-- impractical and make things more difficult to calculate:
|
||||
function circular_saw.allow_metadata_inventory_move(
|
||||
pos, from_list, from_index, to_list, to_index, count, player)
|
||||
return 0
|
||||
end
|
||||
|
||||
|
||||
-- Only input- and recycle-slot are intended as input slots:
|
||||
function circular_saw.allow_metadata_inventory_put(
|
||||
pos, listname, index, stack, player)
|
||||
-- The player is not allowed to put something in there:
|
||||
if listname == "output" or listname == "micro" then
|
||||
return 0
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local stackname = stack:get_name()
|
||||
local count = stack:get_count()
|
||||
|
||||
-- Only allow those items that are offered in the output inventory to be recycled:
|
||||
if listname == "recycle" then
|
||||
if not inv:contains_item("output", stackname) then
|
||||
return 0
|
||||
end
|
||||
local stackmax = stack:get_stack_max()
|
||||
local instack = inv:get_stack("input", 1)
|
||||
local microstack = inv:get_stack("micro", 1)
|
||||
local incount = instack:get_count()
|
||||
local incost = (incount * 8) + microstack:get_count()
|
||||
local maxcost = (stackmax * 8) + 7
|
||||
local cost = circular_saw:get_cost(inv, stackname)
|
||||
if (incost + cost) > maxcost then
|
||||
return math.max((maxcost - incost) / cost, 0)
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
-- Only accept certain blocks as input which are known to be craftable into stairs:
|
||||
if listname == "input" then
|
||||
if not inv:is_empty("input") then
|
||||
if inv:get_stack("input", index):get_name() ~= stackname then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
if not inv:is_empty("micro") then
|
||||
local microstackname = inv:get_stack("micro", 1):get_name():gsub("^.+:micro_", "", 1)
|
||||
local cutstackname = stackname:gsub("^.+:", "", 1)
|
||||
if microstackname ~= cutstackname then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
for name, t in pairs(circular_saw.known_nodes) do
|
||||
if name == stackname and inv:room_for_item("input", stack) then
|
||||
return count
|
||||
end
|
||||
end
|
||||
return 0
|
||||
end
|
||||
end
|
||||
|
||||
-- Taking is allowed from all slots (even the internal microblock slot).
|
||||
-- Putting something in is slightly more complicated than taking anything
|
||||
-- because we have to make sure it is of a suitable material:
|
||||
function circular_saw.on_metadata_inventory_put(
|
||||
pos, listname, index, stack, player)
|
||||
-- We need to find out if the circular_saw is already set to a
|
||||
-- specific material or not:
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local stackname = stack:get_name()
|
||||
local count = stack:get_count()
|
||||
|
||||
-- Putting something into the input slot is only possible if that had
|
||||
-- been empty before or did contain something of the same material:
|
||||
if listname == "input" then
|
||||
-- Each new block is worth 8 microblocks:
|
||||
circular_saw:update_inventory(pos, 8 * count)
|
||||
elseif listname == "recycle" then
|
||||
-- Lets look which shape this represents:
|
||||
local cost = circular_saw:get_cost(inv, stackname)
|
||||
local input_stack = inv:get_stack("input", 1)
|
||||
-- check if this would not exceed input itemstack max_stacks
|
||||
if input_stack:get_count() + ((cost * count) / 8) <= input_stack:get_stack_max() then
|
||||
circular_saw:update_inventory(pos, cost * count)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function circular_saw.allow_metadata_inventory_take(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local input_stack = inv:get_stack(listname, index)
|
||||
local player_inv = player:get_inventory()
|
||||
if not player_inv:room_for_item("main", input_stack) then
|
||||
return 0
|
||||
else
|
||||
return stack:get_count()
|
||||
end
|
||||
end
|
||||
|
||||
function circular_saw.on_metadata_inventory_take(
|
||||
pos, listname, index, stack, player)
|
||||
|
||||
-- Prevent (inbuilt) swapping between inventories with different blocks
|
||||
-- corrupting player inventory or Saw with 'unknown' items.
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
local input_stack = inv:get_stack(listname, index)
|
||||
if not input_stack:is_empty() and input_stack:get_name() ~= stack:get_name() then
|
||||
local player_inv = player:get_inventory()
|
||||
|
||||
-- Prevent arbitrary item duplication.
|
||||
inv:remove_item(listname, input_stack)
|
||||
|
||||
if player_inv:room_for_item("main", input_stack) then
|
||||
player_inv:add_item("main", input_stack)
|
||||
end
|
||||
|
||||
circular_saw:reset(pos)
|
||||
return
|
||||
end
|
||||
|
||||
-- If it is one of the offered stairs: find out how many
|
||||
-- microblocks have to be subtracted:
|
||||
if listname == "output" then
|
||||
-- We do know how much each block at each position costs:
|
||||
local cost = circular_saw.cost_in_microblocks[index]
|
||||
* stack:get_count()
|
||||
|
||||
circular_saw:update_inventory(pos, -cost)
|
||||
elseif listname == "micro" then
|
||||
-- Each microblock costs 1 microblock:
|
||||
circular_saw:update_inventory(pos, -stack:get_count())
|
||||
elseif listname == "input" then
|
||||
-- Each normal (= full) block taken costs 8 microblocks:
|
||||
circular_saw:update_inventory(pos, 8 * -stack:get_count())
|
||||
end
|
||||
-- The recycle field plays no role here since it is processed immediately.
|
||||
end
|
||||
|
||||
function circular_saw.on_construct(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local fancy_inv = ""
|
||||
if has_default_mod then
|
||||
-- prepend background and slot styles from default if available
|
||||
fancy_inv = default.gui_bg .. default.gui_bg_img .. default.gui_slots
|
||||
end
|
||||
meta:set_string(
|
||||
--FIXME Not work with @n in this part bug in minetest/minetest#7450.
|
||||
"formspec", "size[11,10]" .. fancy_inv ..
|
||||
"label[0,0;" .. S("Input material") .. "]" ..
|
||||
"list[current_name;input;1.7,0;1,1;]" ..
|
||||
"label[0,1;" .. F(S("Left-over")) .. "]" ..
|
||||
"list[current_name;micro;1.7,1;1,1;]" ..
|
||||
"label[0,2;" .. F(S("Recycle output")) .. "]" ..
|
||||
"list[current_name;recycle;1.7,2;1,1;]" ..
|
||||
"field[0.3,3.5;1,1;max_offered;" .. F(S("Max")) .. ":;${max_offered}]" ..
|
||||
"button[1,3.2;1.7,1;Set;" .. F(S("Set")) .. "]" ..
|
||||
"list[current_name;output;2.8,0;8,6;]" ..
|
||||
"list[current_player;main;1.5,6.25;8,4;]" ..
|
||||
"listring[current_name;output]" ..
|
||||
"listring[current_player;main]" ..
|
||||
"listring[current_name;input]" ..
|
||||
"listring[current_player;main]" ..
|
||||
"listring[current_name;micro]" ..
|
||||
"listring[current_player;main]" ..
|
||||
"listring[current_name;recycle]" ..
|
||||
"listring[current_player;main]"
|
||||
)
|
||||
|
||||
meta:set_int("anz", 0) -- No microblocks inside yet.
|
||||
meta:set_string("max_offered", 99) -- How many items of this kind are offered by default?
|
||||
meta:set_string("infotext", S("Circular Saw is empty"))
|
||||
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("input", 1) -- Input slot for full blocks of material x.
|
||||
inv:set_size("micro", 1) -- Storage for 1-7 surplus microblocks.
|
||||
inv:set_size("recycle", 1) -- Surplus partial blocks can be placed here.
|
||||
inv:set_size("output", 6 * 8) -- 6x8 versions of stair-parts of material x.
|
||||
|
||||
circular_saw:reset(pos)
|
||||
end
|
||||
|
||||
function circular_saw.can_dig(pos, player)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
if not inv:is_empty("input") or
|
||||
not inv:is_empty("micro") or
|
||||
not inv:is_empty("recycle") then
|
||||
return false
|
||||
end
|
||||
-- Can be dug by anyone when empty, not only by the owner:
|
||||
return true
|
||||
end
|
||||
|
Reference in New Issue
Block a user