xconnected/xconnected.lua

391 lines
12 KiB
Lua

xconnected = {}
-- if unifieddyes or morecolor is installed, use colorfacedir
xconnected.with_unifieddyes = minetest.get_modpath( "unifieddyes");
-- morecolor adds less colors than unifieddyes (but more than none)
xconnected.with_morecolor = minetest.get_modpath( "morecolor");
-- change the drops value if you want the player to get one of the other node types when digged (i.e. c0 or ln or lp)
local drops = "c4";
-- this table contains the new postfix and param2 for a newly placed node
-- depending on its neighbours
local xconnected_get_candidate = {};
-- no neighbours
xconnected_get_candidate[0] = {"_c0", 0 };
-- exactly one neighbour
xconnected_get_candidate[1] = {"_c1", 1 };
xconnected_get_candidate[2] = {"_c1", 0 };
xconnected_get_candidate[4] = {"_c1", 3 };
xconnected_get_candidate[8] = {"_c1", 2 };
-- a line between two nodes
xconnected_get_candidate[5] = {"_ln", 1 };
xconnected_get_candidate[10] = {"_ln", 0 };
-- two neighbours
xconnected_get_candidate[3] = {"_c2", 0 };
xconnected_get_candidate[6] = {"_c2", 3 };
xconnected_get_candidate[12] = {"_c2", 2 };
xconnected_get_candidate[9] = {"_c2", 1 };
-- three neighbours
xconnected_get_candidate[7] = {"_c3", 3 };
xconnected_get_candidate[11] = {"_c3", 0 };
xconnected_get_candidate[13] = {"_c3", 1 };
xconnected_get_candidate[14] = {"_c3", 2 };
-- four neighbours
xconnected_get_candidate[15] = {"_c4", 1 };
local directions = {
{x = 1, y = 0, z = 0},
{x = 0, y = 0, z = 1},
{x = -1, y = 0, z = 0},
{x = 0, y = 0, z = -1},
}
-- this function is called when a middle node (connected to two other ones on opposing sides)
-- is punched with a normal node of that type (the _c4 variant)
xconnected.on_punch = function( pos, node, puncher, pointed_thing )
if( not( puncher ) or not( puncher:get_wielded_item() )) then
return;
end
local wielded = puncher:get_wielded_item():get_name();
if( not( wielded )
or wielded == ""
or not( node )
or not( node.name )
or not( minetest.registered_items[ wielded ] )
or not( minetest.registered_items[ node.name ] )) then
return;
end
-- if the node is a middle one, swap between the version with a middle post and without
local base_name = string.sub( wielded, 1, string.len( wielded )-3 );
if( node.name == base_name.."_ln" ) then
minetest.swap_node( pos, {name=base_name.."_lp", param2=node.param2});
elseif( node.name == base_name.."_lp" ) then
minetest.swap_node( pos, {name=base_name.."_ln", param2=node.param2});
end
end
-- each node depends on the position and amount of neighbours of the same type;
-- the param2 value of the neighbour is not important here
xconnected_update_one_node = function( pos, name, digged )
if( not( pos ) or not( name) or not( minetest.registered_nodes[name])) then
return;
end
local candidates = {0,0,0,0};
local id = 0;
local pow2 = {1,2,4,8};
for i, dir in pairs(directions) do
local node = minetest.get_node( {x=pos.x+dir.x, y=pos.y, z=pos.z+dir.z });
if( node
and node.name
and minetest.registered_nodes[node.name] ) then
local ndef= minetest.registered_nodes[node.name];
-- nodes that drop the same are considered similar xconnected nodes
if( ndef.drop == name
-- ..and also those that share the xcentered group
or (ndef.groups and ndef.groups.xconnected)) then
candidates[i] = node.name;
id = id+pow2[i];
end
-- connect to other solid nodes as well
if( ndef.walkable ~= false
and ndef.drawtype ~= "nodebox") then
-- this neighbour does not need updating
candidates[i] = 0;
-- ..but is relevant as far as selecting our shape goes
id = id+pow2[i];
end
end
end
if( digged ) then
return candidates;
end
local new_node = xconnected_get_candidate[ id ];
if( new_node and new_node[1] ) then
local node = minetest.get_node(pos);
-- support for coloredfacedir
local p2_color = node.param2 - (node.param2 % 32);
local new_name = string.sub( name, 1, string.len( name )-3 )..new_node[1];
if( new_name and minetest.registered_nodes[ new_name ]) then
minetest.swap_node( pos, {name=new_name, param2=new_node[2] + p2_color });
-- if no central node without neighbours is defined, take the c4 variant
elseif( new_node[1]=='_c0' and not( minetest.registered_nodes[ new_name ])) then
minetest.swap_node( pos, {name=name, param2=p2_color });
end
end
return candidates;
end
-- called in on_construct and after_dig_node
xconnected_update = function( pos, name, active, has_been_digged )
if( not( pos ) or not( name) or not( minetest.registered_nodes[name])) then
return;
end
local c = xconnected_update_one_node( pos, name, has_been_digged );
for j,dir2 in pairs(directions) do
if( c[j]~=0 and c[j]~='ignore') then
xconnected_update_one_node( {x=pos.x+dir2.x, y=pos.y, z=pos.z+dir2.z}, c[j], false );
end
end
end
-- def: that part of the node definition that is shared between all nodes
-- node_box_data: has to be a table that contains defs for "c0", "c1", "c2", "c3", "c4", "ln" (optionally "lp")
-- c<nr>: node is connected to that many neighbours clockwise
-- ln: node has 2 neighbours at opposite ends and forms a line with them
xconnected.register = function( name, def, node_box_data, selection_box_data, craft_from )
for k,v in pairs( node_box_data ) do
-- some common values for all xconnected nodes
def.drawtype = "nodebox";
def.paramtype = "light";
if( xconnected.with_morecolor ) then
def.paramtype2 = "colorfacedir";
def.palette = "colorfacedir_palette.png";
elseif( xconnected.with_unifieddyes ) then
def.paramtype2 = "colorfacedir";
def.palette = "unifieddyes_palette_colorwallmounted.png";
else
def.paramtype2 = "facedir";
end
-- similar xconnected nodes are identified by having the same drop
def.drop = name.."_"..drops; -- default: "_c4";
-- nodebox and selection box have been calculated using smmyetry
def.node_box = {
type = "fixed",
fixed = node_box_data[k],
};
def.selection_box = {
type = "fixed",
fixed = selection_box_data[k],
};
if( not( def.tiles )) then
def.tiles = def.textures;
end
-- nodes of the xconnected type all share one group and connect to each other
if( not( def.groups )) then
def.groups = {xconnected=1,oddly_breakable_by_hand=1,choppy=1};
else
def.groups.xconnected = 1;
end
-- that group apparently got a new meaning
def.groups.pane = nil;
local new_def = minetest.deserialize( minetest.serialize( def ));
if( k==drops ) then
-- update nodes when needed
new_def.on_construct = function( pos )
return xconnected_update( pos, name.."_"..drops, true, nil );
end
else
-- avoid spam in creative inventory
new_def.groups.not_in_creative_inventory = 1;
end
-- update neighbours when this node is dug
new_def.after_dig_node = function(pos, oldnode, oldmetadata, digger)
return xconnected_update( pos, name.."_"..drops, true, true );
end
-- punching is used to swap nodes of type _ln and _lp
if( k=='ln' or k=='lp' ) then
new_def.on_punch = xconnected.on_punch;
end
-- actually register the node
minetest.register_node( name.."_"..k, new_def );
end
if( craft_from ) then
minetest.register_craft({
output = name.."_"..drops.." 6",
recipe = {
{craft_from, craft_from, craft_from},
{'default:stick', craft_from, 'default:stick'},
}
})
end
end
-- make use of the symmetry of the nodes and calculate the nodeboxes that way
-- (may also be used for collusion boxes);
-- the center_node_box_list is shared by all nodes that have nighbours
xconnected.construct_node_box_data = function( node_box_list, center_node_box_list, node_box_line )
local res = {};
res.c1 = {};
res.c2 = {};
res.c3 = {};
res.c4 = {};
-- start with the node that is only connected to one neighbour
for _,v in pairs( node_box_list ) do
-- the node c1 already contains all nodes rotated the right way
table.insert( res.c1, v );
table.insert( res.c2, v );
table.insert( res.c3, v );
table.insert( res.c4, v );
end
-- this node is connected to two neighbours and forms a curve/corner;
-- it keeps the nodes from above plus..
for _,v in pairs( node_box_list ) do
-- swap x and z - we are working on a corner node
table.insert( res.c2, {v[3], v[2], v[1], v[6], v[5], v[4]});
table.insert( res.c3, {v[3], v[2], v[1], v[6], v[5], v[4]});
table.insert( res.c4, {v[3], v[2], v[1], v[6], v[5], v[4]});
end
-- now we have a t-crossing
for _,v in pairs( node_box_list ) do
-- mirror x
table.insert( res.c3, {v[4], v[2], v[3]-0.5, v[1], v[5], v[6]-0.5});
table.insert( res.c4, {v[4], v[2], v[3]-0.5, v[1], v[5], v[6]-0.5});
end
-- ...and now a node which is connected to four neighbours
for _,v in pairs( node_box_list ) do
-- swap x and z and mirror
table.insert( res.c4, {v[3]-0.5, v[2], v[4], v[6]-0.5, v[5], v[1]});
end
res.c0 = {};
for _,v in pairs( center_node_box_list ) do
table.insert( res.c0, v );
table.insert( res.c1, v );
table.insert( res.c2, v );
table.insert( res.c3, v );
table.insert( res.c4, v );
end
-- no center node
if( #res.c0 < 1 ) then
res.c0 = nil;
end
res.ln = node_box_line;
res.lp = {}; -- like ln, but with a middle post
for _,v in pairs( node_box_line ) do
table.insert( res.lp, v );
end
for _,v in pairs( center_node_box_list ) do
table.insert( res.lp, v );
end
return res;
end
-- emulate xpanes
xconnected.register_pane = function( name, tiles, craft_from, def )
local node_box_data = xconnected.construct_node_box_data(
-- a half-pane
{{-1/32, -0.5, 0, 1/32, 0.5, 0.5}},
-- there is nothing special in the center
{},
-- a full pane (with neighbours on opposite sides)
{{-1/32, -0.5, -0.5, 1/32, 0.5, 0.5}});
local selection_box_data =
xconnected.construct_node_box_data(
{{-0.06, -0.5, 0, 0.06, 0.5, 0.5}},
{},
{{-0.06, -0.5, -0.5, 0.06, 0.5, 0.5}});
if( not( def )) then
def = {
description = name.." Pane",
textures = {tiles,tiles,tiles,tiles},
is_ground_content = false,
sunlight_propagates = true,
use_texture_alpha = true,
sounds = default.node_sound_glass_defaults(),
groups = {snappy=3, cracky=3, oddly_breakable_by_hand=3},
};
end
xconnected.register( name,
def,
-- node boxes (last one: full one)
node_box_data,
-- selection boxes (last one: full one)
selection_box_data,
craft_from
);
end
xconnected.register_wall = function( name, tiles, craft_from, def )
local node_box_data = xconnected.construct_node_box_data(
-- one extension
-- {{-3/16, -0.5, 0, 3/16, 5/16, 0.5}},
{{-3/16, -0.5-(3/16), 0, 3/16, 5/16, 0.5}},
-- the central part
{{-4/16, -0.5, -4/16, 4/16, 0.5, 4/16 },
{ -3/16, -0.5-(3/16), -3/16, 3/16, -0.5, 3/16 }},
-- neighbours on two opposide sides
-- {{-3/16, -0.5, -0.5, 3/16, 5/16, 0.5}});
{{-3/16, -0.5-(3/16), -0.5, 3/16, 5/16, 0.5}});
local selection_box_data =
xconnected.construct_node_box_data(
{{-0.2, -0.5, 0, 0.2, 5/16, 0.5}},
{{-0.25, -0.5, -0.25, 0.25, 0.5, 0.25 }},
{{-0.2, -0.5, -0.5, 0.2, 5/16, 0.5}});
if( not( def )) then
def = {
description = name.." Wall",
textures = {tiles,tiles,tiles,tiles},
is_ground_content = false,
sunlight_propagates = true,
sounds = default.node_sound_stone_defaults(),
groups = {cracky=3, stone=2},
};
end
xconnected.register( name,
def,
node_box_data,
selection_box_data,
craft_from
);
end
xconnected.register_fence = function( name, tiles, craft_from, def )
local node_box_data = xconnected.construct_node_box_data(
-- one extension
{{-0.06, 0.25, 0, 0.06, 0.4, 0.5},
{-0.06, -0.15, 0, 0.06, 0, 0.5}},
-- the central part
-- {{-0.1, -0.5, -0.1, 0.1, 0.5, 0.1}},
{{-0.1, -0.5-(3/16), -0.1, 0.1, 0.5, 0.1}},
-- neighbours on two opposide sides
{{-0.06, 0.25, -0.5, 0.06, 0.4, 0.5},
{-0.06, -0.15, -0.5, 0.06, 0, 0.5}});
-- only the central part acts as a selection box
local selection_box_data = xconnected.construct_node_box_data(
{},
{{-0.2, -0.5, -0.2, 0.2, 0.5, 0.2}},
{{-0.2, -0.5, -0.2, 0.2, 0.5, 0.2}});
if( not( def )) then
def = {
description = name.." Wall",
textures = {tiles,tiles,tiles,tiles},
is_ground_content = false,
sunlight_propagates = true,
sounds = default.node_sound_stone_defaults(),
groups = {snappy=3, cracky=3, oddly_breakable_by_hand=3},
};
end
xconnected.register( name,
def,
node_box_data,
selection_box_data,
craft_from
);
end