|
@ -1,3 +1,4 @@
|
|||
player_*.png
|
||||
*.bak
|
||||
.*.swp
|
||||
clothing.conf
|
||||
|
|
|
@ -0,0 +1,26 @@
|
|||
Minetest Game mod: stairs
|
||||
=========================
|
||||
|
||||
License of source code:
|
||||
-----------------------
|
||||
Copyright (C) 2011-2012 Kahrl <kahrl@gmx.net>
|
||||
Copyright (C) 2011-2012 celeron55, Perttu Ahola <celeron55@gmail.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 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
http://www.gnu.org/licenses/lgpl-2.1.html
|
||||
|
||||
License of media (textures and sounds)
|
||||
--------------------------------------
|
||||
Attribution-ShareAlike 3.0 Unported (CC BY-SA 3.0)
|
||||
http://creativecommons.org/licenses/by-sa/3.0/
|
||||
|
||||
Authors of media files
|
||||
-----------------------
|
||||
Everything not listed in here:
|
||||
Copyright (C) 2010-2012 celeron55, Perttu Ahola <celeron55@gmail.com>
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
default
|
||||
farming
|
|
@ -0,0 +1,409 @@
|
|||
-- Minetest 0.4 mod: stairs
|
||||
-- See README.txt for licensing and other information.
|
||||
|
||||
|
||||
-- Global namespace for functions
|
||||
|
||||
stairs = {}
|
||||
|
||||
|
||||
-- Register aliases for new pine node names
|
||||
|
||||
minetest.register_alias("stairs:stair_pinewood", "stairs:stair_pine_wood")
|
||||
minetest.register_alias("stairs:slab_pinewood", "stairs:slab_pine_wood")
|
||||
|
||||
|
||||
-- Get setting for replace ABM
|
||||
|
||||
local replace = minetest.setting_getbool("enable_stairs_replace_abm")
|
||||
|
||||
|
||||
-- Register stairs.
|
||||
-- Node will be called stairs:stair_<subname>
|
||||
|
||||
function stairs.register_stair(subname, recipeitem, groups, images, description, sounds)
|
||||
groups.stair = 1
|
||||
minetest.register_node(":stairs:stair_" .. subname, {
|
||||
description = description,
|
||||
drawtype = "mesh",
|
||||
mesh = "stairs_stair.obj",
|
||||
tiles = images,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
groups = groups,
|
||||
sounds = sounds,
|
||||
selection_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
|
||||
{-0.5, 0, 0, 0.5, 0.5, 0.5},
|
||||
},
|
||||
},
|
||||
collision_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
{-0.5, -0.5, -0.5, 0.5, 0, 0.5},
|
||||
{-0.5, 0, 0, 0.5, 0.5, 0.5},
|
||||
},
|
||||
},
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
local p0 = pointed_thing.under
|
||||
local p1 = pointed_thing.above
|
||||
local param2 = 0
|
||||
|
||||
local placer_pos = placer:getpos()
|
||||
if placer_pos then
|
||||
local dir = {
|
||||
x = p1.x - placer_pos.x,
|
||||
y = p1.y - placer_pos.y,
|
||||
z = p1.z - placer_pos.z
|
||||
}
|
||||
param2 = minetest.dir_to_facedir(dir)
|
||||
end
|
||||
|
||||
if p0.y - 1 == p1.y then
|
||||
param2 = param2 + 20
|
||||
if param2 == 21 then
|
||||
param2 = 23
|
||||
elseif param2 == 23 then
|
||||
param2 = 21
|
||||
end
|
||||
end
|
||||
|
||||
return minetest.item_place(itemstack, placer, pointed_thing, param2)
|
||||
end,
|
||||
})
|
||||
|
||||
-- for replace ABM
|
||||
if replace then
|
||||
minetest.register_node(":stairs:stair_" .. subname .. "upside_down", {
|
||||
replace_name = "stairs:stair_" .. subname,
|
||||
groups = {slabs_replace = 1},
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'stairs:stair_' .. subname .. ' 6',
|
||||
recipe = {
|
||||
{recipeitem, "", ""},
|
||||
{recipeitem, recipeitem, ""},
|
||||
{recipeitem, recipeitem, recipeitem},
|
||||
},
|
||||
})
|
||||
|
||||
-- Flipped recipe for the silly minecrafters
|
||||
minetest.register_craft({
|
||||
output = 'stairs:stair_' .. subname .. ' 6',
|
||||
recipe = {
|
||||
{"", "", recipeitem},
|
||||
{"", recipeitem, recipeitem},
|
||||
{recipeitem, recipeitem, recipeitem},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- Register slabs.
|
||||
-- Node will be called stairs:slab_<subname>
|
||||
|
||||
function stairs.register_slab(subname, recipeitem, groups, images, description, sounds)
|
||||
groups.slab = 1
|
||||
minetest.register_node(":stairs:slab_" .. subname, {
|
||||
description = description,
|
||||
drawtype = "nodebox",
|
||||
tiles = images,
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
is_ground_content = false,
|
||||
groups = groups,
|
||||
sounds = sounds,
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {-0.5, -0.5, -0.5, 0.5, 0, 0.5},
|
||||
},
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
if pointed_thing.type ~= "node" then
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- If it's being placed on an another similar one, replace it with
|
||||
-- a full block
|
||||
local slabpos = nil
|
||||
local slabnode = nil
|
||||
local p0 = pointed_thing.under
|
||||
local p1 = pointed_thing.above
|
||||
local n0 = minetest.get_node(p0)
|
||||
local n1 = minetest.get_node(p1)
|
||||
local param2 = 0
|
||||
|
||||
local n0_is_upside_down = (n0.name == "stairs:slab_" .. subname and
|
||||
n0.param2 >= 20)
|
||||
|
||||
if n0.name == "stairs:slab_" .. subname and not n0_is_upside_down and
|
||||
p0.y + 1 == p1.y then
|
||||
slabpos = p0
|
||||
slabnode = n0
|
||||
elseif n1.name == "stairs:slab_" .. subname then
|
||||
slabpos = p1
|
||||
slabnode = n1
|
||||
end
|
||||
if slabpos then
|
||||
-- Remove the slab at slabpos
|
||||
minetest.remove_node(slabpos)
|
||||
-- Make a fake stack of a single item and try to place it
|
||||
local fakestack = ItemStack(recipeitem)
|
||||
fakestack:set_count(itemstack:get_count())
|
||||
|
||||
pointed_thing.above = slabpos
|
||||
local success
|
||||
fakestack, success = minetest.item_place(fakestack, placer,
|
||||
pointed_thing)
|
||||
-- If the item was taken from the fake stack, decrement original
|
||||
if success then
|
||||
itemstack:set_count(fakestack:get_count())
|
||||
-- Else put old node back
|
||||
else
|
||||
minetest.set_node(slabpos, slabnode)
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Upside down slabs
|
||||
if p0.y - 1 == p1.y then
|
||||
-- Turn into full block if pointing at a existing slab
|
||||
if n0_is_upside_down then
|
||||
-- Remove the slab at the position of the slab
|
||||
minetest.remove_node(p0)
|
||||
-- Make a fake stack of a single item and try to place it
|
||||
local fakestack = ItemStack(recipeitem)
|
||||
fakestack:set_count(itemstack:get_count())
|
||||
|
||||
pointed_thing.above = p0
|
||||
local success
|
||||
fakestack, success = minetest.item_place(fakestack, placer,
|
||||
pointed_thing)
|
||||
-- If the item was taken from the fake stack, decrement original
|
||||
if success then
|
||||
itemstack:set_count(fakestack:get_count())
|
||||
-- Else put old node back
|
||||
else
|
||||
minetest.set_node(p0, n0)
|
||||
end
|
||||
return itemstack
|
||||
end
|
||||
|
||||
-- Place upside down slab
|
||||
param2 = 20
|
||||
end
|
||||
|
||||
-- If pointing at the side of a upside down slab
|
||||
if n0_is_upside_down and p0.y + 1 ~= p1.y then
|
||||
param2 = 20
|
||||
end
|
||||
|
||||
return minetest.item_place(itemstack, placer, pointed_thing, param2)
|
||||
end,
|
||||
})
|
||||
|
||||
-- for replace ABM
|
||||
if replace then
|
||||
minetest.register_node(":stairs:slab_" .. subname .. "upside_down", {
|
||||
replace_name = "stairs:slab_".. subname,
|
||||
groups = {slabs_replace = 1},
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
output = 'stairs:slab_' .. subname .. ' 6',
|
||||
recipe = {
|
||||
{recipeitem, recipeitem, recipeitem},
|
||||
},
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- Optionally replace old "upside_down" nodes with new param2 versions.
|
||||
-- Disabled by default.
|
||||
|
||||
if replace then
|
||||
minetest.register_abm({
|
||||
nodenames = {"group:slabs_replace"},
|
||||
interval = 8,
|
||||
chance = 1,
|
||||
action = function(pos, node)
|
||||
node.name = minetest.registered_nodes[node.name].replace_name
|
||||
node.param2 = node.param2 + 20
|
||||
if node.param2 == 21 then
|
||||
node.param2 = 23
|
||||
elseif node.param2 == 23 then
|
||||
node.param2 = 21
|
||||
end
|
||||
minetest.set_node(pos, node)
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
-- Stair/slab registration function.
|
||||
-- Nodes will be called stairs:{stair,slab}_<subname>
|
||||
|
||||
function stairs.register_stair_and_slab(subname, recipeitem, groups, images,
|
||||
desc_stair, desc_slab, sounds)
|
||||
stairs.register_stair(subname, recipeitem, groups, images, desc_stair, sounds)
|
||||
stairs.register_slab(subname, recipeitem, groups, images, desc_slab, sounds)
|
||||
end
|
||||
|
||||
|
||||
-- Register default stairs and slabs
|
||||
|
||||
stairs.register_stair_and_slab("wood", "default:wood",
|
||||
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
|
||||
{"default_wood.png"},
|
||||
"Wooden Stair",
|
||||
"Wooden Slab",
|
||||
default.node_sound_wood_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("junglewood", "default:junglewood",
|
||||
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
|
||||
{"default_junglewood.png"},
|
||||
"Junglewood Stair",
|
||||
"Junglewood Slab",
|
||||
default.node_sound_wood_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("pine_wood", "default:pine_wood",
|
||||
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
|
||||
{"default_pine_wood.png"},
|
||||
"Pine Wood Stair",
|
||||
"Pine Wood Slab",
|
||||
default.node_sound_wood_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("acacia_wood", "default:acacia_wood",
|
||||
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
|
||||
{"default_acacia_wood.png"},
|
||||
"Acacia Wood Stair",
|
||||
"Acacia Wood Slab",
|
||||
default.node_sound_wood_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("aspen_wood", "default:aspen_wood",
|
||||
{snappy = 2, choppy = 2, oddly_breakable_by_hand = 2, flammable = 3},
|
||||
{"default_aspen_wood.png"},
|
||||
"Aspen Wood Stair",
|
||||
"Aspen Wood Slab",
|
||||
default.node_sound_wood_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("stone", "default:stone",
|
||||
{cracky = 3},
|
||||
{"default_stone.png"},
|
||||
"Stone Stair",
|
||||
"Stone Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("cobble", "default:cobble",
|
||||
{cracky = 3},
|
||||
{"default_cobble.png"},
|
||||
"Cobblestone Stair",
|
||||
"Cobblestone Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("stonebrick", "default:stonebrick",
|
||||
{cracky = 3},
|
||||
{"default_stone_brick.png"},
|
||||
"Stone Brick Stair",
|
||||
"Stone Brick Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("desert_stone", "default:desert_stone",
|
||||
{cracky = 3},
|
||||
{"default_desert_stone.png"},
|
||||
"Desertstone Stair",
|
||||
"Desertstone Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("desert_cobble", "default:desert_cobble",
|
||||
{cracky = 3},
|
||||
{"default_desert_cobble.png"},
|
||||
"Desert Cobblestone Stair",
|
||||
"Desert Cobblestone Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("desert_stonebrick", "default:desert_stonebrick",
|
||||
{cracky = 3},
|
||||
{"default_desert_stone_brick.png"},
|
||||
"Desert Stone Brick Stair",
|
||||
"Desert Stone Brick Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("sandstone", "default:sandstone",
|
||||
{crumbly = 2, cracky = 2},
|
||||
{"default_sandstone.png"},
|
||||
"Sandstone Stair",
|
||||
"Sandstone Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("sandstonebrick", "default:sandstonebrick",
|
||||
{crumbly = 2, cracky = 2},
|
||||
{"default_sandstone_brick.png"},
|
||||
"Sandstone Brick Stair",
|
||||
"Sandstone Brick Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("obsidian", "default:obsidian",
|
||||
{cracky = 1, level = 2},
|
||||
{"default_obsidian.png"},
|
||||
"Obsidian Stair",
|
||||
"Obsidian Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("obsidianbrick", "default:obsidianbrick",
|
||||
{cracky = 1, level = 2},
|
||||
{"default_obsidian_brick.png"},
|
||||
"Obsidian Brick Stair",
|
||||
"Obsidian Brick Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("brick", "default:brick",
|
||||
{cracky = 3},
|
||||
{"default_brick.png"},
|
||||
"Brick Stair",
|
||||
"Brick Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("straw", "farming:straw",
|
||||
{snappy = 3, flammable = 4},
|
||||
{"farming_straw.png"},
|
||||
"Straw Stair",
|
||||
"Straw Slab",
|
||||
default.node_sound_leaves_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("steelblock", "default:steelblock",
|
||||
{cracky = 1, level = 2},
|
||||
{"default_steel_block.png"},
|
||||
"Steel Block Stair",
|
||||
"Steel Block Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("copperblock", "default:copperblock",
|
||||
{cracky = 1, level = 2},
|
||||
{"default_copper_block.png"},
|
||||
"Copper Block Stair",
|
||||
"Copper Block Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("bronzeblock", "default:bronzeblock",
|
||||
{cracky = 1, level = 2},
|
||||
{"default_bronze_block.png"},
|
||||
"Bronze Block Stair",
|
||||
"Bronze Block Slab",
|
||||
default.node_sound_stone_defaults())
|
||||
|
||||
stairs.register_stair_and_slab("goldblock", "default:goldblock",
|
||||
{cracky = 1},
|
||||
{"default_gold_block.png"},
|
||||
"Gold Block Stair",
|
||||
"Gold Block Slab",
|
||||
default.node_sound_stone_defaults())
|
|
@ -0,0 +1,113 @@
|
|||
# Blender v2.72 (sub 0) OBJ File: ''
|
||||
# www.blender.org
|
||||
mtllib stairs.mtl
|
||||
o stairs_top
|
||||
v -0.500000 0.000000 -0.500000
|
||||
v -0.500000 0.000000 0.000000
|
||||
v 0.500000 0.000000 0.000000
|
||||
v 0.500000 0.000000 -0.500000
|
||||
v -0.500000 0.500000 0.000000
|
||||
v 0.500000 0.500000 0.000000
|
||||
v -0.500000 0.500000 0.500000
|
||||
v 0.500000 0.500000 0.500000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 0.500000
|
||||
vt 0.000000 0.500000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vn 0.000000 1.000000 0.000000
|
||||
g stairs_top
|
||||
usemtl None
|
||||
s off
|
||||
f 4/1/1 1/2/1 2/3/1 3/4/1
|
||||
f 7/5/1 8/6/1 6/4/1 5/3/1
|
||||
o stairs_bottom
|
||||
v -0.500000 -0.500000 -0.500000
|
||||
v 0.500000 -0.500000 -0.500000
|
||||
v -0.500000 -0.500000 0.500000
|
||||
v 0.500000 -0.500000 0.500000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vn 0.000000 -1.000000 -0.000000
|
||||
g stairs_bottom
|
||||
usemtl None
|
||||
s off
|
||||
f 11/7/2 9/8/2 10/9/2 12/10/2
|
||||
o stairs_right
|
||||
v -0.500000 0.000000 -0.500000
|
||||
v -0.500000 -0.500000 -0.500000
|
||||
v -0.500000 0.000000 0.000000
|
||||
v -0.500000 -0.500000 0.500000
|
||||
v -0.500000 0.500000 0.000000
|
||||
v -0.500000 0.500000 0.500000
|
||||
vt 0.000000 0.500000
|
||||
vt 0.000000 0.000000
|
||||
vt 0.500000 0.500000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.500000 1.000000
|
||||
vt 1.000000 0.000000
|
||||
vn -1.000000 0.000000 0.000000
|
||||
g stairs_right
|
||||
usemtl None
|
||||
s off
|
||||
f 13/11/3 14/12/3 15/13/3
|
||||
f 15/13/3 18/14/3 17/15/3
|
||||
f 14/12/3 16/16/3 18/14/3
|
||||
o stairs_left
|
||||
v 0.500000 0.000000 0.000000
|
||||
v 0.500000 -0.500000 -0.500000
|
||||
v 0.500000 0.000000 -0.500000
|
||||
v 0.500000 -0.500000 0.500000
|
||||
v 0.500000 0.500000 0.000000
|
||||
v 0.500000 0.500000 0.500000
|
||||
vt 0.500000 0.500000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 0.500000
|
||||
vt 0.500000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vn 1.000000 0.000000 0.000000
|
||||
g stairs_left
|
||||
usemtl None
|
||||
s off
|
||||
f 19/17/4 20/18/4 21/19/4
|
||||
f 19/17/4 23/20/4 24/21/4
|
||||
f 20/18/4 24/21/4 22/22/4
|
||||
o stairs_back
|
||||
v -0.500000 -0.500000 0.500000
|
||||
v 0.500000 -0.500000 0.500000
|
||||
v -0.500000 0.500000 0.500000
|
||||
v 0.500000 0.500000 0.500000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vt 0.000000 0.000000
|
||||
vn 0.000000 -0.000000 1.000000
|
||||
g stairs_back
|
||||
usemtl None
|
||||
s off
|
||||
f 26/23/5 28/24/5 27/25/5 25/26/5
|
||||
o stairs_front
|
||||
v -0.500000 0.000000 -0.500000
|
||||
v -0.500000 -0.500000 -0.500000
|
||||
v -0.500000 0.000000 0.000000
|
||||
v 0.500000 0.000000 0.000000
|
||||
v 0.500000 -0.500000 -0.500000
|
||||
v 0.500000 0.000000 -0.500000
|
||||
v -0.500000 0.500000 0.000000
|
||||
v 0.500000 0.500000 0.000000
|
||||
vt 1.000000 0.000000
|
||||
vt 1.000000 0.500000
|
||||
vt 0.000000 0.500000
|
||||
vt 0.000000 0.000000
|
||||
vt 1.000000 1.000000
|
||||
vt 0.000000 1.000000
|
||||
vn 0.000000 0.000000 -1.000000
|
||||
g stairs_front
|
||||
usemtl None
|
||||
s off
|
||||
f 30/27/6 29/28/6 34/29/6 33/30/6
|
||||
f 31/28/6 35/31/6 36/32/6 32/29/6
|
|
@ -1,38 +0,0 @@
|
|||
|
||||
xban.importers = { }
|
||||
|
||||
dofile(xban.MP.."/importers/minetest.lua")
|
||||
dofile(xban.MP.."/importers/v1.lua")
|
||||
dofile(xban.MP.."/importers/v2.lua")
|
||||
|
||||
minetest.register_chatcommand("xban_dbi", {
|
||||
description = "Import old databases",
|
||||
params = "<importer>",
|
||||
privs = { server=true },
|
||||
func = function(name, params)
|
||||
if params == "--list" then
|
||||
local names = { }
|
||||
for name in pairs(xban.importers) do
|
||||
table.insert(names, name)
|
||||
end
|
||||
minetest.chat_send_player(name,
|
||||
("[xban] Known importers: %s"):format(
|
||||
table.concat(names, ", ")))
|
||||
return
|
||||
elseif not xban.importers[params] then
|
||||
minetest.chat_send_player(name,
|
||||
("[xban] Unknown importer `%s'"):format(params))
|
||||
minetest.chat_send_player(name, "[xban] Try `--list'")
|
||||
return
|
||||
end
|
||||
local f = xban.importers[params]
|
||||
local ok, err = f()
|
||||
if ok then
|
||||
minetest.chat_send_player(name,
|
||||
"[xban] Import successfull")
|
||||
else
|
||||
minetest.chat_send_player(name,
|
||||
("[xban] Import failed: %s"):format(err))
|
||||
end
|
||||
end,
|
||||
})
|
|
@ -1,32 +0,0 @@
|
|||
|
||||
## Extended Ban Mod API
|
||||
|
||||
### ban_player
|
||||
|
||||
`xban.ban_player(player_or_ip, source, expires, reason)`
|
||||
|
||||
Ban a player and all of his/her alternative names and IPs.
|
||||
|
||||
#### Arguments:
|
||||
|
||||
* `player_or_ip` - Player to search for and ban. See note 1 below.
|
||||
* `source` - Source of the ban. See note 2 below.
|
||||
* `expires` - Time at which the ban expires. If nil, ban is permanent.
|
||||
* `reason` - Reason for ban.
|
||||
|
||||
### unban_player
|
||||
|
||||
`xban.unban_player(player_or_ip, source)`
|
||||
|
||||
Unban a player and all of his/her alternative names and IPs.
|
||||
|
||||
#### Arguments:
|
||||
|
||||
* `player_or_ip` - Player to search for and unban.
|
||||
* `source` - Source of the ban. See note 2 below.
|
||||
|
||||
### Notes
|
||||
|
||||
* 1: If player is currently online, all his accounts are kicked.
|
||||
* 2: Mods using the xban API are advised to use the `"modname:source"`
|
||||
format for `source` (for example: `"anticheat:main"`).
|
|
@ -1,45 +0,0 @@
|
|||
|
||||
Database is a regular Lua script that returns a table.
|
||||
|
||||
Table has a single named field `timestamp' containing the time_t the
|
||||
DB was last saved. It's not used in the mod and is only meant for
|
||||
external use (I don't find filesystem timestamps too reliable).
|
||||
|
||||
Next is a simple array (number indices) of entries.
|
||||
|
||||
Each entry contains following fields:
|
||||
|
||||
[1] = {
|
||||
-- Names/IPs associated with this entry
|
||||
names = {
|
||||
["foo"] = true,
|
||||
["bar"] = true,
|
||||
["123.45.67.89"] = true,
|
||||
},
|
||||
banned = true, -- Whether this user is banned
|
||||
-- Other fields do not apply if false
|
||||
time = 12341234, -- Time of last ban (*1)
|
||||
expires = 43214321 -- Time at which ban expires (*2)
|
||||
-- If nil, permanent ban
|
||||
reason = "asdf", -- Reason for ban
|
||||
source = "qwerty", -- Source of ban (*2)
|
||||
record = {
|
||||
[1] = {
|
||||
source = "asdf",
|
||||
reason = "qwerty",
|
||||
time = 12341234,
|
||||
expires = 43214321,
|
||||
},
|
||||
[1] = {
|
||||
source = "asdf",
|
||||
reason = "Unbanned", -- When unbanned
|
||||
time = 12341234,
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
Notes:
|
||||
(*1) All times are expressed in whatever unit `os.time()' uses
|
||||
(`time_t' on most (all?) systems).
|
||||
(*2) Mods using the xban API are advised to use the "modname:source"
|
||||
format for `source' (for example: "anticheat:main").
|
|
@ -1,90 +0,0 @@
|
|||
|
||||
local FORMNAME = "xban2:main"
|
||||
|
||||
local states = { }
|
||||
|
||||
local table_insert, table_concat =
|
||||
table.insert, table.concat
|
||||
|
||||
local ESC = minetest.formspec_escape
|
||||
|
||||
local function make_fs(name)
|
||||
local state = states[name]
|
||||
if not state then return end
|
||||
local list, index, filter = state.list, state.index, state.filter
|
||||
if index > #list then
|
||||
index = #list
|
||||
end
|
||||
local fs = {
|
||||
"size[10,8]",
|
||||
"label[0.5,0.6;Filter]",
|
||||
"field[1.5,0.5;6,2;filter;;"..ESC(filter).."]",
|
||||
"button[7.5,0.5;2,1;search;Search]",
|
||||
}
|
||||
table_insert(fs,
|
||||
("textlist[0.5,2;3,5.5;player;%s;%d;0]"):
|
||||
format(table_concat(list, ","), index))
|
||||
local record_name = list[index]
|
||||
if record_name then
|
||||
local record, err = xban.get_record(record_name)
|
||||
if record then
|
||||
local reclist = { }
|
||||
for _, r in ipairs(record) do
|
||||
table_insert(reclist, ESC(r))
|
||||
end
|
||||
table_insert(fs,
|
||||
("textlist[4,2;5,5.5;entry;%s;0;0]"):
|
||||
format(table_concat(reclist, ",")))
|
||||
else
|
||||
table_insert(fs,
|
||||
"textlist[4,2;5,5.5;entry;"..ESC(err)..";0]")
|
||||
end
|
||||
end
|
||||
return table_concat(fs)
|
||||
end
|
||||
|
||||
minetest.register_on_player_receive_fields(function(player, formname, fields)
|
||||
if formname ~= FORMNAME then return end
|
||||
local name = player:get_player_name()
|
||||
local state = states[name]
|
||||
if fields.player then
|
||||
local t = minetest.explode_textlist_event(fields.player)
|
||||
if (t.type == "CHG") or (t.type == "DCL") then
|
||||
state.index = t.index
|
||||
minetest.show_formspec(name, FORMNAME, make_fs(name))
|
||||
end
|
||||
return
|
||||
end
|
||||
if fields.search then
|
||||
local filter = fields.filter or ""
|
||||
state.filter = filter
|
||||
local list = { }
|
||||
state.list = list
|
||||
for k in pairs(minetest.auth_table) do
|
||||
if k:find(filter, 1, true) then
|
||||
table_insert(list, k)
|
||||
end
|
||||
end
|
||||
table.sort(list)
|
||||
minetest.show_formspec(name, FORMNAME, make_fs(name))
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_chatcommand("xban_gui", {
|
||||
description = "Show XBan GUI",
|
||||
params = "",
|
||||
func = function(name, params)
|
||||
local state = states[name]
|
||||
if not state then
|
||||
state = { index=1, filter="" }
|
||||
states[name] = state
|
||||
local list = { }
|
||||
state.list = list
|
||||
for k in pairs(minetest.auth_table) do
|
||||
table_insert(list, k)
|
||||
end
|
||||
table.sort(list)
|
||||
end
|
||||
minetest.show_formspec(name, FORMNAME, make_fs(name))
|
||||
end,
|
||||
})
|
|
@ -1,29 +0,0 @@
|
|||
|
||||
function xban.importers.minetest()
|
||||
local f, e = io.open(minetest.get_worldpath().."/ipban.txt")
|
||||
if not f then
|
||||
return false, "Unable to open `ipban.txt': "..e
|
||||
end
|
||||
for line in f:lines() do
|
||||
local ip, name = line:match("([^|]+)%|(.+)")
|
||||
if ip and name then
|
||||
local entry
|
||||
entry = xban.find_entry(ip, true)
|
||||
entry.banned = true
|
||||
entry.reason = "Banned in `ipban.txt'"
|
||||
entry.names[name] = true
|
||||
entry.names[ip] = true
|
||||
entry.time = os.time()
|
||||
entry.expires = nil
|
||||
entry.source = "xban:importer_minetest"
|
||||
table.insert(entry.record, {
|
||||
source = entry.source,
|
||||
reason = entry.reason,
|
||||
time = entry.time,
|
||||
expires = nil,
|
||||
})
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
return true
|
||||
end
|
|
@ -1,33 +0,0 @@
|
|||
|
||||
function xban.importers.v1()
|
||||
local f, e = io.open(minetest.get_worldpath().."/players.iplist")
|
||||
if not f then
|
||||
return false, "Unable to open `players.iplist': "..e
|
||||
end
|
||||
for line in f:lines() do
|
||||
local list = line:split("|")
|
||||
if #list >= 2 then
|
||||
local banned = (list[1]:sub(1, 1) == "!")
|
||||
local entry
|
||||
entry = xban.find_entry(list[1], true)
|
||||
entry.banned = banned
|
||||
for _, name in ipairs(list) do
|
||||
entry.names[name] = true
|
||||
end
|
||||
if banned then
|
||||
entry.reason = "Banned in `players.iplist'"
|
||||
entry.time = os.time()
|
||||
entry.expires = nil
|
||||
entry.source = "xban:importer_v1"
|
||||
table.insert(entry.record, {
|
||||
source = entry.source,
|
||||
reason = entry.reason,
|
||||
time = entry.time,
|
||||
expires = nil,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
f:close()
|
||||
return true
|
||||
end
|
|
@ -1,35 +0,0 @@
|
|||
|
||||
function xban.importers.v2()
|
||||
return pcall(function()
|
||||
local f, e = io.open(minetest.get_worldpath().."/players.iplist.v2")
|
||||
if not f then
|
||||
error("Unable to open `players.iplist.v2': "..e)
|
||||
end
|
||||
local text = f:read("*a")
|
||||
f:close()
|
||||
local db = minetest.deserialize(text)
|
||||
for _, e in ipairs(db) do
|
||||
for name in pairs(e.names) do
|
||||
local entry = xban.find_entry(name, true)
|
||||
if entry.source ~= "xban:importer_v2" then
|
||||
for nm in pairs(e.names) do
|
||||
entry.names[nm] = true
|
||||
end
|
||||
if e.banned then
|
||||
entry.banned = true
|
||||
entry.reason = e.banned
|
||||
entry.source = "xban:importer_v2"
|
||||
entry.time = e.time
|
||||
entry.expires = e.expires
|
||||
table.insert(entry.record, {
|
||||
source = entry.source,
|
||||
reason = entry.reason,
|
||||
time = entry.time,
|
||||
expires = entry.expires,
|
||||
})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
|
@ -1,327 +0,0 @@
|
|||
|
||||
xban = { MP = minetest.get_modpath(minetest.get_current_modname()) }
|
||||
|
||||
dofile(xban.MP.."/serialize.lua")
|
||||
|
||||
local db = { }
|
||||
local tempbans = { }
|
||||
|
||||
local DEF_SAVE_INTERVAL = 300 -- 5 minutes
|
||||
local DEF_DB_FILENAME = minetest.get_worldpath().."/xban.db"
|
||||
|
||||
local DB_FILENAME = minetest.setting_get("xban.db_filename")
|
||||
local SAVE_INTERVAL = tonumber(
|
||||
minetest.setting_get("xban.db_save_interval")) or DEF_SAVE_INTERVAL
|
||||
|
||||
if (not DB_FILENAME) or (DB_FILENAME == "") then
|
||||
DB_FILENAME = DEF_DB_FILENAME
|
||||
end
|
||||
|
||||
local function make_logger(level)
|
||||
return function(text, ...)
|
||||
minetest.log(level, "[xban] "..text:format(...))
|
||||
end
|
||||
end
|
||||
|
||||
local ACTION = make_logger("action")
|
||||
local INFO = make_logger("info")
|
||||
local WARNING = make_logger("warning")
|
||||
local ERROR = make_logger("error")
|
||||
|
||||
local unit_to_secs = {
|
||||
s = 1, m = 60, h = 3600,
|
||||
D = 86400, W = 604800, M = 2592000, Y = 31104000,
|
||||
[""] = 1,
|
||||
}
|
||||
|
||||
local function parse_time(t) --> secs
|
||||
local secs = 0
|
||||
for num, unit in t:gmatch("(%d+)([smhDWMY]?)") do
|
||||
secs = secs + (tonumber(num) * (unit_to_secs[unit] or 1))
|
||||
end
|
||||
return secs
|
||||
end
|
||||
|
||||
function xban.find_entry(player, create) --> entry, index
|
||||
for index, e in ipairs(db) do
|
||||
for name in pairs(e.names) do
|
||||
if name == player then
|
||||
return e, index
|
||||
end
|
||||
end
|
||||
end
|
||||
if create then
|
||||
print(("Created new entry for `%s'"):format(player))
|
||||
local e = {
|
||||
names = { [player]=true },
|
||||
banned = false,
|
||||
record = { },
|
||||
}
|
||||
table.insert(db, e)
|
||||
return e, #db
|
||||
end
|
||||
return nil
|
||||
end
|
||||
|
||||
function xban.get_info(player) --> ip_name_list, banned, last_record
|
||||
local e = xban.find_entry(player)
|
||||
if not e then
|
||||
return nil, "No such entry"
|
||||
end
|
||||
return e.names, e.banned, e.record[#e.record]
|
||||
end
|
||||
|
||||
function xban.ban_player(player, source, expires, reason) --> bool, err
|
||||
local e = xban.find_entry(player, true)
|
||||
local rec = {
|
||||
source = source,
|
||||
time = os.time(),
|
||||
expires = expires,
|
||||
reason = reason,
|
||||
}
|
||||
table.insert(e.record, rec)
|
||||
e.names[player] = true
|
||||
local pl = minetest.get_player_by_name(player)
|
||||
if pl then
|
||||
local ip = minetest.get_player_ip(player)
|
||||
if ip then
|
||||
e.names[ip] = true
|
||||
end
|
||||
e.last_pos = pl:getpos()
|
||||
end
|
||||
e.reason = reason
|
||||
e.time = rec.time
|
||||
e.expires = expires
|
||||
e.banned = true
|
||||
local msg
|
||||
local date = (expires and os.date("%c", expires)
|
||||
or "the end of time")
|
||||
if expires then
|
||||
table.insert(tempbans, e)
|
||||
msg = ("Banned: Expires: %s, Reason: %s"):format(date, reason)
|
||||
else
|
||||
msg = ("Banned: Reason: %s"):format(reason)
|
||||
end
|
||||
for nm in pairs(e.names) do
|
||||
minetest.kick_player(nm, msg)
|
||||
end
|
||||
ACTION("%s bans %s until %s for reason: %s", source, player,
|
||||
date, reason)
|
||||
ACTION("Banned Names/IPs: %s", table.concat(e.names, ", "))
|
||||
return true
|
||||
end
|
||||
|
||||
function xban.unban_player(player, source) --> bool, err
|
||||
local e = xban.find_entry(player)
|
||||
if not e then
|
||||
return nil, "No such entry"
|
||||
end
|
||||
local rec = {
|
||||
source = source,
|
||||
time = os.time(),
|
||||
reason = "Unbanned",
|
||||
}
|
||||
table.insert(e.record, rec)
|
||||
e.banned = false
|
||||
e.reason = nil
|
||||
e.expires = nil
|
||||
e.time = nil
|
||||
ACTION("%s unbans %s", source, player)
|
||||
ACTION("Unbanned Names/IPs: %s", table.concat(e.names, ", "))
|
||||
return true
|
||||
end
|
||||
|
||||
function xban.get_record(player)
|
||||
local e = xban.find_entry(player)
|
||||
if not e then
|
||||
return nil, ("No entry for `%s'"):format(player)
|
||||
elseif (not e.record) or (#e.record == 0) then
|
||||
return nil, ("`%s' has no ban records"):format(player)
|
||||
end
|
||||
local record = { }
|
||||
for _, rec in ipairs(e.record) do
|
||||
local msg = rec.reason or "No reason given."
|
||||
if rec.expires then
|
||||
msg = msg..(", Expires: %s"):format(os.date("%c", e.expires))
|
||||
end
|
||||
if rec.source then
|
||||
msg = msg..", Source: "..rec.source
|
||||
end
|
||||
table.insert(record, ("[%s]: %s"):format(os.date("%c", e.time), msg))
|
||||
end
|
||||
local last_pos
|
||||
if e.last_pos then
|
||||
last_pos = ("User was last seen at %s"):format(
|
||||
minetest.pos_to_string(e.last_pos))
|
||||
end
|
||||
return record, last_pos
|
||||
end
|
||||
|
||||
minetest.register_on_prejoinplayer(function(name, ip)
|
||||
local e = xban.find_entry(name) or xban.find_entry(ip)
|
||||
if not e then return end
|
||||
if e.banned then
|
||||
local date = (e.expires and os.date("%c", e.expires)
|
||||
or "the end of time")
|
||||
return ("Banned: Expires: %s, Reason: %s"):format(
|
||||
date, e.reason)
|
||||
end
|
||||
end)
|
||||
|
||||
minetest.register_on_joinplayer(function(player)
|
||||
local name = player:get_player_name()
|
||||
local e = xban.find_entry(name)
|
||||
local ip = minetest.get_player_ip(name)
|
||||
if not e then
|
||||
if ip then
|
||||
e = xban.find_entry(ip, true)
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
e.names[name] = true
|
||||
if ip then
|
||||
e.names[ip] = true
|
||||
end
|
||||
e.last_seen = os.time()
|
||||
end)
|
||||
|
||||
minetest.register_chatcommand("xban", {
|
||||
description = "XBan a player",
|
||||
params = "<player> <reason>",
|
||||
privs = { ban=true },
|
||||
func = function(name, params)
|
||||
local plname, reason = params:match("(%S+)%s+(.+)")
|
||||
if not (plname and reason) then
|
||||
return false, "Usage: /xban <player> <reason>"
|
||||
end
|
||||
xban.ban_player(plname, name, nil, reason)
|
||||
return true, ("Banned %s."):format(plname)
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("xtempban", {
|
||||
description = "XBan a player temporarily",
|
||||
params = "<player> <time> <reason>",
|
||||
privs = { ban=true },
|
||||
func = function(name, params)
|
||||
local plname, time, reason = params:match("(%S+)%s+(%S+)%s+(.+)")
|
||||
if not (plname and time and reason) then
|
||||
return false, "Usage: /xtempban <player> <time> <reason>"
|
||||
end
|
||||
time = parse_time(time)
|
||||
if time < 60 then
|
||||
return false, "You must ban for at least 60 seconds."
|
||||
end
|
||||
local expires = os.time() + time
|
||||
xban.ban_player(plname, name, expires, reason)
|
||||
return true, ("Banned %s until %s."):format(plname, os.date("%c", expires))
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("xunban", {
|
||||
description = "XUnBan a player",
|
||||
params = "<player_or_ip>",
|
||||
privs = { ban=true },
|
||||
func = function(name, params)
|
||||
local plname = params:match("%S+")
|
||||
if not plname then
|
||||
minetest.chat_send_player(name,
|
||||
"Usage: /xunban <player_or_ip>")
|
||||
return
|
||||
end
|
||||
local ok, e = xban.unban_player(plname, name)
|
||||
return ok, ok and ("Unbanned %s."):format(plname) or e
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("xban_record", {
|
||||
description = "Show the ban records of a player",
|
||||
params = "<player_or_ip>",
|
||||
privs = { ban=true },
|
||||
func = function(name, params)
|
||||
local plname = params:match("%S+")
|
||||
if not plname then
|
||||
return false, "Usage: /xban_record <player_or_ip>"
|
||||
end
|
||||
local record, last_pos = xban.get_record(plname)
|
||||
if not record then
|
||||
local err = last_pos
|
||||
minetest.chat_send_player(name, "[xban] "..err)
|
||||
return
|
||||
end
|
||||
for _, e in ipairs(record) do
|
||||
minetest.chat_send_player(name, "[xban] "..e)
|
||||
end
|
||||
if last_pos then
|
||||
minetest.chat_send_player(name, "[xban] "..last_pos)
|
||||
end
|
||||
return true, "Record listed."
|
||||
end,
|
||||
})
|
||||
|
||||
local function check_temp_bans()
|
||||
minetest.after(60, check_temp_bans)
|
||||
local to_rm = { }
|
||||
local now = os.time()
|
||||
for i, e in ipairs(tempbans) do
|
||||
if e.expires and (e.expires <= now) then
|
||||
table.insert(to_rm, i)
|
||||
e.banned = false
|
||||
e.expires = nil
|
||||
e.reason = nil
|
||||
e.time = nil
|
||||
end
|
||||
end
|
||||
for _, i in ipairs(to_rm) do
|
||||
table.remove(tempbans, i)
|
||||
end
|
||||
end
|
||||
|
||||
local function save_db()
|
||||
minetest.after(SAVE_INTERVAL, save_db)
|
||||
local f, e = io.open(DB_FILENAME, "wt")
|
||||
db.timestamp = os.time()
|
||||
if f then
|
||||
local ok = f:write(xban.serialize(db))
|
||||
WARNING("Unable to save database: %s", "Write failed")
|
||||
end
|
||||
if f then f:close() end
|
||||
return
|
||||
end
|
||||
|
||||
local function load_db()
|
||||
local f, e = io.open(DB_FILENAME, "rt")
|
||||
if not f then
|
||||
WARNING("Unable to load database: %s", e)
|
||||
return
|
||||
end
|
||||
local cont = f:read("*a")
|
||||
if not cont then
|
||||
WARNING("Unable to load database: %s", "Read failed")
|
||||
return
|
||||
end
|
||||
local t = minetest.deserialize(cont)
|
||||
if not t then
|
||||
WARNING("Unable to load database: %s",
|
||||
"Deserialization failed")
|
||||
return
|
||||
end
|
||||
db = t
|
||||
tempbans = { }
|
||||
for _, entry in ipairs(db) do
|
||||
if entry.banned and entry.expires then
|
||||
table.insert(tempbans, entry)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_on_shutdown(save_db)
|
||||
minetest.after(SAVE_INTERVAL, save_db)
|
||||
load_db()
|
||||
xban.db = db
|
||||
|
||||
minetest.after(1, check_temp_bans)
|
||||
|
||||
dofile(xban.MP.."/dbimport.lua")
|
||||
dofile(xban.MP.."/gui.lua")
|
|
@ -1,31 +0,0 @@
|
|||
|
||||
local function repr(x)
|
||||
if type(x) == "string" then
|
||||
return ("%q"):format(x)
|
||||
else
|
||||
return tostring(x)
|
||||
end
|
||||
end
|
||||
|
||||
local function my_serialize_2(t, level)
|
||||
level = level or 0
|
||||
local lines = { }
|
||||
local indent = ("\t"):rep(level)
|
||||
for k, v in pairs(t) do
|
||||
local typ = type(v)
|
||||
if typ == "table" then
|
||||
table.insert(lines,
|
||||
indent..("[%s] = {\n"):format(repr(k))
|
||||
..my_serialize_2(v, level + 1).."\n"
|
||||
..indent.."},")
|
||||
else
|
||||
table.insert(lines,
|
||||
indent..("[%s] = %s,"):format(repr(k), repr(v)))
|
||||
end
|
||||
end
|
||||
return table.concat(lines, "\n")
|
||||
end
|
||||
|
||||
function xban.serialize(t)
|
||||
return "return {\n"..my_serialize_2(t, 1).."\n}"
|
||||
end
|
|
@ -1,8 +1,16 @@
|
|||
xdecor.box = {
|
||||
slab_y = function(height, shift) return { -0.5, -0.5+(shift or 0), -0.5, 0.5, -0.5+height+(shift or 0), 0.5 } end,
|
||||
slab_z = function(depth) return { -0.5, -0.5, -0.5+depth, 0.5, 0.5, 0.5 } end,
|
||||
bar_y = function(radius) return {-radius, -0.5, -radius, radius, 0.5, radius} end,
|
||||
cuboid = function(radius_x, radius_y, radius_z) return {-radius_x, -radius_y, -radius_z, radius_x, radius_y, radius_z} end
|
||||
slab_y = function(height, shift)
|
||||
return { -0.5, -0.5+(shift or 0), -0.5, 0.5, -0.5+height+(shift or 0), 0.5 }
|
||||
end,
|
||||
slab_z = function(depth)
|
||||
return { -0.5, -0.5, -0.5+depth, 0.5, 0.5, 0.5 }
|
||||
end,
|
||||
bar_y = function(radius)
|
||||
return { -radius, -0.5, -radius, radius, 0.5, radius }
|
||||
end,
|
||||
cuboid = function(radius_x, radius_y, radius_z)
|
||||
return { -radius_x, -radius_y, -radius_z, radius_x, radius_y, radius_z }
|
||||
end
|
||||
}
|
||||
|
||||
xdecor.nodebox = {
|
||||
|
@ -10,10 +18,27 @@ xdecor.nodebox = {
|
|||
null = { type = "fixed", fixed = { 0, 0, 0, 0, 0, 0 } }
|
||||
}
|
||||
|
||||
xdecor.pixelnodebox = function(size, boxes)
|
||||
local fixed = {}
|
||||
for _, box in pairs(boxes) do
|
||||
local x, y, z, w, h, l = unpack(box)
|
||||
fixed[#fixed+1] = {
|
||||
(x / size) - 0.5,
|
||||
(y / size) - 0.5,
|
||||
(z / size) - 0.5,
|
||||
((x + w) / size) - 0.5,
|
||||
((y + h) / size) - 0.5,
|
||||
((z + l) / size) - 0.5
|
||||
}
|
||||
end
|
||||
return { type = "fixed", fixed = fixed }
|
||||
end
|
||||
|
||||
local mt = {}
|
||||
mt.__index = function(table, key)
|
||||
local ref = xdecor.box[key]
|
||||
local ref_type = type(ref)
|
||||
|
||||
if ref_type == "function" then
|
||||
return function(...)
|
||||
return { type = "fixed", fixed = ref(...) }
|
||||
|
@ -21,8 +46,11 @@ mt.__index = function(table, key)
|
|||
elseif ref_type == "table" then
|
||||
return { type = "fixed", fixed = ref }
|
||||
elseif ref_type == "nil" then
|
||||
error(key .. "could not be found among nodebox presets and functions")
|
||||
error(key.."could not be found among nodebox presets and functions")
|
||||
end
|
||||
error("unexpected datatype " .. tostring(type(ref)) .. " while looking for " .. key)
|
||||
|
||||
error("unexpected datatype "..tostring(type(ref)).." while looking for "..key)
|
||||
end
|
||||
|
||||
setmetatable(xdecor.nodebox, mt)
|
||||
|
||||
|
|
|
@ -1,40 +1,42 @@
|
|||
local default_can_dig = function(pos, _)
|
||||
--[[ local default_can_dig = function(pos, _)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
return inv:is_empty("main")
|
||||
end
|
||||
|
||||
local xbg = default.gui_bg..default.gui_bg_img..default.gui_slots
|
||||
end --]]
|
||||
|
||||
xbg = default.gui_bg..default.gui_bg_img..default.gui_slots
|
||||
local default_inventory_size = 32
|
||||
|
||||
local default_inventory_formspecs = {
|
||||
["8"] = "size[8,6]"..xbg..
|
||||
"list[context;main;0,0;8,1;]"..
|
||||
"list[current_player;main;0,2;8,4;]"..
|
||||
"listring[]"..
|
||||
default.get_hotbar_bg(0,2),
|
||||
["8"] = [[ size[8,6]
|
||||
list[context;main;0,0;8,1;]
|
||||
list[current_player;main;0,2;8,4;]
|
||||
listring[current_player;main]
|
||||
listring[context;main] ]]
|
||||
..default.get_hotbar_bg(0,2),
|
||||
|
||||
["16"] = "size[8,7]"..xbg..
|
||||
"list[context;main;0,0;8,2;]"..
|
||||
"list[current_player;main;0,3;8,4;]"..
|
||||
"listring[]"..
|
||||
default.get_hotbar_bg(0,3),
|
||||
["16"] = [[ size[8,7]
|
||||
list[context;main;0,0;8,2;]
|
||||
list[current_player;main;0,3;8,4;]
|
||||
listring[current_player;main]
|
||||
listring[context;main] ]]
|
||||
..default.get_hotbar_bg(0,3),
|
||||
|
||||
["24"] = "size[8,7.5]"..xbg..
|
||||
"list[context;main;0,0;8,3;]"..
|
||||
"list[current_player;main;0,3.5;8,1;]"..
|
||||
"list[current_player;main;0,4.75;8,3;8]"..
|
||||
"listring[current_name;main]"..
|
||||
"listring[current_player;main]"..
|
||||
default.get_hotbar_bg(0,3.5),
|
||||
["24"] = [[ size[8,8]
|
||||
list[context;main;0,0;8,3;]
|
||||
list[current_player;main;0,4;8,4;]
|
||||
listring[current_player;main]
|
||||
listring[context;main]" ]]
|
||||
..default.get_hotbar_bg(0,4),
|
||||
|
||||
["32"] = "size[8,9]"..xbg..
|
||||
"list[context;main;0,0.3;8,4;]"..
|
||||
"list[current_player;main;0,4.85;8,1;]"..
|
||||
"listring[]"..
|
||||
"list[current_player;main;0,6.08;8,3;8]"..
|
||||
default.get_hotbar_bg(0, 4.85)
|
||||
["32"] = [[ size[8,9]
|
||||
list[context;main;0,0.3;8,4;]
|
||||
list[current_player;main;0,4.85;8,1;]
|
||||
list[current_player;main;0,6.08;8,3;8]
|
||||
listring[current_player;main]
|
||||
listring[context;main] ]]
|
||||
..default.get_hotbar_bg(0,4.85)
|
||||
}
|
||||
|
||||
local function get_formspec_by_size(size)
|
||||
|
@ -42,14 +44,34 @@ local function get_formspec_by_size(size)
|
|||
return formspec or default_inventory_formspecs
|
||||
end
|
||||
|
||||
local function drop_stuff()
|
||||
return function(pos, oldnode, oldmetadata, digger)
|
||||
local meta = minetest.get_meta(pos)
|
||||
meta:from_table(oldmetadata)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
for i=1, inv:get_size("main") do
|
||||
local stack = inv:get_stack("main", i)
|
||||
if not stack:is_empty() then
|
||||
local p = {
|
||||
x = pos.x + math.random(0,5) / 5 - 0.5,
|
||||
y = pos.y,
|
||||
z = pos.z + math.random(0,5) / 5 - 0.5
|
||||
}
|
||||
minetest.add_item(p, stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function xdecor.register(name, def)
|
||||
def.drawtype = def.drawtype or (def.node_box and "nodebox")
|
||||
def.paramtype = def.paramtype or "light"
|
||||
def.sounds = def.sounds or default.node_sound_defaults()
|
||||
|
||||
if not (def.drawtype == "glasslike_framed" or
|
||||
def.drawtype == "glasslike_framed_optional" or def.drawtype == "plantlike" or
|
||||
def.drawtype == "signlike" or def.drawtype == "normal") then
|
||||
|
||||
if not (def.drawtype == "normal" or def.drawtype == "signlike" or
|
||||
def.drawtype == "plantlike" or def.drawtype == "glasslike_framed" or
|
||||
def.drawtype == "glasslike_framed_optional") then
|
||||
def.paramtype2 = def.paramtype2 or "facedir"
|
||||
end
|
||||
|
||||
|
@ -68,13 +90,14 @@ function xdecor.register(name, def)
|
|||
if infotext then
|
||||
meta:set_string("infotext", infotext)
|
||||
end
|
||||
|
||||
local size = inventory.size or default_inventory_size
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("main", size)
|
||||
meta:set_string("formspec", inventory.formspec or get_formspec_by_size(size))
|
||||
meta:set_string("formspec", (inventory.formspec or get_formspec_by_size(size))..xbg)
|
||||
end
|
||||
|
||||
def.can_dig = def.can_dig or default_can_dig
|
||||
def.after_dig_node = def.after_dig_node or drop_stuff()
|
||||
--def.can_dig = def.can_dig or default_can_dig
|
||||
elseif infotext and not def.on_construct then
|
||||
def.on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
|
@ -82,5 +105,5 @@ function xdecor.register(name, def)
|
|||
end
|
||||
end
|
||||
|
||||
minetest.register_node("xdecor:".. name, def)
|
||||
minetest.register_node("xdecor:"..name, def)
|
||||
end
|
||||
|
|
Before Width: | Height: | Size: 82 B After Width: | Height: | Size: 127 B |
After Width: | Height: | Size: 170 B |
After Width: | Height: | Size: 175 B |
After Width: | Height: | Size: 673 B |
After Width: | Height: | Size: 115 B |
After Width: | Height: | Size: 207 B |
Before Width: | Height: | Size: 181 B After Width: | Height: | Size: 2.1 KiB |
After Width: | Height: | Size: 247 B |
After Width: | Height: | Size: 194 B |
After Width: | Height: | Size: 231 B |
After Width: | Height: | Size: 235 B |
Before Width: | Height: | Size: 140 B After Width: | Height: | Size: 208 B |
After Width: | Height: | Size: 166 B |
After Width: | Height: | Size: 166 B |
After Width: | Height: | Size: 238 B |
After Width: | Height: | Size: 188 B |
After Width: | Height: | Size: 172 B |
After Width: | Height: | Size: 178 B |
After Width: | Height: | Size: 126 B |
After Width: | Height: | Size: 157 B |
After Width: | Height: | Size: 192 B |
After Width: | Height: | Size: 171 B |
After Width: | Height: | Size: 189 B |
After Width: | Height: | Size: 456 B |
After Width: | Height: | Size: 321 B |
Before Width: | Height: | Size: 237 B After Width: | Height: | Size: 193 B |
After Width: | Height: | Size: 449 B |
After Width: | Height: | Size: 440 B |
After Width: | Height: | Size: 331 B |
After Width: | Height: | Size: 224 B |
After Width: | Height: | Size: 381 B |
Before Width: | Height: | Size: 532 B After Width: | Height: | Size: 117 B |
Before Width: | Height: | Size: 294 B After Width: | Height: | Size: 265 B |
Before Width: | Height: | Size: 371 B After Width: | Height: | Size: 331 B |
Before Width: | Height: | Size: 349 B After Width: | Height: | Size: 310 B |
Before Width: | Height: | Size: 334 B After Width: | Height: | Size: 288 B |
Before Width: | Height: | Size: 312 B After Width: | Height: | Size: 288 B |
Before Width: | Height: | Size: 575 B After Width: | Height: | Size: 570 B |
Before Width: | Height: | Size: 248 B After Width: | Height: | Size: 415 B |
Before Width: | Height: | Size: 272 B After Width: | Height: | Size: 244 B |
Before Width: | Height: | Size: 479 B After Width: | Height: | Size: 403 B |
Before Width: | Height: | Size: 463 B After Width: | Height: | Size: 378 B |
Before Width: | Height: | Size: 375 B After Width: | Height: | Size: 334 B |
After Width: | Height: | Size: 678 B |
Before Width: | Height: | Size: 297 B After Width: | Height: | Size: 220 B |
Before Width: | Height: | Size: 227 B After Width: | Height: | Size: 219 B |
Before Width: | Height: | Size: 405 B After Width: | Height: | Size: 332 B |
After Width: | Height: | Size: 256 B |
After Width: | Height: | Size: 392 B |
After Width: | Height: | Size: 370 B |
After Width: | Height: | Size: 385 B |
After Width: | Height: | Size: 260 B |
Before Width: | Height: | Size: 1.8 KiB After Width: | Height: | Size: 1.8 KiB |
After Width: | Height: | Size: 277 B |
After Width: | Height: | Size: 267 B |
After Width: | Height: | Size: 135 B |
Before Width: | Height: | Size: 312 B After Width: | Height: | Size: 300 B |
|
@ -1,195 +1,418 @@
|
|||
local worktable = {}
|
||||
local xbg = default.gui_bg..default.gui_bg_img..default.gui_slots
|
||||
screwdriver = screwdriver or {}
|
||||
|
||||
local nodes = { -- Nodes allowed to be cut.
|
||||
"default:wood", "default:junglewood", "default:pine_wood", "default:acacia_wood",
|
||||
"default:tree", "default:jungletree", "default:pine_tree", "default:acacia_tree",
|
||||
"default:pinewood", "default:pinetree",
|
||||
"default:cobble", "default:mossycobble", "default:desert_cobble",
|
||||
"default:stone", "default:sandstone", "default:desert_stone", "default:obsidian",
|
||||
"default:stonebrick", "default:sandstonebrick", "default:desert_stonebrick", "default:obsidianbrick",
|
||||
"default:coalblock", "default:copperblock", "default:steelblock", "default:goldblock",
|
||||
"default:bronzeblock", "default:mese", "default:diamondblock",
|
||||
"default:brick", "default:cactus", "default:ice", "default:meselamp",
|
||||
"default:glass", "default:obsidian_glass", "default:gravel",
|
||||
"default:leaves", "default:dirt", "default:snowblock", "default:sand",
|
||||
"default:desert_sand", "default:clay",
|
||||
-- Nodes allowed to be cut.
|
||||
-- Registration format: [mod name] = [[ node names ]].
|
||||
worktable.nodes = {
|
||||
["default"] = [[
|
||||
wood junglewood pine_wood acacia_wood
|
||||
tree jungletree pine_tree acacia_tree
|
||||
pinewood pinetree
|
||||
cobble mossycobble desert_cobble
|
||||
stone sandstone desert_stone obsidian
|
||||
stonebrick sandstonebrick desert_stonebrick obsidianbrick
|
||||
coalblock copperblock steelblock goldblock
|
||||
bronzeblock mese diamondblock
|
||||
brick cactus ice meselamp glass obsidian_glass
|
||||
clay desert_sand sand snowblock dirt
|
||||
leaves gravel
|
||||
]],
|
||||
|
||||
"farming:straw",
|
||||
["xdecor"] = [[
|
||||
coalstone_tile desertstone_tile stone_rune stone_tile
|
||||
cactusbrick hard_clay packed_ice moonbrick
|
||||
woodframed_glass wood_tile
|
||||
]],
|
||||
|
||||
"bones:bones",
|
||||
["oresplus"] = [[
|
||||
emerald_block glowstone
|
||||
]],
|
||||
|
||||
"oresplus:emerald_block", "oresplus:glowstone",
|
||||
["farming"] = "straw",
|
||||
|
||||
"xdecor:coalstone_tile", "xdecor:desertstone_tile", "xdecor:stone_rune", "xdecor:stone_tile",
|
||||
"xdecor:hard_clay", "xdecor:packed_ice", "xdecor:moonbrick",
|
||||
"xdecor:woodframed_glass", "xdecor:wood_tile",
|
||||
["bones"] = "bones",
|
||||
|
||||
"wool:black", "wool:brown", "wool:dark_green", "wool:green",
|
||||
"wool:magenta", "wool:pink", "wool:violet", "wool:yellow",
|
||||
"wool:blue", "wool:cyan", "wool:dark_grey", "wool:grey",
|
||||
"wool:orange", "wool:red", "wool:white",
|
||||
["wool"] = [[
|
||||
black brown dark_green green
|
||||
magenta pink violet yellow
|
||||
blue cyan dark_grey grey
|
||||
orange red white
|
||||
]],
|
||||
|
||||
"caverealms:glow_crystal", "caverealms:glow_emerald", "caverealms:glow_mese",
|
||||
"caverealms:glow_ruby", "caverealms:glow_amethyst", "caverealms:glow_ore",
|
||||
"caverealms:glow_emerald_ore", "caverealms:glow_ruby_ore", "caverealms:glow_amethyst_ore",
|
||||
"caverealms:thin_ice", "caverealms:salt_crystal", "caverealms:mushroom_cap",
|
||||
"caverealms:mushroom_stem", "caverealms:stone_with_salt", "caverealms:hot_cobble",
|
||||
"caverealms:glow_obsidian", "caverealms:glow_obsidian_2", "caverealms:coal_dust"
|
||||
["caverealms"] = [[
|
||||
glow_crystal glow_emerald glow_mese
|
||||
glow_ruby glow_amethyst glow_ore
|
||||
glow_emerald_ore glow_ruby_ore glow_amethyst_ore
|
||||
thin_ice salt_crystal mushroom_cap
|
||||
mushroom_stem stone_with_salt hot_cobble
|
||||
glow_obsidian glow_obsidian_2 coal_dust
|
||||
]]
|
||||
}
|
||||
|
||||
local def = { -- Nodebox name, anzhal, definition.
|
||||
{"nanoslab", 16, {-.5,-.5,-.5,0,-.4375,0}},
|
||||
{"micropanel", 16, {-.5,-.5,-.5,.5,-.4375,0}},
|
||||
{"microslab", 8, {-.5,-.5,-.5,.5,-.4375,.5}},
|
||||
{"thinstair", 8, {{-.5,-.0625,-.5,.5,0,0},{-.5,.4375,0,.5,.5,.5}}},
|
||||
{"cube", 4, {-.5,-.5,0,0,0,.5}},
|
||||
{"panel", 4, {-.5,-.5,-.5,.5,0,0}},
|
||||
{"slab", 2, {-.5,-.5,-.5,.5,0,.5}},
|
||||
{"doublepanel", 2, {{-.5,-.5,-.5,.5,0,0},{-.5,0,0,.5,.5,.5}}},
|
||||
{"halfstair", 2, {{-.5,-.5,-.5,0,0,.5},{-.5,0,0,0,.5,.5}}},
|
||||
{"outerstair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,0,.5,.5}}},
|
||||
{"stair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,.5,.5,.5}}},
|
||||
{"innerstair", 1, {{-.5,-.5,-.5,.5,0,.5},{-.5,0,0,.5,.5,.5},{-.5,0,-.5,0,.5,0}}}
|
||||
-- Nodeboxes definitions.
|
||||
worktable.defs = {
|
||||
-- Name Yield X Y Z W H L
|
||||
{"nanoslab", 16, { 0, 0, 0, 8, 1, 8 }},
|
||||
{"micropanel", 16, { 0, 0, 0, 16, 1, 8 }},
|
||||
{"microslab", 8, { 0, 0, 0, 16, 1, 16 }},
|
||||
{"thinstair", 8, { 0, 7, 0, 16, 1, 8 },
|
||||
{ 0, 15, 8, 16, 1, 8 }},
|
||||
{"cube", 4, { 0, 0, 0, 8, 8, 8 }},
|
||||
{"panel", 4, { 0, 0, 0, 16, 8, 8 }},
|
||||
{"slab", 2, nil },
|
||||
{"doublepanel", 2, { 0, 0, 0, 16, 8, 8 },
|
||||
{ 0, 8, 8, 16, 8, 8 }},
|
||||
{"halfstair", 2, { 0, 0, 0, 8, 8, 16 },
|
||||
{ 0, 8, 8, 8, 8, 8 }},
|
||||
{"outerstair", 1, { 0, 0, 0, 16, 8, 16 },
|
||||
{ 0, 8, 8, 8, 8, 8 }},
|
||||
{"stair", 1, nil },
|
||||
{"innerstair", 1, { 0, 0, 0, 16, 8, 16 },
|
||||
{ 0, 8, 8, 16, 8, 8 },
|
||||
{ 0, 8, 0, 8, 8, 8 }}
|
||||
}
|
||||
|
||||
function worktable.crafting(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
return "size[8,7;]"..xbg..
|
||||
"list[current_player;main;0,3.3;8,4;]"..
|
||||
"image[5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
|
||||
"list[current_player;craft;2,0;3,3;]"..
|
||||
"list[current_player;craftpreview;6,1;1,1;]"
|
||||
function worktable.get_recipe(item)
|
||||
if item:find("^group:") then
|
||||
if item:find("wool$") or item:find("dye$") then
|
||||
item = item:sub(7)..":white"
|
||||
elseif minetest.registered_items["default:"..item:sub(7)] then
|
||||
item = item:gsub("group:", "default:")
|
||||
else
|
||||
for node, def in pairs(minetest.registered_items) do
|
||||
if def.groups[item:match("[^,:]+$")] then
|
||||
item = node
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return item
|
||||
end
|
||||
|
||||
function worktable.storage(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local f = "size[8,7]"..xbg..
|
||||
"list[context;storage;0,0;8,2;]list[current_player;main;0,3.25;8,4;]"
|
||||
inv:set_size("storage", 8*2)
|
||||
return f
|
||||
function worktable.craftguide_formspec(meta, pagenum, item, recipe_num, filter, tab_id)
|
||||
local inv_size = meta:get_int("inv_size")
|
||||
local npp, i, s = 8*3, 0, 0
|
||||
local pagemax = math.floor((inv_size - 1) / npp + 1)
|
||||
|
||||
if pagenum > pagemax then
|
||||
pagenum = 1
|
||||
elseif pagenum == 0 then
|
||||
pagenum = pagemax
|
||||
end
|
||||
|
||||
local formspec = [[ size[8,6.6;]
|
||||
tablecolumns[color;text;color;text]
|
||||
tableoptions[background=#00000000;highlight=#00000000;border=false]
|
||||
button[5.5,0;0.7,1;prev;<]
|
||||
button[7.3,0;0.7,1;next;>]
|
||||
button[4,0.2;0.7,0.5;search;?]
|
||||
button[4.6,0.2;0.7,0.5;clearfilter;X]
|
||||
button[0,0;1.5,1;backcraft;< Back]
|
||||
tooltip[search;Search]
|
||||
tooltip[clearfilter;Reset] ]]
|
||||
.."tabheader[0,0;tabs;All,Nodes,Tools,Items;"..tostring(tab_id)..";true;false]"..
|
||||
"table[6.1,0.2;1.1,0.5;pagenum;#FFFF00,"..tostring(pagenum)..
|
||||
",#FFFFFF,/ "..tostring(pagemax).."]"..
|
||||
"field[1.8,0.32;2.6,1;filter;;"..filter.."]"..xbg
|
||||
|
||||
for _, name in pairs(worktable.craftguide_main_list(meta, filter, tab_id)) do
|
||||
if s < (pagenum - 1) * npp then
|
||||
s = s + 1
|
||||
else
|
||||
if i >= npp then break end
|
||||
formspec = formspec.."item_image_button["..(i%8)..","..
|
||||
(math.floor(i/8)+1)..";1,1;"..name..";"..name..";]"
|
||||
i = i + 1
|
||||
end
|
||||
end
|
||||
|
||||
if item and minetest.registered_items[item] then
|
||||
--print(dump(minetest.get_all_craft_recipes(item)))
|
||||
local items_num = #minetest.get_all_craft_recipes(item)
|
||||
if recipe_num > items_num then recipe_num = 1 end
|
||||
|
||||
if items_num > 1 then
|
||||
formspec = formspec.."button[0,6;1.6,1;alternate;Alternate]"..
|
||||
"label[0,5.5;Recipe "..recipe_num.." of "..items_num.."]"
|
||||
end
|
||||
|
||||
local type = minetest.get_all_craft_recipes(item)[recipe_num].type
|
||||
if type == "cooking" then
|
||||
formspec = formspec.."image[3.75,4.6;0.5,0.5;default_furnace_fire_fg.png]"
|
||||
end
|
||||
|
||||
local items = minetest.get_all_craft_recipes(item)[recipe_num].items
|
||||
local width = minetest.get_all_craft_recipes(item)[recipe_num].width
|
||||
local yield = minetest.get_all_craft_recipes(item)[recipe_num].output:match("%s(%d+)") or ""
|
||||
if width == 0 then width = math.min(3, #items) end
|
||||
local rows = math.ceil(table.maxn(items) / width)
|
||||
|
||||
local function is_group(item)
|
||||
if item:find("^group:") then return "G" end
|
||||
return ""
|
||||
end
|
||||
|
||||
for i, v in pairs(items) do
|
||||
formspec = formspec.."item_image_button["..((i-1) % width + 4.5)..","..
|
||||
(math.floor((i-1) / width + (6 - math.min(2, rows))))..";1,1;"..
|
||||
worktable.get_recipe(v)..";"..worktable.get_recipe(v)..";"..is_group(v).."]"
|
||||
end
|
||||
|
||||
formspec = formspec.."item_image_button[2.5,5;1,1;"..item..";"..item..";"..yield.."]"..
|
||||
"image[3.5,5;1,1;gui_furnace_arrow_bg.png^[transformR90]"
|
||||
end
|
||||
|
||||
meta:set_string("formspec", formspec)
|
||||
end
|
||||
|
||||
local function tab_category(tab_id)
|
||||
local id_category = {
|
||||
minetest.registered_items,
|
||||
minetest.registered_nodes,
|
||||
minetest.registered_tools,
|
||||
minetest.registered_craftitems
|
||||
}
|
||||
|
||||
return id_category[tab_id] or id_category[1]
|
||||
end
|
||||
|
||||
function worktable.craftguide_main_list(meta, filter, tab_id)
|
||||
local items_list = {}
|
||||
for name, def in pairs(tab_category(tab_id)) do
|
||||
if not (def.groups.not_in_creative_inventory == 1) and
|
||||
minetest.get_craft_recipe(name).items and
|
||||
def.description and def.description ~= "" and
|
||||
(not filter or def.name:find(filter, 1, true)) then
|
||||
items_list[#items_list+1] = name
|
||||
end
|
||||
end
|
||||
|
||||
meta:set_int("inv_size", #items_list)
|
||||
table.sort(items_list)
|
||||
return items_list
|
||||
end
|
||||
|
||||
worktable.formspecs = {
|
||||
crafting = function(meta)
|
||||
meta:set_string("formspec", [[ size[8,7;]
|
||||
image[5,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
|
||||
image[0.06,2.12;0.8,0.8;trash_icon.png]
|
||||
button[0,0;1.5,1;back;< Back]
|
||||
button[0,0.85;1.5,1;craftguide;Guide]
|
||||
list[context;trash;0,2;1,1;]
|
||||
list[current_player;main;0,3.3;8,4;]
|
||||
list[current_player;craft;2,0;3,3;]
|
||||
list[current_player;craftpreview;6,1;1,1;]
|
||||
listring[current_player;main]
|
||||
listring[current_player;craft] ]]
|
||||
..xbg..default.get_hotbar_bg(0,3.3))
|
||||
end,
|
||||
storage = function(meta)
|
||||
meta:set_string("formspec", [[ size[8,7]
|
||||
image[7.06,0.12;0.8,0.8;trash_icon.png]
|
||||
list[context;trash;7,0;1,1;]
|
||||
list[context;storage;0,1;8,2;]
|
||||
list[current_player;main;0,3.25;8,4;]
|
||||
listring[context;storage]
|
||||
listring[current_player;main]
|
||||
button[0,0;1.5,1;back;< Back] ]]
|
||||
..xbg..default.get_hotbar_bg(0,3.25))
|
||||
end,
|
||||
main = function(meta)
|
||||
meta:set_string("formspec", [[ size[8,7;]
|
||||
label[0.9,1.23;Cut]
|
||||
label[0.9,2.23;Repair]
|
||||
box[-0.05,1;2.05,0.9;#555555]
|
||||
box[-0.05,2;2.05,0.9;#555555]
|
||||
image[3,1;1,1;gui_furnace_arrow_bg.png^[transformR270]
|
||||
image[0,1;1,1;worktable_saw.png]
|
||||
image[0,2;1,1;worktable_anvil.png]
|
||||
image[3,2;1,1;hammer_layout.png]
|
||||
list[context;input;2,1;1,1;]
|
||||
list[context;tool;2,2;1,1;]
|
||||
list[context;hammer;3,2;1,1;]
|
||||
list[context;forms;4,0;4,3;]
|
||||
list[current_player;main;0,3.25;8,4;]
|
||||
button[0,0;2,1;craft;Crafting]
|
||||
button[2,0;2,1;storage;Storage] ]]
|
||||
..xbg..default.get_hotbar_bg(0,3.25))
|
||||
end
|
||||
}
|
||||
|
||||
function worktable.construct(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
inv:set_size("forms", 4*3)
|
||||
inv:set_size("input", 1)
|
||||
inv:set_size("tool", 1)
|
||||
inv:set_size("trash", 1)
|
||||
inv:set_size("input", 1)
|
||||
inv:set_size("hammer", 1)
|
||||
|
||||
local formspec = "size[8,7;]"..xbg..
|
||||
"list[context;forms;4,0;4,3;]" ..
|
||||
"label[0.95,1.23;Cut]box[-0.05,1;2.05,0.9;#555555]"..
|
||||
"image[3,1;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
|
||||
"label[0.95,2.23;Repair]box[-0.05,2;2.05,0.9;#555555]"..
|
||||
"image[0,1;1,1;xdecor_saw.png]image[0,2;1,1;xdecor_anvil.png]"..
|
||||
"image[3,2;1,1;hammer_layout.png]"..
|
||||
"list[current_name;input;2,1;1,1;]"..
|
||||
"list[current_name;tool;2,2;1,1;]list[current_name;hammer;3,2;1,1;]"..
|
||||
"button[0,0;2,1;craft;Crafting]"..
|
||||
"button[2,0;2,1;storage;Storage]"..
|
||||
"list[current_player;main;0,3.25;8,4;]"
|
||||
|
||||
meta:set_string("formspec", formspec)
|
||||
inv:set_size("forms", 4*3)
|
||||
inv:set_size("storage", 8*2)
|
||||
meta:set_string("infotext", "Work Table")
|
||||
|
||||
worktable.formspecs.main(meta)
|
||||
end
|
||||
|
||||
function worktable.fields(pos, _, fields, sender)
|
||||
local player = sender:get_player_name()
|
||||
function worktable.fields(pos, _, fields)
|
||||
if fields.quit then return end
|
||||
local meta = minetest.get_meta(pos)
|
||||
local formspec = meta:to_table().fields.formspec
|
||||
local filter = formspec:match("filter;;([%w_:]+)") or ""
|
||||
local pagenum = tonumber(formspec:match("#FFFF00,(%d+)")) or 1
|
||||
local tab_id = tonumber(formspec:match("tabheader%[.*;(%d+)%;.*%]")) or 1
|
||||
|
||||
if fields.back then
|
||||
worktable.formspecs.main(meta)
|
||||
elseif fields.craft or fields.backcraft then
|
||||
worktable.formspecs.crafting(meta)
|
||||
elseif fields.storage then
|
||||
worktable.formspecs.storage(meta)
|
||||
elseif fields.craftguide or fields.clearfilter then
|
||||
worktable.craftguide_main_list(meta, nil, tab_id)
|
||||
worktable.craftguide_formspec(meta, 1, nil, 1, "", tab_id)
|
||||
elseif fields.alternate then
|
||||
local item = formspec:match("item_image_button%[.*;([%w_:]+);.*%]") or ""
|
||||
local recipe_num = tonumber(formspec:match("Recipe%s(%d+)")) or 1
|
||||
recipe_num = recipe_num + 1
|
||||
worktable.craftguide_formspec(meta, pagenum, item, recipe_num, filter, tab_id)
|
||||
elseif fields.search then
|
||||
worktable.craftguide_main_list(meta, fields.filter:lower(), tab_id)
|
||||
worktable.craftguide_formspec(meta, 1, nil, 1, fields.filter:lower(), tab_id)
|
||||
elseif fields.tabs then
|
||||
worktable.craftguide_main_list(meta, filter, tonumber(fields.tabs))
|
||||
worktable.craftguide_formspec(meta, 1, nil, 1, filter, tonumber(fields.tabs))
|
||||
elseif fields.prev or fields.next then
|
||||
if fields.prev then
|
||||
pagenum = pagenum - 1
|
||||
else
|
||||
pagenum = pagenum + 1
|
||||
end
|
||||
worktable.craftguide_formspec(meta, pagenum, nil, 1, filter, tab_id)
|
||||
else
|
||||
for item in pairs(fields) do
|
||||
if item:match(".-:") and minetest.get_craft_recipe(item).items then
|
||||
worktable.craftguide_formspec(meta, pagenum, item, 1, filter, tab_id)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
function worktable.dig(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
|
||||
if fields.storage then
|
||||
minetest.show_formspec(player, "", worktable.storage(pos))
|
||||
end
|
||||
if fields.craft then
|
||||
minetest.show_formspec(player, "", worktable.crafting(pos))
|
||||
end
|
||||
return inv:is_empty("input") and inv:is_empty("hammer") and
|
||||
inv:is_empty("tool") and inv:is_empty("storage")
|
||||
end
|
||||
|
||||
function worktable.dig(pos, _)
|
||||
function worktable.allowed(mod, node)
|
||||
if not mod then return end
|
||||
for it in mod:gmatch("[%w_]+") do
|
||||
if it == node then return true end
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function trash_delete(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
if not inv:is_empty("input") or not inv:is_empty("hammer") or not
|
||||
inv:is_empty("tool") or not inv:is_empty("storage") then
|
||||
return false
|
||||
end
|
||||
return true
|
||||
minetest.after(0, function()
|
||||
inv:set_stack("trash", 1, "")
|
||||
end)
|
||||
end
|
||||
|
||||
function worktable.put(_, listname, _, stack, _)
|
||||
local stn = stack:get_name()
|
||||
local count = stack:get_count()
|
||||
local mat = table.concat(nodes)
|
||||
function worktable.put(pos, listname, _, stack)
|
||||
local stackname = stack:get_name()
|
||||
local mod, node = stackname:match("(.*):(.*)")
|
||||
local allowed_tools = "pick, axe, shovel, sword, hoe, armor"
|
||||
|
||||
if listname == "forms" then return 0 end
|
||||
if listname == "input" then
|
||||
if mat:match(stn) then return count end
|
||||
for v in allowed_tools:gmatch("[%w_]+") do
|
||||
if listname == "tool" and stack:get_wear() > 0 and stackname:find(v) then
|
||||
return stack:get_count()
|
||||
end
|
||||
end
|
||||
if (listname == "input" and worktable.allowed(worktable.nodes[mod], node)) or
|
||||
(listname == "hammer" and stackname == "xdecor:hammer") or
|
||||
listname == "storage" or listname == "trash" then
|
||||
if listname == "trash" then trash_delete(pos) end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
return 0
|
||||
end
|
||||
|
||||
function worktable.take(_, listname, _, stack, player)
|
||||
if listname == "forms" then
|
||||
local inv = player:get_inventory()
|
||||
if inv:room_for_item("main", stack:get_name()) then
|
||||
return -1
|
||||
end
|
||||
return 0
|
||||
end
|
||||
if listname == "hammer" then
|
||||
if stn ~= "xdecor:hammer" then return 0 end
|
||||
end
|
||||
if listname == "tool" then
|
||||
local tdef = minetest.registered_tools[stn]
|
||||
local twear = stack:get_wear()
|
||||
if not (tdef and twear > 0) then return 0 end
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
function worktable.take(_, listname, _, stack, _)
|
||||
if listname == "forms" then return -1 end
|
||||
return stack:get_count()
|
||||
end
|
||||
|
||||
function worktable.move(_, from_list, _, to_list, _, count, _)
|
||||
if from_list == "storage" and to_list == "storage" then
|
||||
return count else return 0 end
|
||||
function worktable.move(pos, _, _, to_list, _, count)
|
||||
if to_list == "storage" then
|
||||
return count
|
||||
elseif to_list == "trash" then
|
||||
trash_delete(pos)
|
||||
return count
|
||||
end
|
||||
return 0
|
||||
end
|
||||
|
||||
local function update_inventory(inv, inputstack)
|
||||
if inv:is_empty("input") then inv:set_list("forms", {}) return end
|
||||
local output = {}
|
||||
|
||||
for _, n in pairs(def) do
|
||||
local mat = inputstack:get_name()
|
||||
local input = inv:get_stack("input", 1)
|
||||
local count = math.min(n[2] * input:get_count(), inputstack:get_stack_max())
|
||||
|
||||
output[#output+1] = string.format("%s_%s %d", mat, n[1], count)
|
||||
function worktable.get_output(inv, input, name)
|
||||
if inv:is_empty("input") then
|
||||
inv:set_list("forms", {})
|
||||
return
|
||||
end
|
||||
|
||||
local output = {}
|
||||
for _, n in pairs(worktable.defs) do
|
||||
local count = math.min(n[2] * input:get_count(), input:get_stack_max())
|
||||
local item = name.."_"..n[1]
|
||||
if not n[3] then item = "stairs:"..n[1].."_"..name:match(":(.*)") end
|
||||
|
||||
output[#output+1] = item.." "..count
|
||||
end
|
||||
|
||||
inv:set_list("forms", output)
|
||||
end
|
||||
|
||||
function worktable.on_put(pos, listname, _, stack, _)
|
||||
function worktable.on_put(pos, listname, _, stack)
|
||||
if listname == "input" then
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
update_inventory(inv, stack)
|
||||
local input = inv:get_stack("input", 1)
|
||||
worktable.get_output(inv, input, stack:get_name())
|
||||
end
|
||||
end
|
||||
|
||||
function worktable.on_take(pos, listname, index, stack, _)
|
||||
function worktable.on_take(pos, listname, index, stack)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local input = inv:get_stack("input", 1)
|
||||
|
||||
if listname == "input" then
|
||||
update_inventory(inv, stack)
|
||||
if stack:get_name() == input:get_name() then
|
||||
worktable.get_output(inv, input, stack:get_name())
|
||||
else
|
||||
inv:set_list("forms", {})
|
||||
end
|
||||
elseif listname == "forms" then
|
||||
local inputstack = inv:get_stack("input", 1)
|
||||
inputstack:take_item(math.ceil(stack:get_count() / def[index][2]))
|
||||
inv:set_stack("input", 1, inputstack)
|
||||
update_inventory(inv, inputstack)
|
||||
input:take_item(math.ceil(stack:get_count() / worktable.defs[index][2]))
|
||||
inv:set_stack("input", 1, input)
|
||||
worktable.get_output(inv, input, input:get_name())
|
||||
end
|
||||
end
|
||||
|
||||
xdecor.register("worktable", {
|
||||
description = "Work Table",
|
||||
groups = {cracky=2, choppy=2},
|
||||
groups = {cracky=2, choppy=2, oddly_breakable_by_hand=1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
tiles = {
|
||||
"xdecor_worktable_top.png", "xdecor_worktable_top.png",
|
||||
"xdecor_worktable_sides.png", "xdecor_worktable_sides.png",
|
||||
"xdecor_worktable_front.png", "xdecor_worktable_front.png"
|
||||
},
|
||||
on_rotate = screwdriver.rotate_simple,
|
||||
can_dig = worktable.dig,
|
||||
on_construct = worktable.construct,
|
||||
on_receive_fields = worktable.fields,
|
||||
|
@ -200,105 +423,125 @@ xdecor.register("worktable", {
|
|||
allow_metadata_inventory_move = worktable.move
|
||||
})
|
||||
|
||||
local function description(node, shape)
|
||||
local desc = node:gsub("%w+:", " "):gsub("_", " "):gsub(" %l", string.upper):sub(2)..
|
||||
" "..shape:gsub("^%l", string.upper)
|
||||
return desc
|
||||
end
|
||||
for _, d in pairs(worktable.defs) do
|
||||
for mod, n in pairs(worktable.nodes) do
|
||||
for name in n:gmatch("[%w_]+") do
|
||||
local ndef = minetest.registered_nodes[mod..":"..name]
|
||||
if ndef and d[3] then
|
||||
local groups, tiles, light = {}, {}, 0
|
||||
groups.not_in_creative_inventory = 1
|
||||
|
||||
local function groups(node)
|
||||
if node:find("tree") or node:find("wood") or node:find("cactus") then
|
||||
return {choppy=3, not_in_creative_inventory=1}
|
||||
end
|
||||
return {cracky=3, not_in_creative_inventory=1}
|
||||
end
|
||||
for k, v in pairs(ndef.groups) do
|
||||
if k ~= "wood" and k ~= "stone" and k ~= "level" then
|
||||
groups[k] = v
|
||||
end
|
||||
end
|
||||
|
||||
local function shady(shape)
|
||||
if shape == "stair" or shape == "slab" or shape == "innerstair" or
|
||||
shape == "outerstair" then return false end
|
||||
return true
|
||||
end
|
||||
if #ndef.tiles > 1 and not ndef.drawtype:find("glass") then
|
||||
tiles = ndef.tiles
|
||||
else
|
||||
tiles = {ndef.tiles[1]}
|
||||
end
|
||||
|
||||
local function tiles(node, ndef)
|
||||
if node:find("glass") then return {node:gsub(":", "_")..".png"} end
|
||||
return ndef.tiles
|
||||
end
|
||||
stairs.register_stair_and_slab(name, mod..":"..name, groups, tiles,
|
||||
ndef.description.." Stair", ndef.description.." Slab", ndef.sounds)
|
||||
|
||||
for _, d in pairs(def) do
|
||||
for _, n in pairs(nodes) do
|
||||
local ndef = minetest.registered_nodes[n]
|
||||
if ndef then
|
||||
minetest.register_node(":"..n.."_"..d[1], {
|
||||
description = description(n, d[1]),
|
||||
if ndef.light_source > 3 then
|
||||
light = ndef.light_source - 1
|
||||
minetest.override_item("stairs:slab_"..name, {light_source=light})
|
||||
minetest.override_item("stairs:stair_"..name, {light_source=light})
|
||||
end
|
||||
|
||||
minetest.register_node(":"..mod..":"..name.."_"..d[1], {
|
||||
description = ndef.description.." "..d[1]:gsub("^%l", string.upper),
|
||||
paramtype = "light",
|
||||
paramtype2 = "facedir",
|
||||
drawtype = "nodebox",
|
||||
light_source = ndef.light_source,
|
||||
light_source = light,
|
||||
sounds = ndef.sounds,
|
||||
tiles = tiles(n, ndef),
|
||||
groups = groups(n),
|
||||
node_box = {type = "fixed", fixed = d[3]},
|
||||
sunlight_propagates = shady(d[1]),
|
||||
on_place = minetest.rotate_node
|
||||
tiles = tiles,
|
||||
groups = groups,
|
||||
node_box = xdecor.pixelnodebox(16, {d[3], d[4], d[5]}),
|
||||
sunlight_propagates = true,
|
||||
on_place = minetest.rotate_node,
|
||||
on_rightclick = function(pos, node, clicker, itemstack, pointed_thing)
|
||||
local pointed_nodebox = minetest.get_node(pos).name:match("(%w+)$")
|
||||
local wield_item = clicker:get_wielded_item():get_name()
|
||||
local player_name = clicker:get_player_name()
|
||||
local newnode = ""
|
||||
|
||||
if minetest.is_protected(pos, player_name) then
|
||||
minetest.record_protection_violation(pos, player_name)
|
||||
return
|
||||
end
|
||||
|
||||
local T = {
|
||||
{"nanoslab", nil, 2},
|
||||
{"micropanel", nil, 3},
|
||||
{"cube", nil, 6},
|
||||
{"cube", "panel", 9},
|
||||
{"cube", "outerstair", 11},
|
||||
{"cube", "halfstair", 7},
|
||||
{"cube", "innerstair", nil},
|
||||
{"panel", nil, 7},
|
||||
{"panel", "outerstair", 12},
|
||||
{"halfstair", nil, 11},
|
||||
{"halfstair", "outerstair", nil}
|
||||
}
|
||||
|
||||
for _, x in pairs(T) do
|
||||
if wield_item == mod..":"..name.."_"..x[1] then
|
||||
if not x[2] then x[2] = x[1] end
|
||||
if x[2] == pointed_nodebox then
|
||||
if not x[3] then
|
||||
newnode = mod..":"..name
|
||||
else
|
||||
newnode = mod..":"..name.."_"..worktable.defs[x[3]][1]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if clicker:get_player_control().sneak then
|
||||
if not minetest.registered_nodes[newnode] then return end
|
||||
minetest.set_node(pos, {name=newnode, param2=node.param2})
|
||||
else
|
||||
minetest.item_place_node(ItemStack(wield_item), clicker, pointed_thing)
|
||||
end
|
||||
|
||||
if not minetest.setting_getbool("creative_mode") then
|
||||
itemstack:take_item()
|
||||
end
|
||||
|
||||
return itemstack
|
||||
end
|
||||
})
|
||||
end
|
||||
if n:match("default:") then
|
||||
minetest.register_alias("xdecor:"..d[1].."_"..n:match(":(.+)"), n.."_"..d[1])
|
||||
--print("xdecor:"..d[1].."_"..n:match(":(.+)"), n.."_"..d[1])
|
||||
else
|
||||
minetest.register_alias("xdecor:"..d[1].."_"..n:gsub(":", "_", 1), n.."_"..d[1])
|
||||
--print("xdecor:"..d[1].."_"..n:gsub(":", "_", 1), n.."_"..d[1])
|
||||
if not d[3] then
|
||||
minetest.register_alias(mod..":"..name.."_"..d[1], "stairs:"..d[1].."_"..name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Register craft recipes and aliases for stairs and slabs.
|
||||
for _, n in pairs(nodes) do
|
||||
local bolly = string.gsub(n, "(default_)", "")
|
||||
minetest.register_alias("stairs:stair_"..bolly, "xdecor:stair_"..bolly)
|
||||
minetest.register_craft({
|
||||
output = bolly.."_stair 6",
|
||||
recipe = {
|
||||
{n, "", ""},
|
||||
{n, n, ""},
|
||||
{n, n, n}
|
||||
}
|
||||
})
|
||||
minetest.register_craft({
|
||||
output = bolly.."_stair 6",
|
||||
recipe = {
|
||||
{"", "", n},
|
||||
{"", n, n},
|
||||
{n, n, n}
|
||||
}
|
||||
})
|
||||
minetest.register_alias("stairs:slab_"..bolly, "xdecor:slab_"..bolly)
|
||||
minetest.register_craft({
|
||||
output = bolly.."_slab 3",
|
||||
recipe = {
|
||||
{"", "", ""},
|
||||
{"", "", ""},
|
||||
{n, n, n}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"xdecor:worktable"},
|
||||
interval = 3, chance = 1,
|
||||
action = function(pos, _, _, _)
|
||||
action = function(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
local tool = inv:get_stack("tool", 1)
|
||||
local hammer = inv:get_stack("hammer", 1)
|
||||
local wear = tool:get_wear()
|
||||
|
||||
if tool:is_empty() or hammer:is_empty() or wear == 0 then return end
|
||||
if tool:is_empty() or hammer:is_empty() or tool:get_wear() == 0 then
|
||||
return
|
||||
end
|
||||
|
||||
-- Wear : 0-65535 | 0 = new condition.
|
||||
tool:add_wear(-500)
|
||||
hammer:add_wear(300)
|
||||
hammer:add_wear(700)
|
||||
|
||||
inv:set_stack("tool", 1, tool)
|
||||
inv:set_stack("hammer", 1, hammer)
|
||||
end
|
||||
})
|
||||
|
||||
|
|