diff --git a/functions.php b/functions.php
index ee0f9d51..0f502790 100755
--- a/functions.php
+++ b/functions.php
@@ -155,6 +155,7 @@ function settingsForm() {
Server Desc:
Server Website:
Server MOTD:
+ Default Privs:
Spawnpoint (Format example: '32, 20, -140')
Player Transfer Distance (Value in chunks, 0 = unlimited)
diff --git a/index.php b/index.php
index 75a269ec..9bb49735 100755
--- a/index.php
+++ b/index.php
@@ -304,6 +304,11 @@ else if(isset($_GET['do']))
file_put_contents("mtm_db/worlds/$userid/minetest.conf", "player_transfer_distance = " . $_POST['playertransferdistance'] . "\n", FILE_APPEND);
}
+ // default_privs
+ if(isset($_POST['defaultprivs']) && $_POST['defaultprivs']!="") {
+ file_put_contents("mtm_db/worlds/$userid/minetest.conf", "default_privs = " . $_POST['defaultprivs'] . "\n", FILE_APPEND);
+ }
+
header("Location: index.php?do=manage");
} else {
echo "Shut down server before configuration...";
diff --git a/mods/areas/README.md b/mods/areas/README.md
new file mode 100644
index 00000000..2a69ac93
--- /dev/null
+++ b/mods/areas/README.md
@@ -0,0 +1,63 @@
+simple_protection
+=================
+
+A Minetest area protection mod, based on a fixed claim grid,
+like seen in [landrush](https://github.com/Bremaweb/landrush).
+
+You can claim areas by punching those with a claim stick.
+
+![Screenshot](https://raw.githubusercontent.com/SmallJoker/simple_protection/master/screenshot.png)
+
+
+License: CC0
+
+**Dependencies**
+- Minetest 5.0.0+
+- default: Crafting recipes
+
+**Optional dependencies**
+- [areas](https://github.com/ShadowNinja/areas): HUD compatibility
+
+
+Features
+--------
+
+- Easy, single-click protection
+- Fixed claim grid: 16x80x16 by default
+ - To configure: see `default_settings.lua` header text
+- Minimap-like radar to see areas nearby
+- Visual area border feedback, as seen in the [protector](https://github.com/tenplus1/protector) mod
+- List of claimed areas
+- Shared Chest for exchanging items
+- Translation support
+- Optional setting to require an area before digging
+
+
+Chat command(s)
+--------------
+
+```
+/area [ ...]
+ show -> Provides information about the current area
+ radar -> Displays a minimap-like area overview
+ share -> Shares the current area with
+ unshare -> Unshares the current area with
+ shareall -> Shares all your areas with
+ unshareall -> Unshares all your areas with
+ list [] -> Lists all areas (optional )
+ unclaim -> Unclaims the current area
+ delete -> Removes all areas of (requires "server" privilege)
+```
+
+
+About "/area show"
+------------------
+
+Area status: Not claimable
+- Shown when the area can not be claimed
+- Happens (by default) in the underground
+
+Players with access: foo, bar*, leprechaun, *all
+- foo, leprechaun: Regular single area share
+- bar*: Has access to all areas with the same owner
+- *all: Everybody can build and dig in the area
diff --git a/mods/areas/chest.lua b/mods/areas/chest.lua
new file mode 100644
index 00000000..d295b853
--- /dev/null
+++ b/mods/areas/chest.lua
@@ -0,0 +1,80 @@
+local S = s_protect.translator
+
+-- A shared chest for simple_protection but works with other protection mods too
+
+local function get_item_count(pos, player, count)
+ local name = player and player:get_player_name()
+ if not name or minetest.is_protected(pos, name) then
+ return 0
+ end
+ return count
+end
+
+local tex_mod = "^[colorize:#FF2:50"
+minetest.register_node("simple_protection:chest", {
+ description = S("Shared Chest") .. " " .. S("(by protection)"),
+ tiles = {
+ "default_chest_top.png" .. tex_mod,
+ "default_chest_top.png" .. tex_mod,
+ "default_chest_side.png" .. tex_mod,
+ "default_chest_side.png" .. tex_mod,
+ "default_chest_side.png" .. tex_mod,
+ "default_chest_lock.png" .. tex_mod
+ },
+ paramtype2 = "facedir",
+ sounds = default.node_sound_wood_defaults(),
+ groups = {choppy = 2, oddly_breakable_by_hand = 2},
+
+ after_place_node = function(pos, placer)
+ local meta = minetest.get_meta(pos)
+ meta:set_string("infotext", S("Shared Chest"))
+ meta:set_string("formspec",
+ "size[8,9]" ..
+ default.gui_bg ..
+ default.gui_bg_img ..
+ "list[context;main;0,0.3;8,4;]" ..
+ "list[current_player;main;0,5;8,4;]" ..
+ "listring[context;main]" ..
+ "listring[current_player;main]"
+ )
+ local inv = meta:get_inventory()
+ inv:set_size("main", 8*4)
+ end,
+ can_dig = function(pos, player)
+ return minetest.get_meta(pos):get_inventory():is_empty("main")
+ end,
+ on_blast = function() end,
+
+ allow_metadata_inventory_put = function(pos, fl, fi, stack, player)
+ return get_item_count(pos, player, stack:get_count())
+ end,
+ allow_metadata_inventory_take = function(pos, fl, fi, stack, player)
+ return get_item_count(pos, player, stack:get_count())
+ end,
+ allow_metadata_inventory_move = function(pos, fl, fi, tl, ti, count, player)
+ return get_item_count(pos, player, count)
+ end,
+ on_metadata_inventory_put = function(pos, fl, fi, stack, player)
+ minetest.log("action", player:get_player_name()
+ .. " moves " .. stack:get_name() .. " to shared chest at "
+ .. minetest.pos_to_string(pos))
+ end,
+ on_metadata_inventory_take = function(pos, fl, fi, stack, player)
+ minetest.log("action", player:get_player_name()
+ .. " takes " .. stack:get_name() .. " from shared chest at "
+ .. minetest.pos_to_string(pos))
+ end,
+ -- on_metadata_inventory_move logging is redundant: Same chest contents
+})
+
+minetest.register_craft({
+ type = "shapeless",
+ output = "simple_protection:shared_chest",
+ recipe = { "simple_protection:claim", "default:chest_locked" }
+})
+
+minetest.register_craft({
+ type = "shapeless",
+ output = "simple_protection:shared_chest",
+ recipe = { "simple_protection:claim", "default:chest" }
+})
\ No newline at end of file
diff --git a/mods/areas/command_mgr.lua b/mods/areas/command_mgr.lua
new file mode 100644
index 00000000..7df9a70c
--- /dev/null
+++ b/mods/areas/command_mgr.lua
@@ -0,0 +1,293 @@
+local S = s_protect.translator
+
+local commands = {}
+
+function s_protect.register_subcommand(name, func)
+ if commands[name] then
+ minetest.log("info", "[simple_protection] Overwriting chat command " .. name)
+ end
+
+ assert(#name:split(" ") == 1, "Invalid name")
+ assert(type(func) == "function")
+
+ commands[name] = func
+end
+
+minetest.register_chatcommand("area", {
+ description = S("Manages all of your areas."),
+ privs = {interact = true},
+ func = function(name, param)
+ if param == "" or param == "help" then
+ local function chat_send(desc, cmd)
+ minetest.chat_send_player(name, S(desc) .. ": "
+ .. minetest.colorize("#0FF", cmd))
+ end
+ local privs = minetest.get_player_privs(name)
+ minetest.chat_send_player(name, minetest.colorize("#0F0",
+ "=> " .. S("Available area commands")))
+
+ chat_send("Information about this area", "/area show")
+ chat_send("View of surrounding areas", "/area radar")
+ chat_send("(Un)share one area", "/area (un)share ")
+ chat_send("(Un)share all areas", "/area (un)shareall ")
+ if s_protect.area_list or privs.simple_protection then
+ chat_send("List claimed areas", "/area list []")
+ end
+ chat_send("Unclaim this area", "/area unclaim")
+ if privs.server then
+ chat_send("Delete all areas of a player", "/area delete ")
+ end
+ return
+ end
+
+ local args = param:split(" ", 2)
+ local func = commands[args[1]]
+ if not func then
+ return false, S("Unknown command parameter: @1. Check '/area' for correct usage.", args[1])
+ end
+
+ return func(name, args[2])
+ end,
+})
+
+s_protect.register_subcommand("show", function(name, param)
+ local player = minetest.get_player_by_name(name)
+ local player_pos = player:get_pos()
+ local data = s_protect.get_claim(player_pos)
+
+ minetest.add_entity(s_protect.get_center(player_pos), "simple_protection:marker")
+ local minp, maxp = s_protect.get_area_bounds(player_pos)
+ minetest.chat_send_player(name, S("Vertical from Y @1 to @2",
+ tostring(minp.y), tostring(maxp.y)))
+
+ if not data then
+ if s_protect.underground_limit and minp.y < s_protect.underground_limit then
+ return true, S("Area status: @1", S("Not claimable"))
+ end
+ return true, S("Area status: @1", S("Unowned (!)"))
+ end
+
+ minetest.chat_send_player(name, S("Area status: @1", S("Owned by @1", data.owner)))
+ local text = ""
+ for i, player in ipairs(data.shared) do
+ text = text..player..", "
+ end
+ local shared = s_protect.share[data.owner]
+ if shared then
+ for i, player in ipairs(shared) do
+ text = text..player.."*, "
+ end
+ end
+
+ if text ~= "" then
+ return true, S("Players with access: @1", text)
+ end
+end)
+
+local function check_ownership(name)
+ local player = minetest.get_player_by_name(name)
+ local data, index = s_protect.get_claim(player:get_pos())
+ if not data then
+ return false, S("This area is not claimed yet.")
+ end
+ local priv = minetest.check_player_privs(name, {simple_protection=true})
+ if name ~= data.owner and not priv then
+ return false, S("You do not own this area.")
+ end
+ return true, data, index
+end
+
+local function table_erase(t, e)
+ if not t or not e then
+ return false
+ end
+ local removed = false
+ for i, v in ipairs(t) do
+ if v == e then
+ table.remove(t, i)
+ removed = true
+ end
+ end
+ return removed
+end
+
+s_protect.register_subcommand("share", function(name, param)
+ if not param or name == param then
+ return false, S("No player name given.")
+ end
+ if not minetest.builtin_auth_handler.get_auth(param) and param ~= "*all" then
+ return false, S("Unknown player.")
+ end
+ local success, data, index = check_ownership(name)
+ if not success then
+ return success, data
+ end
+
+ if s_protect.is_shared(name, param) then
+ return true, S("@1 already has access to all your areas.", param)
+ end
+
+ if s_protect.is_shared(data, param) then
+ return true, S("@1 already has access to this area.", param)
+ end
+ table.insert(data.shared, param)
+ s_protect.set_claim(data, index)
+
+ if minetest.get_player_by_name(param) then
+ minetest.chat_send_player(param, S("@1 shared an area with you.", name))
+ end
+ return true, S("@1 has now access to this area.", param)
+end)
+
+s_protect.register_subcommand("unshare", function(name, param)
+ if not param or name == param or param == "" then
+ return false, S("No player name given.")
+ end
+ local success, data, index = check_ownership(name)
+ if not success then
+ return success, data
+ end
+ if not s_protect.is_shared(data, param) then
+ return true, S("That player has no access to this area.")
+ end
+ table_erase(data.shared, param)
+ s_protect.set_claim(data, index)
+
+ if minetest.get_player_by_name(param) then
+ minetest.chat_send_player(param, S("@1 unshared an area with you.", name))
+ end
+ return true, S("@1 has no longer access to this area.", param)
+end)
+
+s_protect.register_subcommand("shareall", function(name, param)
+ if not param or name == param or param == "" then
+ return false, S("No player name given.")
+ end
+ if not minetest.builtin_auth_handler.get_auth(param) then
+ if param == "*all" then
+ return false, S("You can not share all your areas with everybody.")
+ end
+ return false, S("Unknown player.")
+ end
+
+ if s_protect.is_shared(name, param) then
+ return true, S("@1 already has now access to all your areas.", param)
+ end
+ if not shared then
+ s_protect.share[name] = {}
+ end
+ table.insert(s_protect.share[name], param)
+ s_protect.save_share_db()
+
+ if minetest.get_player_by_name(param) then
+ minetest.chat_send_player(param, S("@1 shared all areas with you.", name))
+ end
+ return true, S("@1 has now access to all your areas.", param)
+end)
+
+s_protect.register_subcommand("unshareall", function(name, param)
+ if not param or name == param or param == "" then
+ return false, S("No player name given.")
+ end
+ local removed = false
+ local shared = s_protect.share[name]
+ if table_erase(shared, param) then
+ removed = true
+ s_protect.save_share_db()
+ end
+
+ -- Unshare each single claim
+ local claims = s_protect.get_player_claims(name)
+ for index, data in pairs(claims) do
+ if table_erase(data.shared, param) then
+ removed = true
+ end
+ end
+ if not removed then
+ return false, S("@1 does not have access to any of your areas.", param)
+ end
+ s_protect.update_claims(claims)
+ if minetest.get_player_by_name(param) then
+ minetest.chat_send_player(param, S("@1 unshared all areas with you.", name))
+ end
+ return true, S("@1 has no longer access to your areas.", param)
+end)
+
+s_protect.register_subcommand("unclaim", function(name)
+ local success, data, index = check_ownership(name)
+ if not success then
+ return success, data
+ end
+ if s_protect.claim_return and name == data.owner then
+ local player = minetest.get_player_by_name(name)
+ local inv = player:get_inventory()
+ if inv:room_for_item("main", "simple_protection:claim") then
+ inv:add_item("main", "simple_protection:claim")
+ end
+ end
+ s_protect.set_claim(nil, index)
+ return true, S("This area is unowned now.")
+end)
+
+s_protect.register_subcommand("delete", function(name, param)
+ if not param or name == param or param == "" then
+ return false, S("No player name given.")
+ end
+ if not minetest.check_player_privs(name, {server=true}) then
+ return false, S("Missing privilege: @1", "server")
+ end
+
+ local removed = {}
+ if s_protect.share[param] then
+ s_protect.share[param] = nil
+ table.insert(removed, S("Globally shared areas"))
+ s_protect.save_share_db()
+ end
+
+ -- Delete all claims
+ local claims, count = s_protect.get_player_claims(param)
+ for index in pairs(claims) do
+ claims[index] = false
+ end
+ s_protect.update_claims(claims)
+
+ if count > 0 then
+ table.insert(removed, S("@1 claimed area(s)", tostring(count)))
+ end
+
+ if #removed == 0 then
+ return false, S("@1 does not own any claimed areas.", param)
+ end
+ return true, S("Removed")..": "..table.concat(removed, ", ")
+end)
+
+s_protect.register_subcommand("list", function(name, param)
+ local has_sp_priv = minetest.check_player_privs(name, {simple_protection=true})
+ if not s_protect.area_list and not has_sp_priv then
+ return false, S("This command is not available.")
+ end
+ if not param or param == "" then
+ param = name
+ end
+ if not has_sp_priv and param ~= name then
+ return false, S("Missing privilege: @1", "simple_protection")
+ end
+
+ local list = {}
+ local width = s_protect.claim_size
+ local height = s_protect.claim_height
+
+ local claims = s_protect.get_player_claims(param)
+ for index in pairs(claims) do
+ -- TODO: Add database-specific function to convert the index to a position
+ local abs_pos = minetest.string_to_pos(index)
+ table.insert(list, string.format("%5i,%5i,%5i",
+ abs_pos.x * width + (width / 2),
+ abs_pos.y * height - s_protect.start_underground + (height / 2),
+ abs_pos.z * width + (width / 2)
+ ))
+ end
+
+ local text = S("Listing all areas of @1. Amount: @2", param, tostring(#list))
+ return true, text.."\n"..table.concat(list, "\n")
+end)
diff --git a/mods/areas/database_lsqlite.lua b/mods/areas/database_lsqlite.lua
new file mode 100644
index 00000000..a7382bb0
--- /dev/null
+++ b/mods/areas/database_lsqlite.lua
@@ -0,0 +1,130 @@
+--[[
+File: database_raw.lua
+
+lSQLite database functions:
+ load_db()
+ save_share_db()
+ get_claim(, direct access)
+ set_claim(data, index)
+ get_player_claims(player name)
+ update_claims(claims table)
+]]
+
+local worldpath = minetest.get_worldpath()
+
+local ie = minetest.request_insecure_environment()
+if not ie then
+ error("Cannot access insecure environment!")
+end
+
+local sql = ie.require("lsqlite3")
+-- Remove public table
+if sqlite3 then
+ sqlite3 = nil
+end
+
+local db = sql.open(worldpath .. "/s_protect.sqlite3")
+
+local function sql_exec(q)
+ if db:exec(q) ~= sql.OK then
+ minetest.log("info", "[simple_protection] lSQLite: " .. db:errmsg())
+ end
+end
+
+local function sql_row(q)
+ q = q .. " LIMIT 1;"
+ for row in db:nrows(q) do
+ return row
+ end
+end
+
+sql_exec([[
+CREATE TABLE IF NOT EXISTS claims (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ x INTEGER,
+ y INTEGER,
+ z INTEGER,
+ owner TEXT,
+ shared TEXT,
+ data TEXT
+);
+CREATE TABLE IF NOT EXISTS shares (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ owner TEXT,
+ shared TEXT,
+ data TEXT
+);
+]])
+
+function s_protect.load_db() end
+function s_protect.load_shareall() end
+function s_protect.save_share_db() end
+
+function s_protect.set_claim(cpos, claim)
+ local id, row = s_protect.get_claim(cpos)
+
+ if not claim then
+ if not id then
+ -- Claim never existed
+ return
+ end
+
+ -- Remove claim
+ sql_exec(
+ ("DELETE FROM claims WHERE id = %i LIMIT 1;"):format(id)
+ )
+ end
+
+ if id then
+ local vals = {}
+ for k, v in pairs(claim) do
+ if row[k] ~= v and type(v) == "string" then
+ vals[#vals + 1] = ("%s = `%s`"):format(k, v)
+ end
+ end
+ if #vals == 0 then
+ return
+ end
+ sql_exec(
+ ("UPDATE claims SET %s WHERE id = %i LIMIT 1;")
+ :format(table.concat(vals, ","), id)
+ )
+ else
+ sql_exec(
+ ("INSERT INTO claims VALUES (%i, %i, %i, %s, %s, %s);")
+ :format(pos.x, pos.y, pos.z, claim.owner,
+ claim.shared or "", claim.data or "")
+ )
+ end
+end
+
+function s_protect.get_claim(cpos)
+ local q
+ if type(pos) == "number" then
+ -- Direct index
+ q = "id = " .. cpos
+ else
+ q = ("x = %i AND y = %i AND z = %z"):format(cpos.x, cpos.y, cpos.z)
+ end
+ local row = sql_row("SELECT id, owner, shared, data FROM claims WHERE " .. q)
+ if not row then
+ return
+ end
+
+ local id = row.id
+ row.id = nil
+ return id, row
+end
+
+function s_protect.get_player_claims(owner)
+ local q = ("SELECT * FROM claims WHERE owner = %s;"):format(owner)
+ local vals = {}
+ for row in db:nrows(q) do
+ vals[#vals + 1] = row
+ end
+ return vals
+end
+
+function s_protect.update_claims(updated)
+ error("Inefficient. To be removed.")
+end
diff --git a/mods/areas/database_raw.lua b/mods/areas/database_raw.lua
new file mode 100644
index 00000000..26308168
--- /dev/null
+++ b/mods/areas/database_raw.lua
@@ -0,0 +1,167 @@
+--[[
+File: database_raw.lua
+
+Raw text format database functions:
+ load_db()
+ save_share_db()
+ get_claim(, direct access)
+ set_claim(data, index)
+ get_player_claims(player name)
+ update_claims(claims table)
+]]
+
+local claim_data = {}
+local claim_db = { time = os.time(), dirty = false }
+local share_db = { time = os.time(), dirty = false }
+
+function s_protect.load_db()
+ -- Don't forget the "parties"
+ s_protect.load_shareall()
+
+ local file = io.open(s_protect.file, "r")
+ if not file then
+ return
+ end
+ for line in file:lines() do
+ local data = line:split(" ")
+ if #data >= 2 then
+ -- Line format: pos, owner, shared_player, shared_player2, ..
+ local _shared = {}
+ for index = 3, #data do
+ if data[index] ~= "" then
+ table.insert(_shared, data[index])
+ end
+ end
+ claim_data[data[1]] = {owner=data[2], shared=_shared}
+ end
+ end
+ io.close(file)
+ minetest.log("action", "[simple_protection] Loaded claim data")
+end
+
+function s_protect.load_shareall()
+ local file = io.open(s_protect.sharefile, "r")
+ if not file then
+ return
+ end
+ for line in file:lines() do
+ if line ~= "" then
+ local data = line:split(" ")
+ -- Line format: owner, shared_player, shared_player2, ..
+ local _shared = {}
+ if #data > 1 then
+ for index = 2, #data do
+ if data[index] ~= "" then
+ table.insert(_shared, data[index])
+ end
+ end
+ s_protect.share[data[1]] = _shared
+ end
+ end
+ end
+ io.close(file)
+ minetest.log("action", "[simple_protection] Loaded shared claims")
+end
+
+local function delay(db_info, func)
+ local dtime = os.time() - db_info.time
+ if dtime < 6 then
+ -- Excessive save requests. Delay them.
+ if not db_info.dirty then
+ minetest.after(6 - dtime, func)
+ end
+ db_info.dirty = true
+ return true
+ end
+ db_info.time = os.time()
+ db_info.dirty = false
+end
+
+local function save_claims()
+ if delay(claim_db, save_claims) then
+ return
+ end
+
+ local contents = {}
+ for pos, data in pairs(claim_data) do
+ if data.owner and data.owner ~= "" then
+ contents[#contents + 1] =
+ pos .. " ".. data.owner .. " " ..
+ table.concat(data.shared, " ")
+ end
+ end
+ minetest.safe_file_write(s_protect.file, table.concat(contents, "\n"))
+end
+
+function s_protect.save_share_db()
+ if delay(share_db, s_protect.save_share_db) then
+ return
+ end
+
+ -- Save globally shared areas
+ local contents = {}
+ for name, players in pairs(s_protect.share) do
+ if #players > 0 then
+ contents[#contents + 1] = name .. " " ..
+ table.concat(players, " ")
+ end
+ end
+ minetest.safe_file_write(s_protect.sharefile, table.concat(contents, "\n"))
+end
+
+-- Speed up the function access
+local get_location = s_protect.get_location
+function s_protect.get_claim(pos, direct_access)
+ if direct_access then
+ return claim_data[pos], pos
+ end
+ local pos = get_location(pos)
+ local index = pos.x..","..pos.y..","..pos.z
+ return claim_data[index], index
+end
+
+function s_protect.set_claim(data, index)
+ claim_data[index] = data
+ save_claims()
+end
+
+function s_protect.get_player_claims(owner)
+ local count = 0
+ local claims = {}
+ for index, data in pairs(claim_data) do
+ if data.owner == owner then
+ claims[index] = data
+ count = count + 1
+ end
+ end
+ return claims, count
+end
+
+function s_protect.update_claims(updated)
+ for index, data in pairs(updated) do
+ if not data then
+ claim_data[index] = nil
+ else
+ claim_data[index] = data
+ end
+ end
+ save_claims()
+end
+
+local function table_contains(t, to_find)
+ for i, v in pairs(t) do
+ if v == to_find then
+ return true
+ end
+ end
+ return false
+end
+function s_protect.is_shared(id, player_name)
+ if type(id) == "table" and id.shared then
+ -- by area
+ return table_contains(id.shared, player_name)
+ end
+ assert(type(id) == "string", "is_shared(): Either ClaimData or string expected")
+ -- by owner
+ return table_contains(s_protect.share[id] or {}, player_name)
+end
\ No newline at end of file
diff --git a/mods/areas/default_settings.lua b/mods/areas/default_settings.lua
new file mode 100644
index 00000000..b950c93b
--- /dev/null
+++ b/mods/areas/default_settings.lua
@@ -0,0 +1,53 @@
+--[[
+ SETTINGS FILE
+
+simple_protection/default_settings.lua
+ Contains the default settings for freshly created worlds.
+ Please do not modify in order to not get any configuration-related errors
+ after updating all other files but this
+
+[world_path]/s_protect.conf
+ Contains the per-world specific settings.
+ You may modify this one but pay attention, as some settings may cause
+ unwanted side effects when claims were made.
+]]
+
+
+-- Width and length of claims in nodes
+-- !! Distorts the claim locations along the X and Z axis !!
+-- Type: Integer, positive, even number
+s_protect.claim_size = 16
+
+-- Height of claims in nodes
+-- !! Distorts the claim locations along the Y axis !!
+-- Type: Integer, positive
+s_protect.claim_height = 150
+
+-- Defines the Y offset where the 0th claim should start in the underground
+-- Example of claim (0,0,0): Ymin = -(50) = -50, Ymax = 150 - (50) - 1 = 99
+-- Type: Integer
+s_protect.start_underground = 50
+
+-- Only allows claiming above this Y value
+-- To disable this limit, set the value to 'nil'
+-- Type: Integer or nil
+s_protect.underground_limit = -300
+
+-- Returns the claim stick when unclaiming the area
+-- Type: Boolean
+s_protect.claim_return = true
+
+-- Players will need to claim the area (or have access to it) to dig
+-- Digging will be still allowed in the underground,
+-- as defined by the setting 's_protect.underground_limit'
+-- Type: Boolean
+s_protect.claim_to_dig = false
+
+-- Allows players to list their areas using '/area list'
+-- Type: Boolean
+s_protect.area_list = true
+
+-- Limits the amount of claims per player
+-- Doubled limit for players with the 'simple_protection' privilege
+-- Type: Integer
+s_protect.max_claims = 200
diff --git a/mods/areas/doc/mod_api.txt b/mods/areas/doc/mod_api.txt
new file mode 100644
index 00000000..78cd25a3
--- /dev/null
+++ b/mods/areas/doc/mod_api.txt
@@ -0,0 +1,108 @@
+# simple_protection API
+
+This file provides information about the API of simple_protection for the use in
+custom mods. The API might change slightly when the development goes on, so avoid
+using internal tables or functions which are not documented here.
+
+### Table of contents
+
+* Types: Custom tables or values
+* Helper functions: General purpose functions
+* Protection management: Claim-specific API functions
+* Callbacks: Registrable callbacks
+
+### Notation
+
+* `function_name(arg1, arg2, ...)` -> `returned data type`
+ * Function description
+
+
+## Types
+
+* `ClaimData` -> `table`
+```
+ {
+ owner = "foobar", -- string, owner of the area
+ shared = { "covfefe", "leprechaun" },
+ -- ^ table, list of players who explicitly have access to this area
+ }
+```
+* `ClaimIndex` -> `?`
+ * This might be any value, depending on the database backend.
+ * Can be obtained with `get_claim` and is used for `set_claim`
+
+
+## Helper functions
+
+These functions are grouped so that they make sense and then sorted alphabetically.
+
+* `s_protect.can_access(pos, player_name)` -> `boolean`
+ * Returns whether the player may modify the given position
+ * `pos`: Position as a `vector`
+ * `player_name`: Is a `string` for the player to check
+ * `nil`: Always returns `false`
+ * `""`: Returns `true`. Warning: `get_player_name()` returns this value on
+ objects which are not players, such as Lua entities.
+* `s_protect.get_area_bounds(pos)` -> `vector, vector`
+ * Returns the minimal node position (1st value) and maximal node position
+ (2nd value) of the current claim. The coordinate values are inside the claim.
+ * `pos`: Position as a `vector`
+* `s_protect.get_center(pos)` -> `vector`
+ * Returns the center node position of the current claim near to `pos.y`.
+ * `pos`: Position as a `vector`
+* `s_protect.get_location(pos)` -> `vector`
+ * Returns the location of the given claim (whole numbers) in claim-coordinates
+ * `pos`: Position as a `vector`
+ * This function is only helpful to iterate through multiple claims.
+* `s_protect.load_config()`
+ * Causes simple_protection to (initialize and) reload the configuration.
+
+
+## Protection management
+
+* `s_protect.get_claim(pos)` -> `ClaimData, ClaimIndex`
+ * Returns the area information or `nil` when no area was found at the given
+ position.
+ * `pos`: Position as a `vector`
+* `s_protect.get_player_claims(owner)` -> `table, count`
+ * Returns a table of claims which the player owns, whereas `ClaimIndex` is the
+ key and `ClaimData` the value of the resulting table. The second argument
+ describes how many entries the table has (i.e. how many claims).
+ * `owner`: Player name as `string`
+* `s_protect.set_claim(data, index)`
+ * Updates the area data for the given index. It will be saved automatically.
+ * `data`: Area information as `ClaimData`
+ * `index`: `ClaimIndex` provided by `get_claim`
+* `s_protect.update_claims(update_table)`
+ * Updates multiple areas according to the supplied table
+ * `update_table`: `table` of areas to update, whereas the key is `ClaimIndex`
+ and the value `ClaimData`. Set the value to `nil` to remove the claim.
+* `s_protect.is_shared(id, player_name)` -> `boolean`
+ * Returns whether the owner shared an area (or multiple) with `player_name`
+ * `id`: Can be either `ClaimData` or the owner name as `string`:
+ * Type `ClaimData`: Checks whether `player_name` is contained in
+ the given area's share list.
+ * Type `string`: Checks whether all areas of the given owner are
+ shared with `player_name`
+ * `player_name`: `string`, the player to check
+
+
+## Callbacks
+
+* `s_protect.registered_on_access(func)`
+ * Override or extend access to a certain claim. Depending on the returned
+ value of the registered function.
+ * `func`: `function(pos, player_name, owner_name)` called in `s_protect.can_access`.
+ * Is only called on protected areas. Use settings to control unclaimed areas.
+ * Must return a boolean or `nil`
+ * `pos`: `vector` position of the interaction
+ * `player_name`: `string` name of the interacting player
+ * `owner_name`: `string` name of the claim owner (player)
+ * If `func() -> false`: Access is denied instantly regardless of shared areas.
+ * If `func() -> true`: Access is granted if no other callback returns `false`.
+ * If `func() -> nil`: Normal protection handling
+* `s_protect.register_subcommand(name, func)`
+ * Registers a new subcommand for `/area`; throws errors on failure
+ * `name`: `string` name of the new command
+ * `func`: `function` to call when the command is executed
+ * See "Chat command definition" in the Minetest core Lua API.
diff --git a/mods/areas/hud.lua b/mods/areas/hud.lua
new file mode 100644
index 00000000..7a478672
--- /dev/null
+++ b/mods/areas/hud.lua
@@ -0,0 +1,91 @@
+--[[
+File: hud.lua
+
+areas HUD overlap compatibility
+HUD display and refreshing
+]]
+
+
+local S = s_protect.translator
+
+s_protect.player_huds = {}
+
+local hud_time = 0
+local prefix = ""
+local align_x = 1
+local pos_x = 0.02
+
+-- If areas is installed: Move the HUD to th opposite side
+if minetest.get_modpath("areas") then
+ prefix = "Simple Protection:\n"
+ align_x = -1
+ pos_x = 0.95
+end
+
+local function generate_hud(player, current_owner, has_access)
+ -- green if access
+ local color = 0xFFFFFF
+ if has_access then
+ color = 0x00CC00
+ end
+ s_protect.player_huds[player:get_player_name()] = {
+ hud_id = player:hud_add({
+ hud_elem_type = "text",
+ name = "area_hud",
+ number = color,
+ position = {x=pos_x, y=0.98},
+ text = prefix
+ .. S("Area owner: @1", current_owner),
+ scale = {x=100, y=25},
+ alignment = {x=align_x, y=-1},
+ }),
+ owner = current_owner,
+ had_access = has_access
+ }
+end
+
+minetest.register_globalstep(function(dtime)
+ hud_time = hud_time + dtime
+ if hud_time < 2.9 then
+ return
+ end
+ hud_time = 0
+
+ local is_shared = s_protect.is_shared
+ for _, player in ipairs(minetest.get_connected_players()) do
+ local player_name = player:get_player_name()
+
+ local current_owner = ""
+ local data = s_protect.get_claim(player:get_pos())
+ if data then
+ current_owner = data.owner
+ end
+
+ local has_access = (current_owner == player_name)
+ if not has_access and data then
+ -- Check if this area is shared with this player
+ has_access = is_shared(data, player_name)
+ end
+ if not has_access then
+ -- Check if all areas are shared with this player
+ has_access = is_shared(current_owner, player_name)
+ end
+ local changed = true
+
+ local hud_table = s_protect.player_huds[player_name]
+ if hud_table and hud_table.owner == current_owner
+ and hud_table.had_access == has_access then
+ -- still the same hud
+ changed = false
+ end
+
+ if changed and hud_table then
+ player:hud_remove(hud_table.hud_id)
+ s_protect.player_huds[player_name] = nil
+ end
+
+ if changed and current_owner ~= "" then
+ generate_hud(player, current_owner, has_access)
+ end
+ end
+end)
diff --git a/mods/areas/init.lua b/mods/areas/init.lua
new file mode 100644
index 00000000..8632958c
--- /dev/null
+++ b/mods/areas/init.lua
@@ -0,0 +1,35 @@
+-- simple_protection initialization
+
+if not minetest.get_translator then
+ error("[simple_protection] Your Minetest version is no longer supported."
+ .. " (version < 5.0.0)")
+end
+
+
+local world_path = minetest.get_worldpath()
+s_protect = {}
+s_protect.translator = minetest.get_translator("simple_protection")
+s_protect.share = {}
+s_protect.mod_path = minetest.get_modpath("simple_protection")
+s_protect.conf = world_path.."/s_protect.conf"
+s_protect.file = world_path.."/s_protect.data"
+s_protect.sharefile = world_path.."/s_protect_share.data"
+
+minetest.register_privilege("simple_protection",
+ s_protect.translator("Allows to modify and delete protected areas"))
+
+-- Load helper functions and configuration
+dofile(s_protect.mod_path.."/misc_functions.lua")
+s_protect.load_config()
+
+-- Load database functions
+dofile(s_protect.mod_path.."/command_mgr.lua")
+dofile(s_protect.mod_path.."/database_raw.lua")
+-- Spread the load a bit
+minetest.after(0.5, s_protect.load_db)
+
+-- General things to make this mod friendlier
+dofile(s_protect.mod_path.."/protection.lua")
+dofile(s_protect.mod_path.."/hud.lua")
+dofile(s_protect.mod_path.."/radar.lua")
+dofile(s_protect.mod_path.."/chest.lua")
diff --git a/mods/areas/locale/simple_protection.de.tr b/mods/areas/locale/simple_protection.de.tr
new file mode 100644
index 00000000..07def45d
--- /dev/null
+++ b/mods/areas/locale/simple_protection.de.tr
@@ -0,0 +1,108 @@
+# textdomain: simple_protection
+# Translated by: Krock/SmallJoker
+
+
+# Privilege description
+Allows to modify and delete protected areas=Erlaubt das Abändern und Entfernen von geschützten Gebieten
+
+#### AREA COMMAND ####
+
+# Command description
+Manages all of your areas.=Verwaltet alle deine geschützten Gebiete
+
+# /area
+Available area commands=Verfügbare 'area' Befehle
+Information about this area=Informationen über dieses Gebiet
+View of surrounding areas=Übersicht von allen Gebieten
+(Un)share one area=Zugriffs-Berechtigung eines Gebietes hinzufügen/entfernen
+(Un)share all areas=Zugriffs-Berechtigung für alle Gebiete hinzufügen/entfernen
+List claimed areas=Geschützte Gebiete auflisten
+Delete all areas of a player=Alle Gebiete eines Spielers löschen
+Unclaim this area=Den Schutz für dieses Gebiet aufheben
+
+# Errors of /area
+Unknown command parameter: @1. Check '/area' for correct usage.=Unbekannter Befehls-Parameter: @1. Siehe '/area' für die korrekte Anwendung.
+No player name given.=Kein Spielernahme gegeben.
+Unknown player.=Unbekannter Spieler.
+This area is not claimed yet.=Dieses Gebiet ist noch nicht geschützt.
+You do not own this area.=Du besitzt dieses Gebiet nicht.
+
+# /area show
+Vertical from Y @1 to @2=Senkrecht von Y @1 bis @2
+Area status: @1=Gebiets-Status: @1
+Not claimable=Nicht schützbar
+Unowned (!)=Nicht geschützt (!)
+Owned by @1=Von @1 geschützt
+Players with access: @1=Spieler mit Zugriff: @1
+
+# /area radar
+North @1=Nord @1
+East @1=Ost @1
+South @1=Süd @1
+West @1=West @1
+Your position=Deine Position
+Your area=Dein Gebiet
+Area claimed\nNo access for you=Gebiet geschützt\nKeine Berechtigung
+Access for everybody=Zugriff für jeden
+One area unit (@1m) up/down\n-> no claims on this Y level=Ein Gebiet (@1m) ober-/unterhalb
+square \= 1 area \= @1x@2x@3 nodes (X,Y,Z)=1 Quadrat \= 1 Gebiet \= @1x@2x@3 Blöcke (X,Y,Z)
+
+# /area share
+@1 already has access to all your areas.=@1 hat bereits Zugriff auf alle deine Gebiete.
+@1 already has access to this area.=@1 hat bereits Zugriff auf dieses Gebiet.
+@1 shared an area with you.=Du hast jetzt Zugriff auf ein Gebiet von @1.
+@1 has now access to this area.=@1 kann jetzt dieses Gebiet abändern.
+
+# /area unshare
+That player has no access to this area.=Dieser Spieler hat keinen Zugriff auf dieses Gebiet.
+@1 unshared an area with you.=Du hast nun keinen Zugriff auf ein Gebiet von @1 mehr.
+@1 has no longer access to this area.=@1 kann jetzt dieses Gebiet nicht mehr abändern.
+
+# /area shareall
+You can not share all your areas with everybody.=Du kannst nicht alle Gebiete mit jedem teilen.
+@1 already has now access to all your areas.=@1 hat bereits Zugriff auf alle deine Gebiete
+@1 shared all areas with you.=@1 hat dir Zufriff auf alle die Gebiete erlaubt.
+@1 has now access to all your areas.=@1 kann jetzt alle deine Gebiete abändern.
+
+# /area unshareall
+@1 does not have access to any of your areas.=@1 hat keinen Zugriff auf deine Gebiete.
+@1 unshared all areas with you.=@1 hat dir den Zugriff auf die Gebiete verwehrt.
+@1 has no longer access to your areas.=@1 kann keines deiner Gebiete mehr abändern.
+
+# /area unclaim
+This area is unowned now.=Dieses Gebiet ist nun nicht mehr geschützt.
+
+# /area delete
+Missing privilege: @1=Fehlendes Privileg: @1
+Removed=Entfernt
+Globally shared areas=Globale Gebietszugiffe
+@1 claimed area(s)=@1 geschützte(s) Gebiet(e)
+@1 does not own any claimed areas.=@1 besitzt keine geschützten Gebiete.
+
+# /area list [name]
+This command is not available.=Dieser Befehl ist nicht verfügbar.
+Listing all areas of @1. Amount: @2=Auslistung der Gebiete von @1. Anzahl: @2
+
+
+#### Protection ####
+
+# Item place information
+Area owned by: @1=Gebiet geschützt durch: @1
+# Hud text
+Area owner: @1=Gebiets-Besitzer: @1
+
+# Item: Claim stick
+Claim Stick=Gebietsschutz
+#(click to protect)=(kicken zum schützen)
+This area is already protected by an other protection mod.=Dieses Gebiet ist bereits durch eine andere Mod geschützt.
+You can not claim areas below @1.=Du kannst keine Gebiete unterhalb von @1 schützen.
+This area is already owned by: @1=@1 besitzt dieses Gebiet bereits.
+Congratulations! You now own this area.=Glückwunsch! Du besitzt dieses Gebiet jetzt.
+You can not claim any further areas: Limit (@1) reached.=Du kannst keine weiteren Gebiete schützen lassen: Limit (@1) erreicht.
+Please claim this area to modify it.=Bitte schütze das Gebiet um dieses abändern zu können.
+
+
+#### Shared Chest ####
+
+Shared Chest=Geteilte Truhe
+(by protection)=(durch Gebietsschutz)
diff --git a/mods/areas/locale/simple_protection.fr.tr b/mods/areas/locale/simple_protection.fr.tr
new file mode 100644
index 00000000..e2164096
--- /dev/null
+++ b/mods/areas/locale/simple_protection.fr.tr
@@ -0,0 +1,107 @@
+# textdomain: simple_protection
+# Translated by: d-stephane
+
+
+# Privilege description
+Allows to modify and delete protected areas=Permet de modifier et supprimer des zones protégées
+
+#### AREA COMMAND ####
+
+# Command description
+Manages all of your areas.=Gestion de toutes vos zones.
+
+# /area
+Available area commands=Commandes de zone disponibles
+Information about this area=Informations sur cette zone
+(Un)share one area=Partager (ou pas) une zone
+(Un)share all areas=Partager (ou pas) toutes les zones
+List claimed areas=Liste des zones revendiquées
+Delete all areas of a player=Supprimer toutes les zones d'un joueur
+Unclaim this area=Libérer cette zone
+
+# Errors of /area
+Unknown command parameter: @1. Check '/area' for correct usage.=Paramètre de commande inconnu : @1. Tappez '/area' pour voir les paramètres possible.
+No player name given.=Aucun nom de joueur donné.
+Unknown player.=Joueur inconnu.
+This area is not claimed yet.=Cette zone n'est pas encore revendiquée.
+You do not own this area.=Vous ne possédez pas cette zone.
+
+# /area show
+Vertical from Y @1 to @2=Verticale de Y @1 à @2
+Area status: @1=Statut de la zone : @1
+Not claimable=Non revendiquable
+Unowned (!)=Pas de propriétaire (!)
+Owned by @1=Possédée par @1
+Players with access: @1=Joueurs avec accès : @1
+
+# /area radar
+North @1=Nord @1
+East @1=Est @1
+South @1=Sud @1
+West @1=Ouest @1
+Your position=Votre position
+Your area=Votre zone
+Area claimed\nNo access for you=Zone revendiquée\nNon accessible pour vous
+Access for everybody=Accès pour tout le monde
+One area unit (@1m) up/down\n-> no claims on this Y level=Une unité de zone (@1m) haut/bas\n-> Rien de revendiqué à ce niveau Y
+square \= 1 area \= @1x@2x@3 nodes (X,Y,Z)=carré \= 1 zone \= @1x@2x@3 blocs (X,Y,Z)
+
+# /area share
+@1 already has access to all your areas.=@1 a déjà accès à toutes vos zones.
+@1 already has access to this area.=@1 a déjà accès à cette zone.
+@1 shared an area with you.=@1 a partagé une zone avec vous.
+@1 has now access to this area.=@1 a maintenant accès à cette zone.
+
+# /area unshare
+That player has no access to this area.=Ce joueur n'a pas accès à cette zone.
+@1 unshared an area with you.=@1 a enlevé le partage de la zone avec vous.
+@1 has no longer access to this area.=@1 n'a plus accès à cette zone.
+
+# /area shareall
+You can not share all your areas with everybody.=Vous ne pouvez pas partager toutes vos zones avec tout le monde.
+@1 already has now access to all your areas.=@1 a déjà accès à toutes vos zones.
+@1 shared all areas with you.=@1 a partagé toutes les zones avec vous.
+@1 has now access to all your areas.=@1 a maintenant accès à toutes vos zones.
+
+# /area unshareall
+@1 does not have access to any of your areas.=@1 n'a accès à aucune de vos zones.
+@1 unshared all areas with you.=@1 a enlevé le partage de toutes les zones avec vous.
+@1 has no longer access to your areas.=@1 n'a plus accès à vos zones.
+
+# /area unclaim
+This area is unowned now.=Cette zone n'est plus revendiquée.
+
+# /area delete
+Missing privilege: @1=Privilège manquant : @1
+Removed=Supprimé
+Globally shared areas=Zones partagées globalement
+@1 claimed area(s)=@1 a revendiqué des zones
+@1 does not own any claimed areas.=@1 ne possède aucune zone revendiquée.
+
+# /area list [name]
+This command is not available.=Cette commande n'est pas disponible.
+Listing all areas of @1. Amount: @2=Liste de tous les zones de @1. Total : @2
+
+
+#### Protection ####
+
+# Item place information
+Area owned by: @1=Zone appartenant à : @1
+# Hud text
+Area owner: @1=Propriétaire de la zone : @1
+
+# Item: Claim stick
+Claim Stick=Bâton de protection
+(click to protect)=(cliquez pour protéger)
+This area is already protected by an other protection mod.=Cette zone est déjà protégée par un autre mod de protection.
+You can not claim areas below @1.=Vous ne pouvez pas revendiquer les zones ci-dessous @1.
+This area is already owned by: @1=Cette zone est déjà détenue par : @1.
+Congratulations! You now own this area.=Félicitations ! Vous possédez maintenant cette zone.
+You can not claim any further areas: Limit (@1) reached.=Vous ne pouvez pas revendiquer d'autres zones : La limite de @1 a été atteinte.
+Please claim this area to modify it.=Veuillez revendiquer cette zone pour la modifier.
+
+
+#### Shared Chest ####
+
+Shared Chest=Coffre partagé
+(by protection)=(par protection)
diff --git a/mods/areas/locale/simple_protection.pt.tr b/mods/areas/locale/simple_protection.pt.tr
new file mode 100644
index 00000000..a6e0c3c8
--- /dev/null
+++ b/mods/areas/locale/simple_protection.pt.tr
@@ -0,0 +1,105 @@
+# textdomain: simple_protection
+# Translated by: BrunoMine
+
+
+# Privilege description
+Allows to modify and delete protected areas=Permitir modificar e deletar areas protegidas
+
+#### AREA COMMAND ####
+
+# Command description
+Manages all of your areas.=Gerencia todas as suas areas.
+
+# /area
+Available area commands=Comandos disponiveis
+Information about this area=Dados sobre essa area
+(Un)share one area=(Des)compartilha uma area
+(Un)share all areas=(Des)compartilha todas areas
+#List claimed areas =
+#Delete all areas of a player =
+Unclaim this area=Desfazer-se dessa area
+
+# Errors of /area
+Unknown command parameter: @1. Check '/area' for correct usage.=Parametro de comando desconhecido: @1. Use '/area' para o uso correto.
+No player name given.=Nenhum nome de jogador dado.
+Unknown player.=Jogador desconhecido.
+This area is not claimed yet.=Essa area nao foi reivindicada ainda.
+You do not own this area.=Voce nao pode possuir essa area.
+
+# /area show
+Vertical from Y @1 to @2=Vertical em Y @1 a @2
+Area status: @1=Status da area: @1
+Not claimable=Nao reivindicada
+Unowned (!)=Sem dono (!)
+Owned by @1=Reivindicada por @1
+Players with access: @1=Jogadores com acesso: @1
+
+# /area radar
+#North @1 =
+#East @1 =
+#South @1 =
+#West @1 =
+#Your position =
+#Your area =
+#Area claimed\nNo access for you =
+#Access for everybody =
+
+# /area share
+@1 already has access to all your areas.=@1 ja possui acesso a todas suas areas.
+@1 already has access to this area.=@1 ja possui acesso a essa area.
+@1 shared an area with you.=@1 compartilhou uma area contigo.
+@1 has now access to this area.=@1 agora tem acesso nessa area.
+
+# /area unshare
+That player has no access to this area.=Esse jogador nao tem acesso a essa area.
+@1 unshared an area with you.=@1 descompartilhou uma area contigo.
+@1 has no longer access to this area.=@1 deixou de ter acesso a essa area.
+
+# /area shareall
+You can not share all your areas with everybody.=Nao podes compartilhar todas as suas areas com todos.
+@1 already has now access to all your areas.=@1 ja possui agora acesso a todas suas areas.
+@1 shared all areas with you.=@1 compartilhou todas as areas contigo.
+@1 has now access to all your areas.=@1 agora tem acesso a todas suas areas.
+
+# /area unshareall
+@1 does not have access to any of your areas.=@1 nao tem acesso a nenhuma de suas areas.
+@1 unshared all areas with you.=@1 descompartilhou todas areas contigo.
+@1 has no longer access to your areas.=@1 deixou de ter acesso a todas suas areas.
+
+# /area unclaim
+This area is unowned now.=Essa area deixou de ser reivindicada agora.
+
+# /area delete
+Missing privilege: @1=Perderam o privilegio: @1
+Removed=Removido
+Globally shared areas=Areas compartilhadas globalmente
+@1 claimed area(s)=@1 area(s) reivindicada(s)
+@1 does not own any claimed areas.=@1 nao possui nenhuma area reivindicada.
+
+# /area list [name]
+#This command is not available. =
+#Listing all areas of @1. Amount: @2 =
+
+
+#### Protection ####
+
+# Item place information
+Area owned by: @1=Area possuida por: @1
+# Hud text
+Area owner: @1=Area de: @1
+
+# Item: Claim stick
+Claim Stick=Graveto Reivindicador
+#(click to protect) =
+This area is already protected by an other protection mod.=Essa area ja foi protegida por um outro mod protetor.
+You can not claim areas below @1.=Nao podes reivindicar areas abaixo @1.
+This area is already owned by: @1=Essa area ja foi reivindicada por: @1.
+Congratulations! You now own this area.=Parabens! Agora reivindicaste essa area.
+#You can not claim any further areas: Limit (@1) reached. =
+#Please claim this area to modify it. =
+
+
+#### Shared Chest ####
+
+#Shared Chest =
+#(by protection) =
diff --git a/mods/areas/locale/template.txt b/mods/areas/locale/template.txt
new file mode 100644
index 00000000..bf589766
--- /dev/null
+++ b/mods/areas/locale/template.txt
@@ -0,0 +1,108 @@
+# textdomain: simple_protection
+# Translated by: YOURNAME
+
+
+# Privilege description
+Allows to modify and delete protected areas=
+
+#### AREA COMMAND ####
+
+# Command description
+Manages all of your areas.=
+
+# /area
+Available area commands=
+Information about this area=
+View of surrounding areas=
+(Un)share one area=
+(Un)share all areas=
+List claimed areas=
+Delete all areas of a player=
+Unclaim this area=
+
+# Errors of /area
+Unknown command parameter: @1. Check '/area' for correct usage.=
+No player name given.=
+Unknown player.=
+This area is not claimed yet.=
+You do not own this area.=
+
+# /area show
+Vertical from Y @1 to @2=
+Area status: @1=
+Not claimable=
+Unowned (!)=
+Owned by @1=
+Players with access: @1=
+
+# /area radar
+North @1=
+East @1=
+South @1=
+West @1=
+Your position=
+Your area=
+Area claimed\nNo access for you=
+Access for everybody=
+One area unit (@1m) up/down\n-> no claims on this Y level=
+square \= 1 area \= @1x@2x@3 nodes (X,Y,Z)=
+
+# /area share
+@1 already has access to all your areas.=
+@1 already has access to this area.=
+@1 shared an area with you.=
+@1 has now access to this area.=
+
+# /area unshare
+That player has no access to this area.=
+@1 unshared an area with you.=
+@1 has no longer access to this area.=
+
+# /area shareall
+You can not share all your areas with everybody.=
+@1 already has now access to all your areas.=
+@1 shared all areas with you.=
+@1 has now access to all your areas.=
+
+# /area unshareall
+@1 does not have access to any of your areas.=
+@1 unshared all areas with you.=
+@1 has no longer access to your areas.=
+
+# /area unclaim
+This area is unowned now.=
+
+# /area delete
+Missing privilege: @1=
+Removed=
+Globally shared areas=
+@1 claimed area(s)=
+@1 does not own any claimed areas.=
+
+# /area list [name]
+This command is not available.=
+Listing all areas of @1. Amount: @2=
+
+
+#### Protection ####
+
+# Item place information
+Area owned by: @1=
+# Hud text
+Area owner: @1=
+
+# Item: Claim stick
+Claim Stick=
+(click to protect)=
+This area is already protected by an other protection mod.=
+You can not claim areas below @1.=
+This area is already owned by: @1=
+Congratulations! You now own this area.=
+You can not claim any further areas: Limit (@1) reached.=
+Please claim this area to modify it.=
+
+
+#### Shared Chest ####
+
+Shared Chest=
+(by protection)=
diff --git a/mods/areas/misc_functions.lua b/mods/areas/misc_functions.lua
new file mode 100644
index 00000000..4dcdca70
--- /dev/null
+++ b/mods/areas/misc_functions.lua
@@ -0,0 +1,184 @@
+--[[
+File: functions.lua
+
+Protection helper functions
+Configuration loading
+]]
+
+-- Helper functions
+
+-- Cache for performance
+local get_player_privs = minetest.get_player_privs
+local registered_on_access = {}
+
+s_protect.can_access = function(pos, player_name)
+ if not player_name then
+ return false
+ end
+ -- Allow access for pipeworks and unidentified mods
+ if player_name == ":pipeworks"
+ or player_name == "" then
+ return true
+ end
+
+ -- Admin power, handle privileges
+ local privs = get_player_privs(player_name)
+ if privs.simple_protection or privs.protection_bypass then
+ return true
+ end
+
+ -- Data of current area
+ local data = s_protect.get_claim(pos)
+
+ -- Area is not claimed
+ if not data then
+ -- Allow digging when claiming is not forced
+ if not s_protect.claim_to_dig then
+ return true
+ end
+
+ -- Must claim everywhere? Disallow everywhere.
+ if not s_protect.underground_limit then
+ return false
+ end
+ -- Is it in claimable area? Yes? Disallow.
+ if pos.y >= s_protect.underground_limit then
+ return false
+ end
+ return true
+ end
+ if player_name == data.owner then
+ return true
+ end
+
+ -- Complicated-looking return value handling:
+ -- false: Forbid access instantly
+ -- true: Access granted if none returns false
+ -- nil: Do nothing
+ local override_access = false
+ for i = 1, #registered_on_access do
+ local ret = registered_on_access[i](
+ vector.new(pos), player_name, data.owner)
+
+ if ret == false then
+ return false
+ end
+ if ret == true then
+ override_access = true
+ end
+ end
+ if override_access then
+ return true
+ end
+
+ -- Owner shared the area with the player
+ if s_protect.is_shared(data.owner, player_name) then
+ return true
+ end
+ -- Globally shared area
+ if s_protect.is_shared(data, player_name) then
+ return true
+ end
+ if s_protect.is_shared(data, "*all") then
+ return true
+ end
+ return false
+end
+
+s_protect.register_on_access = function(func)
+ registered_on_access[#registered_on_access + 1] = func
+end
+
+s_protect.get_location = function(pos_)
+ local pos = vector.round(pos_)
+ return vector.floor({
+ x = pos.x / s_protect.claim_size,
+ y = (pos.y + s_protect.start_underground) / s_protect.claim_height,
+ z = pos.z / s_protect.claim_size
+ })
+end
+
+local get_location = s_protect.get_location
+s_protect.get_area_bounds = function(pos_)
+ local cs = s_protect.claim_size
+ local cy = s_protect.claim_height
+
+ local p = get_location(pos_)
+
+ local minp = {
+ x = p.x * cs,
+ y = p.y * cy - s_protect.start_underground,
+ z = p.z * cs
+ }
+ local maxp = {
+ x = minp.x + cs - 1,
+ y = minp.y + cy - 1,
+ z = minp.z + cs - 1
+ }
+
+ return minp, maxp
+end
+
+s_protect.get_center = function(pos1)
+ local size = s_protect.claim_size
+ local pos = {
+ x = pos1.x / size,
+ y = pos1.y + 1.5,
+ z = pos1.z / size
+ }
+ pos = vector.floor(pos)
+ -- Get the middle of the area
+ pos.x = pos.x * size + (size / 2)
+ pos.z = pos.z * size + (size / 2)
+ return pos
+end
+
+s_protect.load_config = function()
+ -- Load defaults
+ dofile(s_protect.mod_path.."/default_settings.lua")
+ local file = io.open(s_protect.conf, "r")
+ if not file then
+ -- Duplicate configuration file on first time
+ local src = io.open(s_protect.mod_path.."/default_settings.lua", "r")
+ file = io.open(s_protect.conf, "w")
+
+ while true do
+ local block = src:read(128) -- 128B at once
+ if not block then
+ io.close(src)
+ io.close(file)
+ break
+ end
+ file:write(block)
+ end
+ return
+ end
+
+ io.close(file)
+
+ -- Load existing config
+ simple_protection = {}
+ dofile(s_protect.conf)
+
+ -- Backwards compatibility
+ for k, v in pairs(simple_protection) do
+ s_protect[k] = v
+ end
+ simple_protection = nil
+
+ -- Sanity check individual settings
+ assert((s_protect.claim_size % 2) == 0 and s_protect.claim_size >= 4,
+ "claim_size must be even and >= 4")
+ assert(s_protect.claim_height >= 4, "claim_height must be >= 4")
+
+ if s_protect.claim_heigh then
+ minetest.log("warning", "[simple_protection] "
+ .. "Deprecated setting: claim_heigh")
+ s_protect.claim_height = s_protect.claim_heigh
+ end
+ if s_protect.underground_claim then
+ minetest.log("warning", "[simple_protection] "
+ .. "Deprecated setting: underground_claim")
+ s_protect.underground_limit = nil
+ end
+end
diff --git a/mods/areas/mod.conf b/mods/areas/mod.conf
new file mode 100644
index 00000000..153a2a33
--- /dev/null
+++ b/mods/areas/mod.conf
@@ -0,0 +1,4 @@
+name = simple_protection
+description = Easy to use fixed-grid quadratic area protection mod with graphical area "minimap"
+depends = default
+optional_depends = areas
diff --git a/mods/areas/protection.lua b/mods/areas/protection.lua
new file mode 100644
index 00000000..fc57233e
--- /dev/null
+++ b/mods/areas/protection.lua
@@ -0,0 +1,148 @@
+--[[
+File: protection.lua
+
+Protection callback handler
+Node placement checks
+Claim Stick item definition
+]]
+
+local S = s_protect.translator
+
+local function notify_player(pos, player_name)
+ local data = s_protect.get_claim(pos)
+ if not data and s_protect.claim_to_dig then
+ minetest.chat_send_player(player_name, S("Please claim this area to modify it."))
+ elseif not data then
+ -- Access restricted by another protection mod. Not my job.
+ return
+ else
+ minetest.chat_send_player(player_name, S("Area owned by: @1", data.owner))
+ end
+end
+
+s_protect.old_is_protected = minetest.is_protected
+minetest.is_protected = function(pos, player_name)
+ if s_protect.can_access(pos, player_name) then
+ return s_protect.old_is_protected(pos, player_name)
+ end
+ return true
+end
+
+local old_item_place = minetest.item_place
+minetest.item_place = function(itemstack, placer, pointed_thing)
+ local player_name = placer and placer:get_player_name() or ""
+
+ if s_protect.can_access(pointed_thing.above, player_name)
+ or not minetest.registered_nodes[itemstack:get_name()] then
+ return old_item_place(itemstack, placer, pointed_thing)
+ end
+
+ notify_player(pointed_thing.above, player_name)
+ return itemstack
+end
+
+minetest.register_on_protection_violation(notify_player)
+
+
+minetest.register_craftitem("simple_protection:claim", {
+ description = S("Claim Stick") .. " " .. S("(click to protect)"),
+ inventory_image = "simple_protection_claim.png",
+ stack_max = 10,
+ on_use = function(itemstack, user, pointed_thing)
+ if pointed_thing.type ~= "node" then
+ return
+ end
+ local player_name = user:get_player_name()
+ local pos = pointed_thing.under
+ if s_protect.old_is_protected(pos, player_name) then
+ minetest.chat_send_player(player_name,
+ S("This area is already protected by an other protection mod."))
+ return
+ end
+ if s_protect.underground_limit then
+ local minp, maxp = s_protect.get_area_bounds(pos)
+ if minp.y < s_protect.underground_limit then
+ minetest.chat_send_player(player_name,
+ S("You can not claim areas below @1.",
+ s_protect.underground_limit .. "m"))
+ return
+ end
+ end
+ local data, index = s_protect.get_claim(pos)
+ if data then
+ minetest.chat_send_player(player_name,
+ S("This area is already owned by: @1", data.owner))
+ return
+ end
+ -- Count number of claims for this user
+ local claims_max = s_protect.max_claims
+
+ if minetest.check_player_privs(player_name, {simple_protection=true}) then
+ claims_max = claims_max * 2
+ end
+
+ local claims, count = s_protect.get_player_claims(player_name)
+ if count >= claims_max then
+ minetest.chat_send_player(player_name,
+ S("You can not claim any further areas: Limit (@1) reached.",
+ tostring(claims_max)))
+ return
+ end
+
+ itemstack:take_item(1)
+ s_protect.update_claims({
+ [index] = {owner=player_name, shared={}}
+ })
+
+ minetest.add_entity(s_protect.get_center(pos), "simple_protection:marker")
+ minetest.chat_send_player(player_name, S("Congratulations! You now own this area."))
+ return itemstack
+ end,
+})
+minetest.register_alias("simple_protection:claim_stick", "simple_protection:claim")
+minetest.register_alias("claim_stick", "simple_protection:claim")
+
+minetest.register_craft({
+ output = "simple_protection:claim",
+ recipe = {
+ {"default:copper_ingot", "default:steel_ingot", "default:copper_ingot"},
+ {"default:steel_ingot", "default:stonebrick", "default:steel_ingot"},
+ {"default:copper_ingot", "default:steel_ingot", "default:copper_ingot"},
+ }
+})
+
+minetest.register_entity("simple_protection:marker",{
+ initial_properties = {
+ hp_max = 1,
+ visual = "wielditem",
+ visual_size = {x=1.0/1.5,y=1.0/1.5},
+ physical = false,
+ textures = {"simple_protection:mark"},
+ },
+ on_activate = function(self, staticdata, dtime_s)
+ minetest.after(10, function()
+ self.object:remove()
+ end)
+ end,
+})
+
+-- hacky - I'm not a regular node!
+local size = s_protect.claim_size / 2
+minetest.register_node("simple_protection:mark", {
+ tiles = {"simple_protection_marker.png"},
+ groups = {dig_immediate=3, not_in_creative_inventory=1},
+ drop = "",
+ use_texture_alpha = true,
+ walkable = false,
+ drawtype = "nodebox",
+ node_box = {
+ type = "fixed",
+ fixed = {
+ -- sides
+ {-size-.5, -size-.5, -size-.5, -size-.5, size+.5, size-.5},
+ {-size-.5, -size-.5, size-.5, size-.5, size+.5, size-.5},
+ { size-.5, -size-.5, -size-.5, size-.5, size+.5, size-.5},
+ {-size-.5, -size-.5, -size-.5, size-.5, size+.5, -size-.5},
+ },
+ },
+})
diff --git a/mods/areas/radar.lua b/mods/areas/radar.lua
new file mode 100644
index 00000000..b916764e
--- /dev/null
+++ b/mods/areas/radar.lua
@@ -0,0 +1,128 @@
+-- /area radar
+local S = s_protect.translator
+local data_cache
+
+local function colorize_area(name, force)
+ if force == "unclaimed" or not force and
+ not data_cache then
+ -- Area not claimed
+ return "[colorize:#FFF:50"
+ end
+ if force == "owner" or not force and
+ data_cache.owner == name then
+ return "[colorize:#0F0:180"
+ end
+ local is_shared = s_protect.is_shared
+ if force == "shared" or not force and (
+ is_shared(data_cache, name)
+ or is_shared(data_cache.owner, name)) then
+ return "[colorize:#0F0:80"
+ end
+ if force == "*all" or not force and
+ is_shared(data_cache, "*all") then
+ return "[colorize:#00F:180"
+ end
+ -- Claimed but not shared
+ return "[colorize:#000:180"
+end
+
+local function combine_escape(str)
+ return str:gsub("%^%[", "\\%^\\%["):gsub(":", "\\:")
+end
+
+s_protect.register_subcommand("radar", function(name)
+ local player = minetest.get_player_by_name(name)
+ local player_pos = player:get_pos()
+ local pos = s_protect.get_location(player_pos)
+ local map_w = 15 - 1
+ local map_wh = map_w / 2
+ local img_w = 20
+
+ local get_single = s_protect.get_claim
+ local function getter(x, ymod, z)
+ data_cache = get_single(x .."," .. (pos.y + ymod) .. "," .. z, true)
+ return data_cache
+ end
+
+ local parts = ""
+ for z = 0, map_w do
+ for x = 0, map_w do
+ local ax = pos.x + x - map_wh
+ local az = pos.z + z - map_wh
+ local img = "simple_protection_radar.png"
+
+ if getter(ax, 0, az) then
+ -- Using default "img" value
+ elseif getter(ax, -1, az) then
+ -- Check for claim below first
+ img = "simple_protection_radar_down.png"
+ elseif getter(ax, 1, az) then
+ -- Last, check upper area
+ img = "simple_protection_radar_up.png"
+ end
+ parts = parts .. string.format(":%i,%i=%s",
+ x * img_w, (map_w - z) * img_w,
+ combine_escape(img .. "^" .. colorize_area(name)))
+ -- Somewhat dirty hack for [combine. Escape everything
+ -- to get the whole text passed into TextureSource::generateImage()
+ end
+ end
+
+ -- Player's position marker (8x8 px)
+ local pp_x = player_pos.x / s_protect.claim_size
+ local pp_z = player_pos.z / s_protect.claim_size
+ -- Get relative position to the map, add map center offset, center image
+ pp_x = math.floor((pp_x - pos.x + map_wh) * img_w + 0.5) - 4
+ pp_z = math.floor((pos.z - pp_z + map_wh + 1) * img_w + 0.5) - 4
+ local marker_str = string.format(":%i,%i=%s", pp_x, pp_z,
+ combine_escape("object_marker_red.png^[resize:8x8"))
+
+ -- Rotation calculation
+ local dir_label = S("North @1", "(Z+)")
+ local dir_mod = ""
+ local look_angle = player.get_look_horizontal and player:get_look_horizontal()
+ if not look_angle then
+ look_angle = player:get_look_yaw() - math.pi / 2
+ end
+ look_angle = look_angle * 180 / math.pi
+
+ if look_angle >= 45 and look_angle < 135 then
+ dir_label = S("West @1", "(X-)")
+ dir_mod = "^[transformR270"
+ elseif look_angle >= 135 and look_angle < 225 then
+ dir_label = S("South @1", "(Z-)")
+ dir_mod = "^[transformR180"
+ elseif look_angle >= 225 and look_angle < 315 then
+ dir_label = S("East @1", "(X+)")
+ dir_mod = "^[transformR90"
+ end
+
+ minetest.show_formspec(name, "covfefe",
+ "size[10.5,7]" ..
+ "button_exit[9.5,0;1,1;exit;X]" ..
+ "label[2,0;"..dir_label.."]" ..
+ "image[0,0.5;7,7;" ..
+ minetest.formspec_escape("[combine:300x300"
+ .. parts .. marker_str)
+ .. dir_mod .. "]" ..
+ "label[0,6.8;1 " .. S("square = 1 area = @1x@2x@3 nodes (X,Y,Z)",
+ s_protect.claim_size,
+ s_protect.claim_height,
+ s_protect.claim_size) .. "]" ..
+ "image[6.25,1.25;0.5,0.5;object_marker_red.png]" ..
+ "label[7,1.25;" .. S("Your position") .. "]" ..
+ "image[6,2;1,1;simple_protection_radar.png^"
+ .. colorize_area(nil, "owner") .. "]" ..
+ "label[7,2.25;" .. S("Your area") .. "]" ..
+ "image[6,3;1,1;simple_protection_radar.png^"
+ .. colorize_area(nil, "other") .. "]" ..
+ "label[7,3;" .. S("Area claimed\nNo access for you") .. "]" ..
+ "image[6,4;1,1;simple_protection_radar.png^"
+ .. colorize_area(nil, "*all") .. "]" ..
+ "label[7,4.25;" .. S("Access for everybody") .. "]" ..
+ "image[6,5;1,1;simple_protection_radar_down.png]" ..
+ "image[7,5;1,1;simple_protection_radar_up.png]" ..
+ "label[6,6;" .. S("One area unit (@1m) up/down\n-> no claims on this Y level",
+ s_protect.claim_height) .. "]"
+ )
+end)
diff --git a/mods/areas/screenshot.png b/mods/areas/screenshot.png
new file mode 100644
index 00000000..f2885ecc
Binary files /dev/null and b/mods/areas/screenshot.png differ
diff --git a/mods/areas/textures/simple_protection_claim.png b/mods/areas/textures/simple_protection_claim.png
new file mode 100644
index 00000000..41dbd8bf
Binary files /dev/null and b/mods/areas/textures/simple_protection_claim.png differ
diff --git a/mods/areas/textures/simple_protection_marker.png b/mods/areas/textures/simple_protection_marker.png
new file mode 100644
index 00000000..950609c8
Binary files /dev/null and b/mods/areas/textures/simple_protection_marker.png differ
diff --git a/mods/areas/textures/simple_protection_radar.png b/mods/areas/textures/simple_protection_radar.png
new file mode 100644
index 00000000..75bbbe1a
Binary files /dev/null and b/mods/areas/textures/simple_protection_radar.png differ
diff --git a/mods/areas/textures/simple_protection_radar_down.png b/mods/areas/textures/simple_protection_radar_down.png
new file mode 100644
index 00000000..aee9c442
Binary files /dev/null and b/mods/areas/textures/simple_protection_radar_down.png differ
diff --git a/mods/areas/textures/simple_protection_radar_up.png b/mods/areas/textures/simple_protection_radar_up.png
new file mode 100644
index 00000000..2d51762a
Binary files /dev/null and b/mods/areas/textures/simple_protection_radar_up.png differ
diff --git a/mods/basic_materials/.luacheckrc b/mods/basic_materials/.luacheckrc
new file mode 100644
index 00000000..55879b03
--- /dev/null
+++ b/mods/basic_materials/.luacheckrc
@@ -0,0 +1,30 @@
+std = "lua51+minetest"
+unused_args = false
+allow_defined_top = true
+max_line_length = 999
+
+stds.minetest = {
+ read_globals = {
+ "DIR_DELIM",
+ "minetest",
+ "core",
+ "dump",
+ "vector",
+ "nodeupdate",
+ "VoxelManip",
+ "VoxelArea",
+ "PseudoRandom",
+ "ItemStack",
+ "default",
+ table = {
+ fields = {
+ "copy",
+ },
+ },
+ }
+}
+
+read_globals = {
+ "default",
+ "moreores",
+}
diff --git a/mods/basic_materials/LICENSE b/mods/basic_materials/LICENSE
new file mode 100644
index 00000000..c5885ae9
--- /dev/null
+++ b/mods/basic_materials/LICENSE
@@ -0,0 +1,600 @@
+License for code: LGPL 3.0
+License for media and all other assets: CC-by-SA 4.0
+
+###############################################################################
+
+ GNU LESSER GENERAL PUBLIC LICENSE
+ Version 3, 29 June 2007
+
+ Copyright (C) 2007 Free Software Foundation, Inc.
+ Everyone is permitted to copy and distribute verbatim copies
+ of this license document, but changing it is not allowed.
+
+
+ This version of the GNU Lesser General Public License incorporates
+the terms and conditions of version 3 of the GNU General Public
+License, supplemented by the additional permissions listed below.
+
+ 0. Additional Definitions.
+
+ As used herein, "this License" refers to version 3 of the GNU Lesser
+General Public License, and the "GNU GPL" refers to version 3 of the GNU
+General Public License.
+
+ "The Library" refers to a covered work governed by this License,
+other than an Application or a Combined Work as defined below.
+
+ An "Application" is any work that makes use of an interface provided
+by the Library, but which is not otherwise based on the Library.
+Defining a subclass of a class defined by the Library is deemed a mode
+of using an interface provided by the Library.
+
+ A "Combined Work" is a work produced by combining or linking an
+Application with the Library. The particular version of the Library
+with which the Combined Work was made is also called the "Linked
+Version".
+
+ The "Minimal Corresponding Source" for a Combined Work means the
+Corresponding Source for the Combined Work, excluding any source code
+for portions of the Combined Work that, considered in isolation, are
+based on the Application, and not on the Linked Version.
+
+ The "Corresponding Application Code" for a Combined Work means the
+object code and/or source code for the Application, including any data
+and utility programs needed for reproducing the Combined Work from the
+Application, but excluding the System Libraries of the Combined Work.
+
+ 1. Exception to Section 3 of the GNU GPL.
+
+ You may convey a covered work under sections 3 and 4 of this License
+without being bound by section 3 of the GNU GPL.
+
+ 2. Conveying Modified Versions.
+
+ If you modify a copy of the Library, and, in your modifications, a
+facility refers to a function or data to be supplied by an Application
+that uses the facility (other than as an argument passed when the
+facility is invoked), then you may convey a copy of the modified
+version:
+
+ a) under this License, provided that you make a good faith effort to
+ ensure that, in the event an Application does not supply the
+ function or data, the facility still operates, and performs
+ whatever part of its purpose remains meaningful, or
+
+ b) under the GNU GPL, with none of the additional permissions of
+ this License applicable to that copy.
+
+ 3. Object Code Incorporating Material from Library Header Files.
+
+ The object code form of an Application may incorporate material from
+a header file that is part of the Library. You may convey such object
+code under terms of your choice, provided that, if the incorporated
+material is not limited to numerical parameters, data structure
+layouts and accessors, or small macros, inline functions and templates
+(ten or fewer lines in length), you do both of the following:
+
+ a) Give prominent notice with each copy of the object code that the
+ Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the object code with a copy of the GNU GPL and this license
+ document.
+
+ 4. Combined Works.
+
+ You may convey a Combined Work under terms of your choice that,
+taken together, effectively do not restrict modification of the
+portions of the Library contained in the Combined Work and reverse
+engineering for debugging such modifications, if you also do each of
+the following:
+
+ a) Give prominent notice with each copy of the Combined Work that
+ the Library is used in it and that the Library and its use are
+ covered by this License.
+
+ b) Accompany the Combined Work with a copy of the GNU GPL and this license
+ document.
+
+ c) For a Combined Work that displays copyright notices during
+ execution, include the copyright notice for the Library among
+ these notices, as well as a reference directing the user to the
+ copies of the GNU GPL and this license document.
+
+ d) Do one of the following:
+
+ 0) Convey the Minimal Corresponding Source under the terms of this
+ License, and the Corresponding Application Code in a form
+ suitable for, and under terms that permit, the user to
+ recombine or relink the Application with a modified version of
+ the Linked Version to produce a modified Combined Work, in the
+ manner specified by section 6 of the GNU GPL for conveying
+ Corresponding Source.
+
+ 1) Use a suitable shared library mechanism for linking with the
+ Library. A suitable mechanism is one that (a) uses at run time
+ a copy of the Library already present on the user's computer
+ system, and (b) will operate properly with a modified version
+ of the Library that is interface-compatible with the Linked
+ Version.
+
+ e) Provide Installation Information, but only if you would otherwise
+ be required to provide such information under section 6 of the
+ GNU GPL, and only to the extent that such information is
+ necessary to install and execute a modified version of the
+ Combined Work produced by recombining or relinking the
+ Application with a modified version of the Linked Version. (If
+ you use option 4d0, the Installation Information must accompany
+ the Minimal Corresponding Source and Corresponding Application
+ Code. If you use option 4d1, you must provide the Installation
+ Information in the manner specified by section 6 of the GNU GPL
+ for conveying Corresponding Source.)
+
+ 5. Combined Libraries.
+
+ You may place library facilities that are a work based on the
+Library side by side in a single library together with other library
+facilities that are not Applications and are not covered by this
+License, and convey such a combined library under terms of your
+choice, if you do both of the following:
+
+ a) Accompany the combined library with a copy of the same work based
+ on the Library, uncombined with any other library facilities,
+ conveyed under the terms of this License.
+
+ b) Give prominent notice with the combined library that part of it
+ is a work based on the Library, and explaining where to find the
+ accompanying uncombined form of the same work.
+
+ 6. Revised Versions of the GNU Lesser General Public License.
+
+ The Free Software Foundation may publish revised and/or new versions
+of the GNU Lesser General Public License from time to time. Such new
+versions will be similar in spirit to the present version, but may
+differ in detail to address new problems or concerns.
+
+ Each version is given a distinguishing version number. If the
+Library as you received it specifies that a certain numbered version
+of the GNU Lesser General Public License "or any later version"
+applies to it, you have the option of following the terms and
+conditions either of that published version or of any later version
+published by the Free Software Foundation. If the Library as you
+received it does not specify a version number of the GNU Lesser
+General Public License, you may choose any version of the GNU Lesser
+General Public License ever published by the Free Software Foundation.
+
+ If the Library as you received it specifies that a proxy can decide
+whether future versions of the GNU Lesser General Public License shall
+apply, that proxy's public statement of acceptance of any version is
+permanent authorization for you to choose that version for the
+Library.
+
+###############################################################################
+
+Attribution-ShareAlike 4.0 International
+
+=======================================================================
+
+Creative Commons Corporation ("Creative Commons") is not a law firm and
+does not provide legal services or legal advice. Distribution of
+Creative Commons public licenses does not create a lawyer-client or
+other relationship. Creative Commons makes its licenses and related
+information available on an "as-is" basis. Creative Commons gives no
+warranties regarding its licenses, any material licensed under their
+terms and conditions, or any related information. Creative Commons
+disclaims all liability for damages resulting from their use to the
+fullest extent possible.
+
+Using Creative Commons Public Licenses
+
+Creative Commons public licenses provide a standard set of terms and
+conditions that creators and other rights holders may use to share
+original works of authorship and other material subject to copyright
+and certain other rights specified in the public license below. The
+following considerations are for informational purposes only, are not
+exhaustive, and do not form part of our licenses.
+
+ Considerations for licensors: Our public licenses are
+ intended for use by those authorized to give the public
+ permission to use material in ways otherwise restricted by
+ copyright and certain other rights. Our licenses are
+ irrevocable. Licensors should read and understand the terms
+ and conditions of the license they choose before applying it.
+ Licensors should also secure all rights necessary before
+ applying our licenses so that the public can reuse the
+ material as expected. Licensors should clearly mark any
+ material not subject to the license. This includes other CC-
+ licensed material, or material used under an exception or
+ limitation to copyright. More considerations for licensors:
+ wiki.creativecommons.org/Considerations_for_licensors
+
+ Considerations for the public: By using one of our public
+ licenses, a licensor grants the public permission to use the
+ licensed material under specified terms and conditions. If
+ the licensor's permission is not necessary for any reason--for
+ example, because of any applicable exception or limitation to
+ copyright--then that use is not regulated by the license. Our
+ licenses grant only permissions under copyright and certain
+ other rights that a licensor has authority to grant. Use of
+ the licensed material may still be restricted for other
+ reasons, including because others have copyright or other
+ rights in the material. A licensor may make special requests,
+ such as asking that all changes be marked or described.
+ Although not required by our licenses, you are encouraged to
+ respect those requests where reasonable. More considerations
+ for the public:
+ wiki.creativecommons.org/Considerations_for_licensees
+
+=======================================================================
+
+Creative Commons Attribution-ShareAlike 4.0 International Public
+License
+
+By exercising the Licensed Rights (defined below), You accept and agree
+to be bound by the terms and conditions of this Creative Commons
+Attribution-ShareAlike 4.0 International Public License ("Public
+License"). To the extent this Public License may be interpreted as a
+contract, You are granted the Licensed Rights in consideration of Your
+acceptance of these terms and conditions, and the Licensor grants You
+such rights in consideration of benefits the Licensor receives from
+making the Licensed Material available under these terms and
+conditions.
+
+
+Section 1 -- Definitions.
+
+ a. Adapted Material means material subject to Copyright and Similar
+ Rights that is derived from or based upon the Licensed Material
+ and in which the Licensed Material is translated, altered,
+ arranged, transformed, or otherwise modified in a manner requiring
+ permission under the Copyright and Similar Rights held by the
+ Licensor. For purposes of this Public License, where the Licensed
+ Material is a musical work, performance, or sound recording,
+ Adapted Material is always produced where the Licensed Material is
+ synched in timed relation with a moving image.
+
+ b. Adapter's License means the license You apply to Your Copyright
+ and Similar Rights in Your contributions to Adapted Material in
+ accordance with the terms and conditions of this Public License.
+
+ c. BY-SA Compatible License means a license listed at
+ creativecommons.org/compatiblelicenses, approved by Creative
+ Commons as essentially the equivalent of this Public License.
+
+ d. Copyright and Similar Rights means copyright and/or similar rights
+ closely related to copyright including, without limitation,
+ performance, broadcast, sound recording, and Sui Generis Database
+ Rights, without regard to how the rights are labeled or
+ categorized. For purposes of this Public License, the rights
+ specified in Section 2(b)(1)-(2) are not Copyright and Similar
+ Rights.
+
+ e. Effective Technological Measures means those measures that, in the
+ absence of proper authority, may not be circumvented under laws
+ fulfilling obligations under Article 11 of the WIPO Copyright
+ Treaty adopted on December 20, 1996, and/or similar international
+ agreements.
+
+ f. Exceptions and Limitations means fair use, fair dealing, and/or
+ any other exception or limitation to Copyright and Similar Rights
+ that applies to Your use of the Licensed Material.
+
+ g. License Elements means the license attributes listed in the name
+ of a Creative Commons Public License. The License Elements of this
+ Public License are Attribution and ShareAlike.
+
+ h. Licensed Material means the artistic or literary work, database,
+ or other material to which the Licensor applied this Public
+ License.
+
+ i. Licensed Rights means the rights granted to You subject to the
+ terms and conditions of this Public License, which are limited to
+ all Copyright and Similar Rights that apply to Your use of the
+ Licensed Material and that the Licensor has authority to license.
+
+ j. Licensor means the individual(s) or entity(ies) granting rights
+ under this Public License.
+
+ k. Share means to provide material to the public by any means or
+ process that requires permission under the Licensed Rights, such
+ as reproduction, public display, public performance, distribution,
+ dissemination, communication, or importation, and to make material
+ available to the public including in ways that members of the
+ public may access the material from a place and at a time
+ individually chosen by them.
+
+ l. Sui Generis Database Rights means rights other than copyright
+ resulting from Directive 96/9/EC of the European Parliament and of
+ the Council of 11 March 1996 on the legal protection of databases,
+ as amended and/or succeeded, as well as other essentially
+ equivalent rights anywhere in the world.
+
+ m. You means the individual or entity exercising the Licensed Rights
+ under this Public License. Your has a corresponding meaning.
+
+
+Section 2 -- Scope.
+
+ a. License grant.
+
+ 1. Subject to the terms and conditions of this Public License,
+ the Licensor hereby grants You a worldwide, royalty-free,
+ non-sublicensable, non-exclusive, irrevocable license to
+ exercise the Licensed Rights in the Licensed Material to:
+
+ a. reproduce and Share the Licensed Material, in whole or
+ in part; and
+
+ b. produce, reproduce, and Share Adapted Material.
+
+ 2. Exceptions and Limitations. For the avoidance of doubt, where
+ Exceptions and Limitations apply to Your use, this Public
+ License does not apply, and You do not need to comply with
+ its terms and conditions.
+
+ 3. Term. The term of this Public License is specified in Section
+ 6(a).
+
+ 4. Media and formats; technical modifications allowed. The
+ Licensor authorizes You to exercise the Licensed Rights in
+ all media and formats whether now known or hereafter created,
+ and to make technical modifications necessary to do so. The
+ Licensor waives and/or agrees not to assert any right or
+ authority to forbid You from making technical modifications
+ necessary to exercise the Licensed Rights, including
+ technical modifications necessary to circumvent Effective
+ Technological Measures. For purposes of this Public License,
+ simply making modifications authorized by this Section 2(a)
+ (4) never produces Adapted Material.
+
+ 5. Downstream recipients.
+
+ a. Offer from the Licensor -- Licensed Material. Every
+ recipient of the Licensed Material automatically
+ receives an offer from the Licensor to exercise the
+ Licensed Rights under the terms and conditions of this
+ Public License.
+
+ b. Additional offer from the Licensor -- Adapted Material.
+ Every recipient of Adapted Material from You
+ automatically receives an offer from the Licensor to
+ exercise the Licensed Rights in the Adapted Material
+ under the conditions of the Adapter's License You apply.
+
+ c. No downstream restrictions. You may not offer or impose
+ any additional or different terms or conditions on, or
+ apply any Effective Technological Measures to, the
+ Licensed Material if doing so restricts exercise of the
+ Licensed Rights by any recipient of the Licensed
+ Material.
+
+ 6. No endorsement. Nothing in this Public License constitutes or
+ may be construed as permission to assert or imply that You
+ are, or that Your use of the Licensed Material is, connected
+ with, or sponsored, endorsed, or granted official status by,
+ the Licensor or others designated to receive attribution as
+ provided in Section 3(a)(1)(A)(i).
+
+ b. Other rights.
+
+ 1. Moral rights, such as the right of integrity, are not
+ licensed under this Public License, nor are publicity,
+ privacy, and/or other similar personality rights; however, to
+ the extent possible, the Licensor waives and/or agrees not to
+ assert any such rights held by the Licensor to the limited
+ extent necessary to allow You to exercise the Licensed
+ Rights, but not otherwise.
+
+ 2. Patent and trademark rights are not licensed under this
+ Public License.
+
+ 3. To the extent possible, the Licensor waives any right to
+ collect royalties from You for the exercise of the Licensed
+ Rights, whether directly or through a collecting society
+ under any voluntary or waivable statutory or compulsory
+ licensing scheme. In all other cases the Licensor expressly
+ reserves any right to collect such royalties.
+
+
+Section 3 -- License Conditions.
+
+Your exercise of the Licensed Rights is expressly made subject to the
+following conditions.
+
+ a. Attribution.
+
+ 1. If You Share the Licensed Material (including in modified
+ form), You must:
+
+ a. retain the following if it is supplied by the Licensor
+ with the Licensed Material:
+
+ i. identification of the creator(s) of the Licensed
+ Material and any others designated to receive
+ attribution, in any reasonable manner requested by
+ the Licensor (including by pseudonym if
+ designated);
+
+ ii. a copyright notice;
+
+ iii. a notice that refers to this Public License;
+
+ iv. a notice that refers to the disclaimer of
+ warranties;
+
+ v. a URI or hyperlink to the Licensed Material to the
+ extent reasonably practicable;
+
+ b. indicate if You modified the Licensed Material and
+ retain an indication of any previous modifications; and
+
+ c. indicate the Licensed Material is licensed under this
+ Public License, and include the text of, or the URI or
+ hyperlink to, this Public License.
+
+ 2. You may satisfy the conditions in Section 3(a)(1) in any
+ reasonable manner based on the medium, means, and context in
+ which You Share the Licensed Material. For example, it may be
+ reasonable to satisfy the conditions by providing a URI or
+ hyperlink to a resource that includes the required
+ information.
+
+ 3. If requested by the Licensor, You must remove any of the
+ information required by Section 3(a)(1)(A) to the extent
+ reasonably practicable.
+
+ b. ShareAlike.
+
+ In addition to the conditions in Section 3(a), if You Share
+ Adapted Material You produce, the following conditions also apply.
+
+ 1. The Adapter's License You apply must be a Creative Commons
+ license with the same License Elements, this version or
+ later, or a BY-SA Compatible License.
+
+ 2. You must include the text of, or the URI or hyperlink to, the
+ Adapter's License You apply. You may satisfy this condition
+ in any reasonable manner based on the medium, means, and
+ context in which You Share Adapted Material.
+
+ 3. You may not offer or impose any additional or different terms
+ or conditions on, or apply any Effective Technological
+ Measures to, Adapted Material that restrict exercise of the
+ rights granted under the Adapter's License You apply.
+
+
+Section 4 -- Sui Generis Database Rights.
+
+Where the Licensed Rights include Sui Generis Database Rights that
+apply to Your use of the Licensed Material:
+
+ a. for the avoidance of doubt, Section 2(a)(1) grants You the right
+ to extract, reuse, reproduce, and Share all or a substantial
+ portion of the contents of the database;
+
+ b. if You include all or a substantial portion of the database
+ contents in a database in which You have Sui Generis Database
+ Rights, then the database in which You have Sui Generis Database
+ Rights (but not its individual contents) is Adapted Material,
+
+ including for purposes of Section 3(b); and
+ c. You must comply with the conditions in Section 3(a) if You Share
+ all or a substantial portion of the contents of the database.
+
+For the avoidance of doubt, this Section 4 supplements and does not
+replace Your obligations under this Public License where the Licensed
+Rights include other Copyright and Similar Rights.
+
+
+Section 5 -- Disclaimer of Warranties and Limitation of Liability.
+
+ a. UNLESS OTHERWISE SEPARATELY UNDERTAKEN BY THE LICENSOR, TO THE
+ EXTENT POSSIBLE, THE LICENSOR OFFERS THE LICENSED MATERIAL AS-IS
+ AND AS-AVAILABLE, AND MAKES NO REPRESENTATIONS OR WARRANTIES OF
+ ANY KIND CONCERNING THE LICENSED MATERIAL, WHETHER EXPRESS,
+ IMPLIED, STATUTORY, OR OTHER. THIS INCLUDES, WITHOUT LIMITATION,
+ WARRANTIES OF TITLE, MERCHANTABILITY, FITNESS FOR A PARTICULAR
+ PURPOSE, NON-INFRINGEMENT, ABSENCE OF LATENT OR OTHER DEFECTS,
+ ACCURACY, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR NOT
+ KNOWN OR DISCOVERABLE. WHERE DISCLAIMERS OF WARRANTIES ARE NOT
+ ALLOWED IN FULL OR IN PART, THIS DISCLAIMER MAY NOT APPLY TO YOU.
+
+ b. TO THE EXTENT POSSIBLE, IN NO EVENT WILL THE LICENSOR BE LIABLE
+ TO YOU ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+ NEGLIGENCE) OR OTHERWISE FOR ANY DIRECT, SPECIAL, INDIRECT,
+ INCIDENTAL, CONSEQUENTIAL, PUNITIVE, EXEMPLARY, OR OTHER LOSSES,
+ COSTS, EXPENSES, OR DAMAGES ARISING OUT OF THIS PUBLIC LICENSE OR
+ USE OF THE LICENSED MATERIAL, EVEN IF THE LICENSOR HAS BEEN
+ ADVISED OF THE POSSIBILITY OF SUCH LOSSES, COSTS, EXPENSES, OR
+ DAMAGES. WHERE A LIMITATION OF LIABILITY IS NOT ALLOWED IN FULL OR
+ IN PART, THIS LIMITATION MAY NOT APPLY TO YOU.
+
+ c. The disclaimer of warranties and limitation of liability provided
+ above shall be interpreted in a manner that, to the extent
+ possible, most closely approximates an absolute disclaimer and
+ waiver of all liability.
+
+
+Section 6 -- Term and Termination.
+
+ a. This Public License applies for the term of the Copyright and
+ Similar Rights licensed here. However, if You fail to comply with
+ this Public License, then Your rights under this Public License
+ terminate automatically.
+
+ b. Where Your right to use the Licensed Material has terminated under
+ Section 6(a), it reinstates:
+
+ 1. automatically as of the date the violation is cured, provided
+ it is cured within 30 days of Your discovery of the
+ violation; or
+
+ 2. upon express reinstatement by the Licensor.
+
+ For the avoidance of doubt, this Section 6(b) does not affect any
+ right the Licensor may have to seek remedies for Your violations
+ of this Public License.
+
+ c. For the avoidance of doubt, the Licensor may also offer the
+ Licensed Material under separate terms or conditions or stop
+ distributing the Licensed Material at any time; however, doing so
+ will not terminate this Public License.
+
+ d. Sections 1, 5, 6, 7, and 8 survive termination of this Public
+ License.
+
+
+Section 7 -- Other Terms and Conditions.
+
+ a. The Licensor shall not be bound by any additional or different
+ terms or conditions communicated by You unless expressly agreed.
+
+ b. Any arrangements, understandings, or agreements regarding the
+ Licensed Material not stated herein are separate from and
+ independent of the terms and conditions of this Public License.
+
+
+Section 8 -- Interpretation.
+
+ a. For the avoidance of doubt, this Public License does not, and
+ shall not be interpreted to, reduce, limit, restrict, or impose
+ conditions on any use of the Licensed Material that could lawfully
+ be made without permission under this Public License.
+
+ b. To the extent possible, if any provision of this Public License is
+ deemed unenforceable, it shall be automatically reformed to the
+ minimum extent necessary to make it enforceable. If the provision
+ cannot be reformed, it shall be severed from this Public License
+ without affecting the enforceability of the remaining terms and
+ conditions.
+
+ c. No term or condition of this Public License will be waived and no
+ failure to comply consented to unless expressly agreed to by the
+ Licensor.
+
+ d. Nothing in this Public License constitutes or may be interpreted
+ as a limitation upon, or waiver of, any privileges and immunities
+ that apply to the Licensor or You, including from the legal
+ processes of any jurisdiction or authority.
+
+
+=======================================================================
+
+Creative Commons is not a party to its public
+licenses. Notwithstanding, Creative Commons may elect to apply one of
+its public licenses to material it publishes and in those instances
+will be considered the “Licensor.” The text of the Creative Commons
+public licenses is dedicated to the public domain under the CC0 Public
+Domain Dedication. Except for the limited purpose of indicating that
+material is shared under a Creative Commons public license or as
+otherwise permitted by the Creative Commons policies published at
+creativecommons.org/policies, Creative Commons does not authorize the
+use of the trademark "Creative Commons" or any other trademark or logo
+of Creative Commons without its prior written consent including,
+without limitation, in connection with any unauthorized modifications
+to any of its public licenses or any other arrangements,
+understandings, or agreements concerning use of licensed material. For
+the avoidance of doubt, this paragraph does not form part of the
+public licenses.
+
+Creative Commons may be contacted at creativecommons.org.
diff --git a/mods/basic_materials/electrical-electronic.lua b/mods/basic_materials/electrical-electronic.lua
new file mode 100644
index 00000000..91fac4e3
--- /dev/null
+++ b/mods/basic_materials/electrical-electronic.lua
@@ -0,0 +1,86 @@
+-- Translation support
+local S = minetest.get_translator("basic_materials")
+
+-- items
+
+minetest.register_craftitem("basic_materials:silicon", {
+ description = S("Silicon lump"),
+ inventory_image = "basic_materials_silicon.png",
+})
+
+minetest.register_craftitem("basic_materials:ic", {
+ description = S("Simple Integrated Circuit"),
+ inventory_image = "basic_materials_ic.png",
+})
+
+minetest.register_craftitem("basic_materials:motor", {
+ description = S("Simple Motor"),
+ inventory_image = "basic_materials_motor.png",
+})
+
+minetest.register_craftitem("basic_materials:heating_element", {
+ description = S("Heating element"),
+ inventory_image = "basic_materials_heating_element.png",
+})
+
+minetest.register_craftitem("basic_materials:energy_crystal_simple", {
+ description = S("Simple energy crystal"),
+ inventory_image = "basic_materials_energy_crystal.png",
+})
+
+-- crafts
+
+minetest.register_craft( {
+ output = "mesecons_materials:silicon 4",
+ recipe = {
+ { "default:sand", "default:sand" },
+ { "default:sand", "default:steel_ingot" },
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:ic 4",
+ recipe = {
+ { "mesecons_materials:silicon", "mesecons_materials:silicon" },
+ { "mesecons_materials:silicon", "default:copper_ingot" },
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:motor 2",
+ recipe = {
+ { "default:mese_crystal_fragment", "basic_materials:copper_wire", "basic_materials:plastic_sheet" },
+ { "default:copper_ingot", "default:steel_ingot", "default:steel_ingot" },
+ { "default:mese_crystal_fragment", "basic_materials:copper_wire", "basic_materials:plastic_sheet" }
+ },
+ replacements = {
+ { "basic_materials:copper_wire", "basic_materials:empty_spool" },
+ { "basic_materials:copper_wire", "basic_materials:empty_spool" },
+ }
+})
+
+minetest.register_craft( {
+ output = "basic_materials:heating_element 2",
+ recipe = {
+ { "default:copper_ingot", "default:mese_crystal_fragment", "default:copper_ingot" }
+ },
+})
+
+minetest.register_craft({
+ --type = "shapeless",
+ output = "basic_materials:energy_crystal_simple 2",
+ recipe = {
+ { "default:mese_crystal_fragment", "default:torch", "default:mese_crystal_fragment" },
+ { "default:diamond", "default:gold_ingot", "default:diamond" }
+ },
+})
+
+-- aliases
+
+minetest.register_alias("homedecor:ic", "basic_materials:ic")
+minetest.register_alias("homedecor:motor", "basic_materials:motor")
+minetest.register_alias("technic:motor", "basic_materials:motor")
+minetest.register_alias("homedecor:heating_element", "basic_materials:heating_element")
+minetest.register_alias("homedecor:power_crystal", "basic_materials:energy_crystal_simple")
+
+minetest.register_alias_force("mesecons_materials:silicon", "basic_materials:silicon")
diff --git a/mods/basic_materials/init.lua b/mods/basic_materials/init.lua
new file mode 100644
index 00000000..348c0598
--- /dev/null
+++ b/mods/basic_materials/init.lua
@@ -0,0 +1,15 @@
+-- Basic materials mod
+-- by Vanessa Dannenberg
+
+-- This mod supplies all those little random craft items that everyone always
+-- seems to need, such as metal bars (ala rebar), plastic, wire, and so on.
+
+local modpath = minetest.get_modpath("basic_materials")
+
+basic_materials = {}
+basic_materials.mod = { author = "Vanessa Dannenberg" }
+
+dofile(modpath.."/metals.lua")
+dofile(modpath.."/plastics.lua")
+dofile(modpath.."/electrical-electronic.lua")
+dofile(modpath.."/misc.lua")
diff --git a/mods/basic_materials/locale/basic_materials.de.tr b/mods/basic_materials/locale/basic_materials.de.tr
new file mode 100644
index 00000000..8fddd8a7
--- /dev/null
+++ b/mods/basic_materials/locale/basic_materials.de.tr
@@ -0,0 +1,33 @@
+# textdomain: basic_materials
+Silicon lump=Siliziumklumpen
+Simple Integrated Circuit=Einfacher Integrierter Schaltkreis
+Simple Motor=Einfacher Motor
+Heating element=Heizelement
+Simple energy crystal=Einfacher Energiekristall
+
+Spool of steel wire=Spule mit Stahldraht
+Spool of copper wire=Spule mit Kupferdraht
+Spool of silver wire=Spule mit Silberdraht
+Spool of gold wire=Spule mit Golddraht
+Steel Strip=Stahlstreifen
+Copper Strip=Kupferstreifen
+Steel Bar=Stahlstab
+Chainlinks (brass)=Messingkettenglieder
+Chainlinks (steel)=Stahlkettenglieder
+Brass Ingot=Messingbarren
+Steel gear=Stahlzahnrad
+Padlock=Vorhängeschloss
+Chain (steel, hanging)=Hängende Stahlkette
+Chain (brass, hanging)=Hängende Messingkette
+Brass Block=Messingblock
+
+Oil extract=Ölextrakt
+Unprocessed paraffin=Unverarbeitetes Paraffin
+Uncooked Terracotta Base=Ungebranntes Terrakotta
+Wet Cement=Nasser Zement
+Cement=Zement
+Concrete Block=Betonblock
+
+Plastic sheet=Kunststoffplatte
+Plastic strips=Kunststoffstreifen
+Empty wire spool=Leere Drahtspule
diff --git a/mods/basic_materials/locale/basic_materials.fr.tr b/mods/basic_materials/locale/basic_materials.fr.tr
new file mode 100644
index 00000000..0bebf79d
--- /dev/null
+++ b/mods/basic_materials/locale/basic_materials.fr.tr
@@ -0,0 +1,33 @@
+# textdomain: basic_materials
+Silicon lump=Morceau de silicium
+Simple Integrated Circuit=Circuit intégré simple
+Simple Motor=Moteur simple
+Heating element=Élément chauffant
+Simple energy crystal=Cristal d’énergie simple
+
+Spool of steel wire=Bobine de fil d’acier
+Spool of copper wire=Bobine de fil de cuivre
+Spool of silver wire=Bobine de fil d’argent
+Spool of gold wire=Bobine de fil d’or
+Steel Strip=Bande de acier
+Copper Strip=Bande de cuivre
+Steel Bar=Barre d’acier
+Chainlinks (brass)=Maillon en laiton
+Chainlinks (steel)=Maillon en acier
+Brass Ingot=Lingot de laiton
+Steel gear=Rouage en acier
+Padlock=Cadenas
+Chain (steel, hanging)=Chaine en acier
+Chain (brass, hanging)=Chaine en laiton
+Brass Block=Bloc de laiton
+
+Oil extract=Extrait d’huile
+Unprocessed paraffin=Paraffine non transformée
+Uncooked Terracotta Base=Argile crue
+Wet Cement=Ciment humide
+Cement=Ciment
+Concrete Block=Bloc de béton
+
+Plastic sheet=Morceau de plastique
+Plastic strips=Bande de plastique
+Empty wire spool=Bobine de fil vide
diff --git a/mods/basic_materials/locale/basic_materials.it.tr b/mods/basic_materials/locale/basic_materials.it.tr
new file mode 100644
index 00000000..aae0b3be
--- /dev/null
+++ b/mods/basic_materials/locale/basic_materials.it.tr
@@ -0,0 +1,34 @@
+# textdomain: basic_materials
+# Author: Salvo 'LtWorf' Tomaselli
+Silicon lump=Grumo di silicio
+Simple Integrated Circuit=Circuito integrato semplice
+Simple Motor=Motore semplice
+Heating element=Elemento riscaldante
+Simple energy crystal=Cristallo di energia semplice
+
+Spool of steel wire=Bobina di filo d'acciaio
+Spool of copper wire=Bobina di filo di rame
+Spool of silver wire=Bobina di filo d'argento
+Spool of gold wire=Bobina di filo d'oro
+Steel Strip=Striscia d'acciaio
+Copper Strip=Striscia di rame
+Steel Bar=Barra d'acciaio
+Chainlinks (brass)=Catena (ottone)
+Chainlinks (steel)=Catena (acciaio)
+Brass Ingot=Lingotto di ottone
+Steel gear=Ingranaggio d'acciaio
+Padlock=Catenaccio
+Chain (steel, hanging)=Catena (acciaio, pendente)
+Chain (brass, hanging)=Catena (ottone, pendente)
+Brass Block=Blocco di ottone
+
+Oil extract=Estratto d'olio
+Unprocessed paraffin=Paraffina grezza
+Uncooked Terracotta Base=Argilla cruda
+Wet Cement=Cemento umido
+Cement=Cemento
+Concrete Block=Blocco di calcestruzzo
+
+Plastic sheet=Foglio di plastica
+Plastic strips=Striscia di plastica
+Empty wire spool=Rocchetto vuoto
diff --git a/mods/basic_materials/locale/basic_materials.ru.tr b/mods/basic_materials/locale/basic_materials.ru.tr
new file mode 100644
index 00000000..85e9c0cf
--- /dev/null
+++ b/mods/basic_materials/locale/basic_materials.ru.tr
@@ -0,0 +1,33 @@
+# textdomain: basic_materials
+Silicon lump=Кусок Кремния
+Simple Integrated Circuit=Микросхема
+Simple Motor=Мотор
+Heating element=Нить Накала
+Simple energy crystal=Энергетический Кристалл
+
+Spool of steel wire=Катушка Стальной Проволоки
+Spool of copper wire=Катушка Медной Проволоки
+Spool of silver wire=Катушка Серебрянной Проволоки
+Spool of gold wire=Катушка Золотой Проволоки
+Steel Strip=Стальная Полоса
+Copper Strip=Медная Полоса
+Steel Bar=Стальной Прут
+Chainlinks (brass)=Латунные Звенья
+Chainlinks (steel)=Стальные Звенья
+Brass Ingot=Латунный Брусок
+Steel gear=Стальная Шестерня
+Padlock=Навесной Замок
+Chain (steel, hanging)=Стальная Цепь
+Chain (brass, hanging)=Латунная Цепь
+Brass Block=Латунный Блок
+
+Oil extract=Масляный Экстракт
+Unprocessed paraffin=Необработанный Парафин
+Uncooked Terracotta Base=Ком Мокрого Терракота
+Wet Cement=Ком Мокрого Цемента
+Cement=Цемент
+Concrete Block=Железобетон
+
+Plastic sheet=Пластиковый Лист
+Plastic strips=Пластиковая Полоса
+Empty wire spool=Пустая Катушка
diff --git a/mods/basic_materials/metals.lua b/mods/basic_materials/metals.lua
new file mode 100644
index 00000000..0a3243b0
--- /dev/null
+++ b/mods/basic_materials/metals.lua
@@ -0,0 +1,300 @@
+-- Translation support
+local S = minetest.get_translator("basic_materials")
+
+-- items
+
+minetest.register_craftitem("basic_materials:steel_wire", {
+ description = S("Spool of steel wire"),
+ groups = { wire = 1 },
+ inventory_image = "basic_materials_steel_wire.png"
+})
+
+minetest.register_craftitem("basic_materials:copper_wire", {
+ description = S("Spool of copper wire"),
+ groups = { wire = 1 },
+ inventory_image = "basic_materials_copper_wire.png"
+})
+
+minetest.register_craftitem("basic_materials:silver_wire", {
+ description = S("Spool of silver wire"),
+ groups = { wire = 1 },
+ inventory_image = "basic_materials_silver_wire.png"
+})
+
+minetest.register_craftitem("basic_materials:gold_wire", {
+ description = S("Spool of gold wire"),
+ groups = { wire = 1 },
+ inventory_image = "basic_materials_gold_wire.png"
+})
+
+minetest.register_craftitem("basic_materials:steel_strip", {
+ description = S("Steel Strip"),
+ groups = { strip = 1 },
+ inventory_image = "basic_materials_steel_strip.png"
+})
+
+minetest.register_craftitem("basic_materials:copper_strip", {
+ description = S("Copper Strip"),
+ groups = { strip = 1 },
+ inventory_image = "basic_materials_copper_strip.png"
+})
+
+minetest.register_craftitem("basic_materials:steel_bar", {
+ description = S("Steel Bar"),
+ inventory_image = "basic_materials_steel_bar.png",
+})
+
+minetest.register_craftitem("basic_materials:chainlink_brass", {
+ description = S("Chainlinks (brass)"),
+ groups = { chainlinks = 1 },
+ inventory_image = "basic_materials_chainlink_brass.png"
+})
+
+minetest.register_craftitem("basic_materials:chainlink_steel", {
+ description = S("Chainlinks (steel)"),
+ groups = { chainlinks = 1 },
+ inventory_image = "basic_materials_chainlink_steel.png"
+})
+
+minetest.register_craftitem("basic_materials:brass_ingot", {
+ description = S("Brass Ingot"),
+ inventory_image = "basic_materials_brass_ingot.png",
+})
+
+minetest.register_craftitem("basic_materials:gear_steel", {
+ description = S("Steel gear"),
+ inventory_image = "basic_materials_gear_steel.png"
+})
+
+minetest.register_craftitem("basic_materials:padlock", {
+ description = S("Padlock"),
+ inventory_image = "basic_materials_padlock.png"
+})
+
+-- nodes
+
+local chains_sbox = {
+ type = "fixed",
+ fixed = { -0.1, -0.5, -0.1, 0.1, 0.5, 0.1 }
+}
+
+minetest.register_node("basic_materials:chain_steel", {
+ description = S("Chain (steel, hanging)"),
+ drawtype = "mesh",
+ mesh = "basic_materials_chains.obj",
+ tiles = {"basic_materials_chain_steel.png"},
+ walkable = false,
+ climbable = true,
+ sunlight_propagates = true,
+ paramtype = "light",
+ inventory_image = "basic_materials_chain_steel_inv.png",
+ groups = {cracky=3},
+ selection_box = chains_sbox,
+})
+
+minetest.register_node("basic_materials:chain_brass", {
+ description = S("Chain (brass, hanging)"),
+ drawtype = "mesh",
+ mesh = "basic_materials_chains.obj",
+ tiles = {"basic_materials_chain_brass.png"},
+ walkable = false,
+ climbable = true,
+ sunlight_propagates = true,
+ paramtype = "light",
+ inventory_image = "basic_materials_chain_brass_inv.png",
+ groups = {cracky=3},
+ selection_box = chains_sbox,
+})
+
+minetest.register_node("basic_materials:brass_block", {
+ description = S("Brass Block"),
+ tiles = { "basic_materials_brass_block.png" },
+ is_ground_content = false,
+ groups = {cracky=1, level=2},
+ sounds = default.node_sound_metal_defaults()
+})
+
+-- crafts
+
+minetest.register_craft( {
+ output = "basic_materials:copper_wire 2",
+ type = "shapeless",
+ recipe = {
+ "default:copper_ingot",
+ "basic_materials:empty_spool",
+ "basic_materials:empty_spool",
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:silver_wire 2",
+ type = "shapeless",
+ recipe = {
+ "moreores:silver_ingot",
+ "basic_materials:empty_spool",
+ "basic_materials:empty_spool",
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:gold_wire 2",
+ type = "shapeless",
+ recipe = {
+ "default:gold_ingot",
+ "basic_materials:empty_spool",
+ "basic_materials:empty_spool",
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:steel_wire 2",
+ type = "shapeless",
+ recipe = {
+ "default:steel_ingot",
+ "basic_materials:empty_spool",
+ "basic_materials:empty_spool",
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:steel_strip 12",
+ recipe = {
+ { "", "default:steel_ingot", "" },
+ { "default:steel_ingot", "", "" },
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:copper_strip 12",
+ recipe = {
+ { "", "default:copper_ingot", "" },
+ { "default:copper_ingot", "", "" },
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:steel_bar 6",
+ recipe = {
+ { "", "", "default:steel_ingot" },
+ { "", "default:steel_ingot", "" },
+ { "default:steel_ingot", "", "" },
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:padlock 2",
+ recipe = {
+ { "basic_materials:steel_bar" },
+ { "default:steel_ingot" },
+ { "default:steel_ingot" },
+ },
+})
+
+minetest.register_craft({
+ output = "basic_materials:chainlink_steel 12",
+ recipe = {
+ {"", "default:steel_ingot", "default:steel_ingot"},
+ { "default:steel_ingot", "", "default:steel_ingot" },
+ { "default:steel_ingot", "default:steel_ingot", "" },
+ },
+})
+
+minetest.register_craft({
+ output = "basic_materials:chainlink_brass 12",
+ recipe = {
+ {"", "basic_materials:brass_ingot", "basic_materials:brass_ingot"},
+ { "basic_materials:brass_ingot", "", "basic_materials:brass_ingot" },
+ { "basic_materials:brass_ingot", "basic_materials:brass_ingot", "" },
+ },
+})
+
+minetest.register_craft({
+ output = 'basic_materials:chain_steel 2',
+ recipe = {
+ {"basic_materials:chainlink_steel"},
+ {"basic_materials:chainlink_steel"},
+ {"basic_materials:chainlink_steel"}
+ }
+})
+
+minetest.register_craft({
+ output = 'basic_materials:chain_brass 2',
+ recipe = {
+ {"basic_materials:chainlink_brass"},
+ {"basic_materials:chainlink_brass"},
+ {"basic_materials:chainlink_brass"}
+ }
+})
+
+minetest.register_craft( {
+ output = "basic_materials:gear_steel 6",
+ recipe = {
+ { "", "default:steel_ingot", "" },
+ { "default:steel_ingot","basic_materials:chainlink_steel", "default:steel_ingot" },
+ { "", "default:steel_ingot", "" }
+ },
+})
+
+minetest.register_craft( {
+ type = "shapeless",
+ output = "basic_materials:brass_ingot 3",
+ recipe = {
+ "default:copper_ingot",
+ "default:copper_ingot",
+ "moreores:silver_ingot",
+ },
+})
+
+if not minetest.get_modpath("moreores") then
+ -- Without moreores, there still should be a way to create brass.
+ minetest.register_craft( {
+ output = "basic_materials:brass_ingot 9",
+ recipe = {
+ {"default:copper_ingot", "default:tin_ingot", "default:copper_ingot"},
+ {"default:gold_ingot", "default:copper_ingot", "default:gold_ingot"},
+ {"default:copper_ingot", "default:tin_ingot", "default:copper_ingot"},
+ },
+ })
+end
+
+minetest.register_craft( {
+ type = "shapeless",
+ output = "basic_materials:brass_ingot 9",
+ recipe = { "basic_materials:brass_block" },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:brass_block",
+ recipe = {
+ { "basic_materials:brass_ingot", "basic_materials:brass_ingot", "basic_materials:brass_ingot" },
+ { "basic_materials:brass_ingot", "basic_materials:brass_ingot", "basic_materials:brass_ingot" },
+ { "basic_materials:brass_ingot", "basic_materials:brass_ingot", "basic_materials:brass_ingot" },
+ },
+})
+
+-- aliases
+
+minetest.register_alias("homedecor:copper_wire", "basic_materials:copper_wire")
+minetest.register_alias("technic:fine_copper_wire", "basic_materials:copper_wire")
+minetest.register_alias("technic:fine_silver_wire", "basic_materials:silver_wire")
+minetest.register_alias("technic:fine_gold_wire", "basic_materials:gold_wire")
+
+minetest.register_alias("homedecor:steel_wire", "basic_materials:steel_wire")
+
+minetest.register_alias("homedecor:brass_ingot", "basic_materials:brass_ingot")
+minetest.register_alias("technic:brass_ingot", "basic_materials:brass_ingot")
+minetest.register_alias("technic:brass_block", "basic_materials:brass_block")
+
+minetest.register_alias("homedecor:copper_strip", "basic_materials:copper_strip")
+minetest.register_alias("homedecor:steel_strip", "basic_materials:steel_strip")
+
+minetest.register_alias_force("glooptest:chainlink", "basic_materials:chainlink_steel")
+minetest.register_alias_force("homedecor:chainlink_steel", "basic_materials:chainlink_steel")
+minetest.register_alias("homedecor:chainlink_brass", "basic_materials:chainlink_brass")
+minetest.register_alias("chains:chain", "basic_materials:chain_steel")
+minetest.register_alias("chains:chain_brass", "basic_materials:chain_brass")
+
+minetest.register_alias("pipeworks:gear", "basic_materials:gear_steel")
+
+minetest.register_alias("technic:rebar", "basic_materials:steel_bar")
+
diff --git a/mods/basic_materials/misc.lua b/mods/basic_materials/misc.lua
new file mode 100644
index 00000000..00128972
--- /dev/null
+++ b/mods/basic_materials/misc.lua
@@ -0,0 +1,126 @@
+-- Translation support
+local S = minetest.get_translator("basic_materials")
+
+-- items
+
+minetest.register_craftitem("basic_materials:oil_extract", {
+ description = S("Oil extract"),
+ inventory_image = "basic_materials_oil_extract.png",
+})
+
+minetest.register_craftitem("basic_materials:paraffin", {
+ description = S("Unprocessed paraffin"),
+ inventory_image = "basic_materials_paraffin.png",
+})
+
+minetest.register_craftitem("basic_materials:terracotta_base", {
+ description = S("Uncooked Terracotta Base"),
+ inventory_image = "basic_materials_terracotta_base.png",
+})
+
+minetest.register_craftitem("basic_materials:wet_cement", {
+ description = S("Wet Cement"),
+ inventory_image = "basic_materials_wet_cement.png",
+})
+
+-- nodes
+
+minetest.register_node("basic_materials:cement_block", {
+ description = S("Cement"),
+ tiles = {"basic_materials_cement_block.png"},
+ is_ground_content = true,
+ groups = {cracky=2},
+ sounds = default.node_sound_stone_defaults(),
+})
+
+minetest.register_node("basic_materials:concrete_block", {
+ description = S("Concrete Block"),
+ tiles = {"basic_materials_concrete_block.png",},
+ groups = {cracky=1, level=2, concrete=1},
+ sounds = default.node_sound_stone_defaults(),
+})
+
+-- crafts
+
+minetest.register_craft({
+ type = "shapeless",
+ output = "basic_materials:oil_extract 2",
+ recipe = {
+ "group:leaves",
+ "group:leaves",
+ "group:leaves",
+ "group:leaves",
+ "group:leaves",
+ "group:leaves"
+ }
+})
+
+minetest.register_craft({
+ type = "cooking",
+ output = "basic_materials:paraffin",
+ recipe = "basic_materials:oil_extract",
+})
+
+minetest.register_craft({
+ type = "fuel",
+ recipe = "basic_materials:oil_extract",
+ burntime = 30,
+})
+
+minetest.register_craft({
+ type = "fuel",
+ recipe = "basic_materials:paraffin",
+ burntime = 30,
+})
+
+minetest.register_craft( {
+ type = "shapeless",
+ output = "basic_materials:terracotta_base 8",
+ recipe = {
+ "bucket:bucket_water",
+ "default:clay_lump",
+ "default:gravel",
+ },
+ replacements = { {"bucket:bucket_water", "bucket:bucket_empty"}, },
+})
+
+minetest.register_craft({
+ type = "shapeless",
+ output = "basic_materials:wet_cement 3",
+ recipe = {
+ "default:dirt",
+ "dye:dark_grey",
+ "dye:dark_grey",
+ "dye:dark_grey",
+ "bucket:bucket_water"
+ },
+ replacements = {{'bucket:bucket_water', 'bucket:bucket_empty'},},
+})
+
+minetest.register_craft({
+ type = "cooking",
+ output = "basic_materials:cement_block",
+ recipe = "basic_materials:wet_cement",
+ cooktime = 8
+})
+
+minetest.register_craft({
+ output = 'basic_materials:concrete_block 6',
+ recipe = {
+ {'group:sand', 'basic_materials:wet_cement', 'default:gravel'},
+ {'basic_materials:steel_bar', 'basic_materials:wet_cement', 'basic_materials:steel_bar'},
+ {'default:gravel', 'basic_materials:wet_cement', 'group:sand'},
+ }
+})
+
+-- aliases
+
+minetest.register_alias("homedecor:oil_extract", "basic_materials:oil_extract")
+minetest.register_alias("homedecor:paraffin", "basic_materials:paraffin")
+minetest.register_alias("homedecor:plastic_base", "basic_materials:paraffin")
+minetest.register_alias("homedecor:terracotta_base", "basic_materials:terracotta_base")
+
+minetest.register_alias("gloopblocks:wet_cement", "basic_materials:wet_cement")
+minetest.register_alias("gloopblocks:cement", "basic_materials:cement_block")
+
+minetest.register_alias("technic:concrete", "basic_materials:concrete_block")
diff --git a/mods/basic_materials/mod.conf b/mods/basic_materials/mod.conf
new file mode 100644
index 00000000..7234bfe9
--- /dev/null
+++ b/mods/basic_materials/mod.conf
@@ -0,0 +1,4 @@
+name = basic_materials
+depends = default
+optional_depends = moreores
+min_minetest_version = 5.2.0
diff --git a/mods/basic_materials/models/basic_materials_chains.obj b/mods/basic_materials/models/basic_materials_chains.obj
new file mode 100644
index 00000000..78724c98
--- /dev/null
+++ b/mods/basic_materials/models/basic_materials_chains.obj
@@ -0,0 +1,881 @@
+# Blender v2.73 (sub 0) OBJ File: 'chains.blend'
+# www.blender.org
+o Torus.016_Torus
+v 0.000000 -0.429978 0.000002
+v 0.000000 -0.401109 0.055211
+v -0.014044 -0.391975 0.048870
+v -0.014044 -0.423304 0.000002
+v -0.009826 -0.379748 0.040970
+v -0.009826 -0.406012 0.000002
+v 0.009826 -0.379748 0.040970
+v 0.009826 -0.406012 0.000002
+v 0.014044 -0.391975 0.048870
+v 0.014044 -0.423304 0.000002
+v 0.000000 -0.316336 0.080195
+v -0.014044 -0.316336 0.069112
+v -0.009826 -0.316336 0.057941
+v 0.009826 -0.316336 0.057941
+v 0.014044 -0.316336 0.069112
+v 0.000000 -0.231564 0.055211
+v -0.014044 -0.240700 0.048870
+v -0.009826 -0.252925 0.040970
+v 0.009826 -0.252925 0.040970
+v 0.014044 -0.240700 0.048870
+v 0.000000 -0.202695 0.000002
+v -0.014044 -0.209368 0.000002
+v -0.009826 -0.226661 0.000002
+v 0.009826 -0.226661 0.000002
+v 0.014044 -0.209368 0.000002
+v 0.000000 -0.231564 -0.055206
+v -0.014044 -0.240700 -0.048868
+v -0.009826 -0.252925 -0.040967
+v 0.009826 -0.252925 -0.040967
+v 0.014044 -0.240700 -0.048865
+v 0.000000 -0.316336 -0.080190
+v -0.014044 -0.316336 -0.069108
+v -0.009826 -0.316336 -0.057936
+v 0.009826 -0.316336 -0.057936
+v 0.014044 -0.316336 -0.069108
+v 0.000000 -0.400361 -0.055206
+v -0.014044 -0.391975 -0.048868
+v -0.009826 -0.379748 -0.040967
+v 0.009826 -0.379748 -0.040967
+v 0.014044 -0.391975 -0.048868
+v 0.000000 -0.262249 0.000002
+v -0.061672 -0.233381 0.000002
+v -0.054590 -0.224245 -0.012569
+v 0.000000 -0.255577 -0.012569
+v -0.045765 -0.212018 -0.008794
+v 0.000000 -0.238285 -0.008794
+v -0.045765 -0.212018 0.008798
+v 0.000000 -0.238285 0.008798
+v -0.054590 -0.224245 0.012574
+v 0.000000 -0.255577 0.012574
+v -0.089582 -0.148609 0.000002
+v -0.077200 -0.148609 -0.012569
+v -0.064722 -0.148609 -0.008794
+v -0.064722 -0.148609 0.008799
+v -0.077200 -0.148609 0.012574
+v -0.061672 -0.063837 0.000002
+v -0.054590 -0.072971 -0.012569
+v -0.045765 -0.085198 -0.008794
+v -0.045765 -0.085198 0.008799
+v -0.054590 -0.072971 0.012574
+v 0.000000 -0.034967 0.000002
+v 0.000000 -0.041641 -0.012569
+v 0.000000 -0.058933 -0.008794
+v 0.000000 -0.058933 0.008799
+v 0.000000 -0.041641 0.012574
+v 0.061672 -0.063837 0.000002
+v 0.054590 -0.072971 -0.012569
+v 0.045765 -0.085198 -0.008794
+v 0.045765 -0.085198 0.008799
+v 0.054590 -0.072971 0.012574
+v 0.089582 -0.148609 0.000002
+v 0.077200 -0.148609 -0.012569
+v 0.064722 -0.148609 -0.008794
+v 0.064722 -0.148609 0.008799
+v 0.077200 -0.148609 0.012574
+v 0.061672 -0.232631 0.000002
+v 0.054590 -0.224245 -0.012569
+v 0.045765 -0.212018 -0.008794
+v 0.045765 -0.212018 0.008798
+v 0.054590 -0.224245 0.012574
+v 0.000000 0.073316 0.000002
+v 0.061672 0.102183 0.000002
+v 0.054590 0.111319 0.012574
+v 0.000000 0.079988 0.012574
+v 0.045765 0.123546 0.008799
+v 0.000000 0.097280 0.008799
+v 0.045765 0.123546 -0.008794
+v 0.000000 0.097280 -0.008794
+v 0.054590 0.111319 -0.012569
+v 0.000000 0.079988 -0.012569
+v 0.089582 0.186956 0.000002
+v 0.077200 0.186956 0.012574
+v 0.064722 0.186956 0.008799
+v 0.064722 0.186956 -0.008794
+v 0.077200 0.186956 -0.012569
+v 0.061672 0.271728 0.000002
+v 0.054590 0.262594 0.012574
+v 0.045765 0.250367 0.008799
+v 0.045765 0.250367 -0.008794
+v 0.054590 0.262594 -0.012569
+v 0.000000 0.300597 0.000002
+v 0.000000 0.293923 0.012574
+v 0.000000 0.276631 0.008799
+v 0.000000 0.276631 -0.008794
+v 0.000000 0.293923 -0.012569
+v -0.061672 0.271728 0.000002
+v -0.054590 0.262594 0.012574
+v -0.045765 0.250367 0.008799
+v -0.045765 0.250367 -0.008794
+v -0.054590 0.262594 -0.012569
+v -0.089582 0.186956 0.000002
+v -0.077200 0.186956 0.012574
+v -0.064722 0.186956 0.008799
+v -0.064722 0.186956 -0.008794
+v -0.077200 0.186956 -0.012569
+v -0.061672 0.102931 0.000002
+v -0.054590 0.111319 0.012574
+v -0.045765 0.123546 0.008799
+v -0.045765 0.123546 -0.008794
+v -0.054590 0.111319 -0.012569
+v 0.000000 -0.095037 0.000002
+v 0.000000 -0.066168 -0.055206
+v 0.014044 -0.057034 -0.048868
+v 0.014044 -0.088363 0.000002
+v 0.009826 -0.044807 -0.040967
+v 0.009826 -0.071071 0.000002
+v -0.009826 -0.044807 -0.040967
+v -0.009826 -0.071071 0.000002
+v -0.014044 -0.057034 -0.048868
+v -0.014044 -0.088363 0.000002
+v 0.000000 0.018605 -0.080190
+v 0.014044 0.018605 -0.069108
+v 0.009826 0.018605 -0.057936
+v -0.009826 0.018605 -0.057936
+v -0.014044 0.018605 -0.069108
+v 0.000000 0.103377 -0.055206
+v 0.014044 0.094243 -0.048868
+v 0.009826 0.082016 -0.040967
+v -0.009826 0.082016 -0.040967
+v -0.014044 0.094243 -0.048868
+v 0.000000 0.132246 0.000002
+v 0.014044 0.125572 0.000002
+v 0.009826 0.108280 0.000002
+v -0.009826 0.108280 0.000002
+v -0.014044 0.125572 0.000002
+v 0.000000 0.103377 0.055211
+v 0.014044 0.094243 0.048870
+v 0.009826 0.082016 0.040970
+v -0.009826 0.082016 0.040970
+v -0.014044 0.094243 0.048870
+v 0.000000 0.018605 0.080195
+v 0.014044 0.018605 0.069112
+v 0.009826 0.018605 0.057941
+v -0.009826 0.018605 0.057941
+v -0.014044 0.018605 0.069112
+v 0.000000 -0.065420 0.055211
+v 0.014044 -0.057032 0.048870
+v 0.009826 -0.044807 0.040970
+v -0.009826 -0.044807 0.040970
+v -0.014044 -0.057032 0.048870
+v 0.000000 -0.598329 0.000002
+v 0.061672 -0.569460 0.000002
+v 0.054590 -0.560326 0.012574
+v 0.000000 -0.591655 0.012574
+v 0.045765 -0.548099 0.008798
+v 0.000000 -0.574363 0.008798
+v 0.045765 -0.548099 -0.008794
+v 0.000000 -0.574363 -0.008794
+v 0.054590 -0.560326 -0.012569
+v 0.000000 -0.591655 -0.012569
+v 0.089582 -0.484687 0.000002
+v 0.077200 -0.484687 0.012574
+v 0.064722 -0.484687 0.008798
+v 0.064722 -0.484687 -0.008794
+v 0.077200 -0.484687 -0.012569
+v 0.061672 -0.399915 0.000002
+v 0.054590 -0.409051 0.012574
+v 0.045765 -0.421278 0.008798
+v 0.045765 -0.421278 -0.008794
+v 0.054590 -0.409051 -0.012569
+v 0.000000 -0.371048 0.000002
+v 0.000000 -0.377719 0.012574
+v 0.000000 -0.395012 0.008798
+v 0.000000 -0.395012 -0.008794
+v 0.000000 -0.377719 -0.012569
+v -0.061672 -0.399915 0.000002
+v -0.054590 -0.409051 0.012574
+v -0.045765 -0.421278 0.008798
+v -0.045765 -0.421278 -0.008794
+v -0.054590 -0.409051 -0.012569
+v -0.089582 -0.484687 0.000002
+v -0.077200 -0.484687 0.012574
+v -0.064722 -0.484687 0.008798
+v -0.064722 -0.484687 -0.008794
+v -0.077200 -0.484687 -0.012569
+v -0.061672 -0.568712 0.000002
+v -0.054590 -0.560326 0.012574
+v -0.045765 -0.548099 0.008798
+v -0.045765 -0.548099 -0.008794
+v -0.054590 -0.560326 -0.012569
+v 0.000000 0.241043 0.000002
+v 0.000000 0.269910 0.055211
+v -0.014044 0.279047 0.048870
+v -0.014044 0.247717 0.000002
+v -0.009826 0.291274 0.040970
+v -0.009826 0.265007 0.000002
+v 0.009826 0.291274 0.040970
+v 0.009826 0.265007 0.000002
+v 0.014044 0.279047 0.048870
+v 0.014044 0.247717 0.000002
+v 0.000000 0.354683 0.080195
+v -0.014044 0.354683 0.069112
+v -0.009826 0.354683 0.057941
+v 0.009826 0.354683 0.057941
+v 0.014044 0.354683 0.069112
+v 0.000000 0.439455 0.055211
+v -0.014044 0.430321 0.048870
+v -0.009826 0.418094 0.040970
+v 0.009826 0.418094 0.040970
+v 0.014044 0.430321 0.048870
+v 0.000000 0.468325 0.000002
+v -0.014044 0.461651 0.000002
+v -0.009826 0.444361 0.000002
+v 0.009826 0.444361 0.000002
+v 0.014044 0.461651 0.000002
+v 0.000000 0.439455 -0.055206
+v -0.014044 0.430321 -0.048868
+v -0.009826 0.418094 -0.040967
+v 0.009826 0.418094 -0.040967
+v 0.014044 0.430321 -0.048868
+v 0.000000 0.354683 -0.080190
+v -0.014044 0.354683 -0.069108
+v -0.009826 0.354683 -0.057936
+v 0.009826 0.354683 -0.057936
+v 0.014044 0.354683 -0.069108
+v 0.000000 0.270661 -0.055206
+v -0.014044 0.279047 -0.048868
+v -0.009826 0.291274 -0.040967
+v 0.009826 0.291274 -0.040967
+v 0.014044 0.279047 -0.048868
+vt 0.187500 0.125000
+vt 0.250000 0.125000
+vt 0.250000 0.187500
+vt 0.187500 0.187500
+vt 0.250000 0.250000
+vt 0.187500 0.250000
+vt 0.250000 0.312500
+vt 0.187500 0.312500
+vt 0.250000 0.375000
+vt 0.187500 0.375000
+vt 0.187500 0.062500
+vt 0.250000 0.062500
+vt 0.312500 0.125000
+vt 0.312500 0.187500
+vt 0.312500 0.250000
+vt 0.312500 0.312500
+vt 0.312500 0.375000
+vt 0.312500 0.062500
+vt 0.375000 0.125000
+vt 0.375000 0.187500
+vt 0.375000 0.250000
+vt 0.375000 0.312500
+vt 0.375000 0.375000
+vt 0.375000 0.062500
+vt 0.437500 0.125000
+vt 0.437500 0.187500
+vt 0.437500 0.250000
+vt 0.437500 0.312500
+vt 0.437500 0.375000
+vt 0.437500 0.062500
+vt 0.500000 0.125000
+vt 0.500000 0.187500
+vt 0.500000 0.250000
+vt 0.500000 0.312500
+vt 0.500000 0.375000
+vt 0.500000 0.062500
+vt -0.000000 0.125000
+vt 0.062500 0.125000
+vt 0.062500 0.187500
+vt -0.000000 0.187500
+vt 0.062500 0.250000
+vt -0.000000 0.250000
+vt 0.062500 0.312500
+vt -0.000000 0.312500
+vt 0.062500 0.375000
+vt -0.000000 0.375000
+vt -0.000000 0.062500
+vt 0.062500 0.062500
+vt 0.125000 0.125000
+vt 0.125000 0.187500
+vt 0.125000 0.250000
+vt 0.125000 0.312500
+vt 0.125000 0.375000
+vt 0.125000 0.062500
+vt 0.750000 0.625000
+vt 0.812500 0.625000
+vt 0.812500 0.687500
+vt 0.750000 0.687500
+vt 0.750000 0.375000
+vt 0.812500 0.375000
+vt 0.812500 0.437500
+vt 0.750000 0.437500
+vt 0.812500 0.500000
+vt 0.750000 0.500000
+vt 0.812500 0.562500
+vt 0.750000 0.562500
+vt 0.875000 0.625000
+vt 0.875000 0.687500
+vt 0.875000 0.375000
+vt 0.875000 0.437500
+vt 0.875000 0.500000
+vt 0.875000 0.562500
+vt 0.937500 0.625000
+vt 0.937500 0.687500
+vt 0.937500 0.375000
+vt 0.937500 0.437500
+vt 0.937500 0.500000
+vt 0.937500 0.562500
+vt 1.000000 0.625000
+vt 1.000000 0.687500
+vt 1.000000 0.375000
+vt 1.000000 0.437500
+vt 1.000000 0.500000
+vt 1.000000 0.562500
+vt 0.500000 0.625000
+vt 0.562500 0.625000
+vt 0.562500 0.687500
+vt 0.500000 0.687500
+vt 0.562500 0.375000
+vt 0.562500 0.437500
+vt 0.500000 0.437500
+vt 0.562500 0.500000
+vt 0.500000 0.500000
+vt 0.562500 0.562500
+vt 0.500000 0.562500
+vt 0.625000 0.625000
+vt 0.625000 0.687500
+vt 0.625000 0.375000
+vt 0.625000 0.437500
+vt 0.625000 0.500000
+vt 0.625000 0.562500
+vt 0.687500 0.625000
+vt 0.687500 0.687500
+vt 0.687500 0.375000
+vt 0.687500 0.437500
+vt 0.687500 0.500000
+vt 0.687500 0.562500
+vt 0.250000 0.625000
+vt 0.312500 0.625000
+vt 0.312500 0.687500
+vt 0.250000 0.687500
+vt 0.312500 0.437500
+vt 0.250000 0.437500
+vt 0.312500 0.500000
+vt 0.250000 0.500000
+vt 0.312500 0.562500
+vt 0.250000 0.562500
+vt 0.375000 0.625000
+vt 0.375000 0.687500
+vt 0.375000 0.437500
+vt 0.375000 0.500000
+vt 0.375000 0.562500
+vt 0.437500 0.625000
+vt 0.437500 0.687500
+vt 0.437500 0.437500
+vt 0.437500 0.500000
+vt 0.437500 0.562500
+vt -0.000000 0.625000
+vt 0.062500 0.625000
+vt 0.062500 0.687500
+vt -0.000000 0.687500
+vt 0.062500 0.437500
+vt -0.000000 0.437500
+vt 0.062500 0.500000
+vt -0.000000 0.500000
+vt 0.062500 0.562500
+vt -0.000000 0.562500
+vt 0.125000 0.625000
+vt 0.125000 0.687500
+vt 0.125000 0.437500
+vt 0.125000 0.500000
+vt 0.125000 0.562500
+vt 0.187500 0.625000
+vt 0.187500 0.687500
+vt 0.187500 0.437500
+vt 0.187500 0.500000
+vt 0.187500 0.562500
+vt 0.687500 0.750000
+vt 0.750000 0.750000
+vt 0.750000 0.812500
+vt 0.687500 0.812500
+vt 0.750000 0.875000
+vt 0.687500 0.875000
+vt 0.750000 0.937500
+vt 0.687500 0.937500
+vt 0.750000 1.000000
+vt 0.687500 1.000000
+vt 0.812500 0.750000
+vt 0.812500 0.812500
+vt 0.812500 0.875000
+vt 0.812500 0.937500
+vt 0.812500 1.000000
+vt 0.875000 0.750000
+vt 0.875000 0.812500
+vt 0.875000 0.875000
+vt 0.875000 0.937500
+vt 0.875000 1.000000
+vt 0.937500 0.750000
+vt 0.937500 0.812500
+vt 0.937500 0.875000
+vt 0.937500 0.937500
+vt 0.937500 1.000000
+vt 1.000000 0.750000
+vt 1.000000 0.812500
+vt 1.000000 0.875000
+vt 1.000000 0.937500
+vt 1.000000 1.000000
+vt 0.500000 0.750000
+vt 0.562500 0.750000
+vt 0.562500 0.812500
+vt 0.500000 0.812500
+vt 0.562500 0.875000
+vt 0.500000 0.875000
+vt 0.562500 0.937500
+vt 0.500000 0.937500
+vt 0.562500 1.000000
+vt 0.500000 1.000000
+vt 0.625000 0.750000
+vt 0.625000 0.812500
+vt 0.625000 0.875000
+vt 0.625000 0.937500
+vt 0.625000 1.000000
+vt 0.750000 0.312500
+vt 0.812500 0.312500
+vt 0.750000 0.062500
+vt 0.812500 0.062500
+vt 0.812500 0.125000
+vt 0.750000 0.125000
+vt 0.812500 0.187500
+vt 0.750000 0.187500
+vt 0.812500 0.250000
+vt 0.750000 0.250000
+vt 0.875000 0.312500
+vt 0.875000 0.062500
+vt 0.875000 0.125000
+vt 0.875000 0.187500
+vt 0.875000 0.250000
+vt 0.937500 0.312500
+vt 0.937500 0.062500
+vt 0.937500 0.125000
+vt 0.937500 0.187500
+vt 0.937500 0.250000
+vt 1.000000 0.312500
+vt 1.000000 0.062500
+vt 1.000000 0.125000
+vt 1.000000 0.187500
+vt 1.000000 0.250000
+vt 0.562500 0.312500
+vt 0.562500 0.062500
+vt 0.562500 0.125000
+vt 0.562500 0.187500
+vt 0.562500 0.250000
+vt 0.625000 0.312500
+vt 0.625000 0.062500
+vt 0.625000 0.125000
+vt 0.625000 0.187500
+vt 0.625000 0.250000
+vt 0.687500 0.312500
+vt 0.687500 0.062500
+vt 0.687500 0.125000
+vt 0.687500 0.187500
+vt 0.687500 0.250000
+vt 0.250000 0.937500
+vt 0.312500 0.937500
+vt 0.312500 1.000000
+vt 0.250000 1.000000
+vt 0.312500 0.750000
+vt 0.250000 0.750000
+vt 0.312500 0.812500
+vt 0.250000 0.812500
+vt 0.312500 0.875000
+vt 0.250000 0.875000
+vt 0.375000 0.937500
+vt 0.375000 1.000000
+vt 0.375000 0.750000
+vt 0.375000 0.812500
+vt 0.375000 0.875000
+vt 0.437500 0.937500
+vt 0.437500 1.000000
+vt 0.437500 0.750000
+vt 0.437500 0.812500
+vt 0.437500 0.875000
+vt 0.000000 0.937500
+vt 0.062500 0.937500
+vt 0.062500 1.000000
+vt 0.000000 1.000000
+vt 0.062500 0.750000
+vt 0.000000 0.750000
+vt 0.062500 0.812500
+vt 0.000000 0.812500
+vt 0.062500 0.875000
+vt 0.000000 0.875000
+vt 0.125000 0.937500
+vt 0.125000 1.000000
+vt 0.125000 0.750000
+vt 0.125000 0.812500
+vt 0.125000 0.875000
+vt 0.187500 0.937500
+vt 0.187500 1.000000
+vt 0.187500 0.750000
+vt 0.187500 0.812500
+vt 0.187500 0.875000
+vn 0.000000 -1.000000 -0.004800
+vn 0.000000 -0.657400 0.753500
+vn -0.898300 -0.248500 0.362300
+vn -0.863600 -0.504100 -0.003400
+vn -0.661500 0.421500 -0.620200
+vn -0.746000 0.665900 0.000000
+vn 0.661500 0.421500 -0.620200
+vn 0.746000 0.665900 0.000000
+vn 0.898300 -0.248500 0.362300
+vn 0.863600 -0.504100 -0.003400
+vn 0.000000 0.000000 1.000000
+vn -0.925200 0.000000 0.379500
+vn -0.617100 0.000000 -0.786900
+vn 0.617100 0.000000 -0.786900
+vn 0.925200 0.000000 0.379500
+vn 0.000000 0.657400 0.753500
+vn -0.898300 0.248400 0.362300
+vn -0.661500 -0.421500 -0.620200
+vn 0.661500 -0.421500 -0.620200
+vn 0.898300 0.248400 0.362300
+vn 0.000000 1.000000 0.000000
+vn -0.866100 0.499800 0.000000
+vn -0.746000 -0.665900 0.000000
+vn 0.746000 -0.665900 0.000000
+vn 0.866100 0.499800 0.000000
+vn 0.000000 0.657400 -0.753500
+vn -0.898300 0.248400 -0.362400
+vn -0.661600 -0.421500 0.620200
+vn 0.661500 -0.421500 0.620200
+vn 0.898300 0.248400 -0.362300
+vn 0.000000 -0.000900 -1.000000
+vn -0.924600 -0.000600 -0.380700
+vn -0.617100 0.000000 0.786900
+vn 0.617100 0.000000 0.786900
+vn 0.924700 -0.000600 -0.380700
+vn 0.000000 -0.650300 -0.759600
+vn -0.895600 -0.254600 -0.364800
+vn -0.661600 0.421500 0.620200
+vn 0.661600 0.421500 0.620200
+vn 0.895600 -0.254600 -0.364800
+vn 0.004900 -1.000000 0.000000
+vn -0.729700 -0.683800 0.000000
+vn -0.324500 -0.256300 -0.910500
+vn 0.003300 -0.475500 -0.879700
+vn 0.578700 0.436200 -0.689100
+vn 0.000000 0.666600 -0.745400
+vn 0.578700 0.436200 0.689100
+vn 0.000000 0.666600 0.745400
+vn -0.324500 -0.256300 0.910500
+vn 0.003300 -0.475500 0.879700
+vn -1.000000 0.000000 0.000000
+vn -0.359600 0.000000 -0.933100
+vn 0.756400 0.000000 -0.654100
+vn 0.756400 0.000000 0.654100
+vn -0.359600 0.000000 0.933100
+vn -0.729700 0.683700 0.000000
+vn -0.324500 0.256300 -0.910500
+vn 0.578700 -0.436200 -0.689100
+vn 0.578700 -0.436200 0.689100
+vn -0.324500 0.256300 0.910500
+vn 0.000000 0.470900 -0.882200
+vn 0.000000 -0.666600 -0.745400
+vn 0.000000 -0.666600 0.745400
+vn 0.000000 0.470900 0.882200
+vn 0.729700 0.683700 0.000000
+vn 0.324500 0.256300 -0.910500
+vn -0.578700 -0.436200 -0.689100
+vn -0.578700 -0.436200 0.689100
+vn 0.324500 0.256300 0.910500
+vn 1.000000 -0.001100 0.000000
+vn 0.361000 -0.000700 -0.932600
+vn -0.756400 0.000000 -0.654100
+vn -0.756400 0.000000 0.654100
+vn 0.361000 -0.000700 0.932600
+vn 0.736100 -0.676800 0.000000
+vn 0.327100 -0.263100 -0.907600
+vn -0.578700 0.436200 -0.689100
+vn -0.578700 0.436200 0.689100
+vn 0.327100 -0.263100 0.907600
+vn -0.004900 -1.000000 0.000000
+vn 0.729700 -0.683800 0.000000
+vn 0.324500 -0.256300 0.910500
+vn -0.003300 -0.475400 0.879700
+vn 0.324500 -0.256300 -0.910500
+vn -0.003300 -0.475400 -0.879700
+vn 1.000000 0.000000 0.000000
+vn 0.359600 0.000000 0.933100
+vn 0.359600 0.000000 -0.933100
+vn -1.000000 -0.001100 0.000000
+vn -0.361000 -0.000700 0.932600
+vn -0.361000 -0.000700 -0.932600
+vn -0.736100 -0.676800 0.000000
+vn -0.327100 -0.263100 0.907600
+vn -0.327100 -0.263100 -0.907600
+vn 0.000000 -1.000000 0.004800
+vn 0.000000 -0.657400 -0.753500
+vn 0.898300 -0.248500 -0.362400
+vn 0.863600 -0.504100 0.003400
+vn -0.898300 -0.248500 -0.362400
+vn -0.863600 -0.504100 0.003400
+vn 0.000000 0.000000 -1.000000
+vn 0.925200 0.000000 -0.379500
+vn -0.925200 0.000000 -0.379500
+vn 0.898300 0.248500 -0.362400
+vn 0.661600 -0.421500 0.620200
+vn -0.898300 0.248500 -0.362400
+vn 0.898300 0.248500 0.362300
+vn -0.898300 0.248500 0.362300
+vn 0.000000 -0.000900 1.000000
+vn 0.924700 -0.000600 0.380700
+vn -0.924700 -0.000600 0.380700
+vn 0.000000 -0.650300 0.759600
+vn 0.895600 -0.254600 0.364700
+vn -0.895600 -0.254600 0.364700
+vn 0.729700 -0.683700 0.000000
+vn 0.729700 0.683800 0.000000
+vn -0.729700 0.683800 0.000000
+vn -0.898300 -0.248400 0.362300
+vn -0.863600 -0.504100 -0.003500
+vn 0.898300 -0.248400 0.362300
+vn 0.863600 -0.504100 -0.003500
+vn -0.661500 -0.421500 0.620200
+vn 0.924600 -0.000600 -0.380700
+vn -0.661500 0.421500 0.620200
+vn 0.661500 0.421500 0.620200
+s 1
+f 1/1/1 2/2/2 3/3/3 4/4/4
+f 4/4/4 3/3/3 5/5/5 6/6/6
+f 6/6/6 5/5/5 7/7/7 8/8/8
+f 8/8/8 7/7/7 9/9/9 10/10/10
+f 1/1/1 10/11/10 9/12/9 2/2/2
+f 2/2/2 11/13/11 12/14/12 3/3/3
+f 3/3/3 12/14/12 13/15/13 5/5/5
+f 5/5/5 13/15/13 14/16/14 7/7/7
+f 7/7/7 14/16/14 15/17/15 9/9/9
+f 9/12/9 15/18/15 11/13/11 2/2/2
+f 11/13/11 16/19/16 17/20/17 12/14/12
+f 12/14/12 17/20/17 18/21/18 13/15/13
+f 13/15/13 18/21/18 19/22/19 14/16/14
+f 14/16/14 19/22/19 20/23/20 15/17/15
+f 15/18/15 20/24/20 16/19/16 11/13/11
+f 16/19/16 21/25/21 22/26/22 17/20/17
+f 17/20/17 22/26/22 23/27/23 18/21/18
+f 18/21/18 23/27/23 24/28/24 19/22/19
+f 19/22/19 24/28/24 25/29/25 20/23/20
+f 20/24/20 25/30/25 21/25/21 16/19/16
+f 21/25/21 26/31/26 27/32/27 22/26/22
+f 22/26/22 27/32/27 28/33/28 23/27/23
+f 23/27/23 28/33/28 29/34/29 24/28/24
+f 24/28/24 29/34/29 30/35/30 25/29/25
+f 25/30/25 30/36/30 26/31/26 21/25/21
+f 26/37/26 31/38/31 32/39/32 27/40/27
+f 27/40/27 32/39/32 33/41/33 28/42/28
+f 28/42/28 33/41/33 34/43/34 29/44/29
+f 29/44/29 34/43/34 35/45/35 30/46/30
+f 30/47/30 35/48/35 31/38/31 26/37/26
+f 31/38/31 36/49/36 37/50/37 32/39/32
+f 32/39/32 37/50/37 38/51/38 33/41/33
+f 33/41/33 38/51/38 39/52/39 34/43/34
+f 34/43/34 39/52/39 40/53/40 35/45/35
+f 35/48/35 40/54/40 36/49/36 31/38/31
+f 36/49/36 1/1/1 4/4/4 37/50/37
+f 37/50/37 4/4/4 6/6/6 38/51/38
+f 38/51/38 6/6/6 8/8/8 39/52/39
+f 39/52/39 8/8/8 10/10/10 40/53/40
+f 1/1/1 36/49/36 40/54/40 10/11/10
+f 41/55/41 42/56/42 43/57/43 44/58/44
+f 44/59/44 43/60/43 45/61/45 46/62/46
+f 46/62/46 45/61/45 47/63/47 48/64/48
+f 48/64/48 47/63/47 49/65/49 50/66/50
+f 41/55/41 50/66/50 49/65/49 42/56/42
+f 42/56/42 51/67/51 52/68/52 43/57/43
+f 43/60/43 52/69/52 53/70/53 45/61/45
+f 45/61/45 53/70/53 54/71/54 47/63/47
+f 47/63/47 54/71/54 55/72/55 49/65/49
+f 49/65/49 55/72/55 51/67/51 42/56/42
+f 51/67/51 56/73/56 57/74/57 52/68/52
+f 52/69/52 57/75/57 58/76/58 53/70/53
+f 53/70/53 58/76/58 59/77/59 54/71/54
+f 54/71/54 59/77/59 60/78/60 55/72/55
+f 55/72/55 60/78/60 56/73/56 51/67/51
+f 56/73/56 61/79/21 62/80/61 57/74/57
+f 57/75/57 62/81/61 63/82/62 58/76/58
+f 58/76/58 63/82/62 64/83/63 59/77/59
+f 59/77/59 64/83/63 65/84/64 60/78/60
+f 60/78/60 65/84/64 61/79/21 56/73/56
+f 61/85/21 66/86/65 67/87/66 62/88/61
+f 62/35/61 67/89/66 68/90/67 63/91/62
+f 63/91/62 68/90/67 69/92/68 64/93/63
+f 64/93/63 69/92/68 70/94/69 65/95/64
+f 65/95/64 70/94/69 66/86/65 61/85/21
+f 66/86/65 71/96/70 72/97/71 67/87/66
+f 67/89/66 72/98/71 73/99/72 68/90/67
+f 68/90/67 73/99/72 74/100/73 69/92/68
+f 69/92/68 74/100/73 75/101/74 70/94/69
+f 70/94/69 75/101/74 71/96/70 66/86/65
+f 71/96/70 76/102/75 77/103/76 72/97/71
+f 72/98/71 77/104/76 78/105/77 73/99/72
+f 73/99/72 78/105/77 79/106/78 74/100/73
+f 74/100/73 79/106/78 80/107/79 75/101/74
+f 75/101/74 80/107/79 76/102/75 71/96/70
+f 76/102/75 41/55/41 44/58/44 77/103/76
+f 77/104/76 44/59/44 46/62/46 78/105/77
+f 78/105/77 46/62/46 48/64/48 79/106/78
+f 79/106/78 48/64/48 50/66/50 80/107/79
+f 41/55/41 76/102/75 80/107/79 50/66/50
+f 81/108/80 82/109/81 83/110/82 84/111/83
+f 84/9/83 83/17/82 85/112/78 86/113/48
+f 86/113/48 85/112/78 87/114/77 88/115/46
+f 88/115/46 87/114/77 89/116/84 90/117/85
+f 81/108/80 90/117/85 89/116/84 82/109/81
+f 82/109/81 91/118/86 92/119/87 83/110/82
+f 83/17/82 92/23/87 93/120/73 85/112/78
+f 85/112/78 93/120/73 94/121/72 87/114/77
+f 87/114/77 94/121/72 95/122/88 89/116/84
+f 89/116/84 95/122/88 91/118/86 82/109/81
+f 91/118/86 96/123/65 97/124/69 92/119/87
+f 92/23/87 97/29/69 98/125/68 93/120/73
+f 93/120/73 98/125/68 99/126/67 94/121/72
+f 94/121/72 99/126/67 100/127/66 95/122/88
+f 95/122/88 100/127/66 96/123/65 91/118/86
+f 96/123/65 101/85/21 102/88/64 97/124/69
+f 97/29/69 102/35/64 103/91/63 98/125/68
+f 98/125/68 103/91/63 104/93/62 99/126/67
+f 99/126/67 104/93/62 105/95/61 100/127/66
+f 100/127/66 105/95/61 101/85/21 96/123/65
+f 101/128/21 106/129/56 107/130/60 102/131/64
+f 102/46/64 107/45/60 108/132/59 103/133/63
+f 103/133/63 108/132/59 109/134/58 104/135/62
+f 104/135/62 109/134/58 110/136/57 105/137/61
+f 105/137/61 110/136/57 106/129/56 101/128/21
+f 106/129/56 111/138/89 112/139/90 107/130/60
+f 107/45/60 112/53/90 113/140/54 108/132/59
+f 108/132/59 113/140/54 114/141/53 109/134/58
+f 109/134/58 114/141/53 115/142/91 110/136/57
+f 110/136/57 115/142/91 111/138/89 106/129/56
+f 111/138/89 116/143/92 117/144/93 112/139/90
+f 112/53/90 117/10/93 118/145/47 113/140/54
+f 113/140/54 118/145/47 119/146/45 114/141/53
+f 114/141/53 119/146/45 120/147/94 115/142/91
+f 115/142/91 120/147/94 116/143/92 111/138/89
+f 116/143/92 81/108/80 84/111/83 117/144/93
+f 117/10/93 84/9/83 86/113/48 118/145/47
+f 118/145/47 86/113/48 88/115/46 119/146/45
+f 119/146/45 88/115/46 90/117/85 120/147/94
+f 81/108/80 116/143/92 120/147/94 90/117/85
+f 121/148/95 122/149/96 123/150/97 124/151/98
+f 124/151/98 123/150/97 125/152/39 126/153/8
+f 126/153/8 125/152/39 127/154/38 128/155/6
+f 128/155/6 127/154/38 129/156/99 130/157/100
+f 121/148/95 130/103/100 129/58/99 122/149/96
+f 122/149/96 131/158/101 132/159/102 123/150/97
+f 123/150/97 132/159/102 133/160/34 125/152/39
+f 125/152/39 133/160/34 134/161/33 127/154/38
+f 127/154/38 134/161/33 135/162/103 129/156/99
+f 129/58/99 135/57/103 131/158/101 122/149/96
+f 131/158/101 136/163/26 137/164/104 132/159/102
+f 132/159/102 137/164/104 138/165/105 133/160/34
+f 133/160/34 138/165/105 139/166/28 134/161/33
+f 134/161/33 139/166/28 140/167/106 135/162/103
+f 135/57/103 140/68/106 136/163/26 131/158/101
+f 136/163/26 141/168/21 142/169/25 137/164/104
+f 137/164/104 142/169/25 143/170/24 138/165/105
+f 138/165/105 143/170/24 144/171/23 139/166/28
+f 139/166/28 144/171/23 145/172/22 140/167/106
+f 140/68/106 145/74/22 141/168/21 136/163/26
+f 141/168/21 146/173/16 147/174/107 142/169/25
+f 142/169/25 147/174/107 148/175/19 143/170/24
+f 143/170/24 148/175/19 149/176/18 144/171/23
+f 144/171/23 149/176/18 150/177/108 145/172/22
+f 145/74/22 150/80/108 146/173/16 141/168/21
+f 146/178/16 151/179/109 152/180/110 147/181/107
+f 147/181/107 152/180/110 153/182/14 148/183/19
+f 148/183/19 153/182/14 154/184/13 149/185/18
+f 149/185/18 154/184/13 155/186/111 150/187/108
+f 150/88/108 155/87/111 151/179/109 146/178/16
+f 151/179/109 156/188/112 157/189/113 152/180/110
+f 152/180/110 157/189/113 158/190/7 153/182/14
+f 153/182/14 158/190/7 159/191/5 154/184/13
+f 154/184/13 159/191/5 160/192/114 155/186/111
+f 155/87/111 160/97/114 156/188/112 151/179/109
+f 156/188/112 121/148/95 124/151/98 157/189/113
+f 157/189/113 124/151/98 126/153/8 158/190/7
+f 158/190/7 126/153/8 128/155/6 159/191/5
+f 159/191/5 128/155/6 130/157/100 160/192/114
+f 121/148/95 156/188/112 160/97/114 130/103/100
+f 161/193/80 162/194/115 163/60/82 164/59/83
+f 164/195/83 163/196/82 165/197/78 166/198/48
+f 166/198/48 165/197/78 167/199/77 168/200/46
+f 168/200/46 167/199/77 169/201/84 170/202/85
+f 161/193/80 170/202/85 169/201/84 162/194/115
+f 162/194/115 171/203/86 172/69/87 163/60/82
+f 163/196/82 172/204/87 173/205/73 165/197/78
+f 165/197/78 173/205/73 174/206/72 167/199/77
+f 167/199/77 174/206/72 175/207/88 169/201/84
+f 169/201/84 175/207/88 171/203/86 162/194/115
+f 171/203/86 176/208/116 177/75/69 172/69/87
+f 172/204/87 177/209/69 178/210/68 173/205/73
+f 173/205/73 178/210/68 179/211/67 174/206/72
+f 174/206/72 179/211/67 180/212/66 175/207/88
+f 175/207/88 180/212/66 176/208/116 171/203/86
+f 176/208/116 181/213/21 182/81/64 177/75/69
+f 177/209/69 182/214/64 183/215/63 178/210/68
+f 178/210/68 183/215/63 184/216/62 179/211/67
+f 179/211/67 184/216/62 185/217/61 180/212/66
+f 180/212/66 185/217/61 181/213/21 176/208/116
+f 181/34/21 186/218/117 187/89/60 182/35/64
+f 182/36/64 187/219/60 188/220/59 183/31/63
+f 183/31/63 188/220/59 189/221/58 184/32/62
+f 184/32/62 189/221/58 190/222/57 185/33/61
+f 185/33/61 190/222/57 186/218/117 181/34/21
+f 186/218/117 191/223/89 192/98/90 187/89/60
+f 187/219/60 192/224/90 193/225/54 188/220/59
+f 188/220/59 193/225/54 194/226/53 189/221/58
+f 189/221/58 194/226/53 195/227/91 190/222/57
+f 190/222/57 195/227/91 191/223/89 186/218/117
+f 191/223/89 196/228/92 197/104/93 192/98/90
+f 192/224/90 197/229/93 198/230/47 193/225/54
+f 193/225/54 198/230/47 199/231/45 194/226/53
+f 194/226/53 199/231/45 200/232/94 195/227/91
+f 195/227/91 200/232/94 196/228/92 191/223/89
+f 196/228/92 161/193/80 164/59/83 197/104/93
+f 197/229/93 164/195/83 166/198/48 198/230/47
+f 198/230/47 166/198/48 168/200/46 199/231/45
+f 199/231/45 168/200/46 170/202/85 200/232/94
+f 161/193/80 196/228/92 200/232/94 170/202/85
+f 201/233/1 202/234/2 203/235/118 204/236/119
+f 204/111/119 203/110/118 205/237/5 206/238/6
+f 206/238/6 205/237/5 207/239/7 208/240/8
+f 208/240/8 207/239/7 209/241/120 210/242/121
+f 201/233/1 210/242/121 209/241/120 202/234/2
+f 202/234/2 211/243/11 212/244/12 203/235/118
+f 203/110/118 212/119/12 213/245/13 205/237/5
+f 205/237/5 213/245/13 214/246/14 207/239/7
+f 207/239/7 214/246/14 215/247/15 209/241/120
+f 209/241/120 215/247/15 211/243/11 202/234/2
+f 211/243/11 216/248/16 217/249/108 212/244/12
+f 212/119/12 217/124/108 218/250/18 213/245/13
+f 213/245/13 218/250/18 219/251/19 214/246/14
+f 214/246/14 219/251/19 220/252/107 215/247/15
+f 215/247/15 220/252/107 216/248/16 211/243/11
+f 216/248/16 221/185/21 222/187/22 217/249/108
+f 217/124/108 222/88/22 223/178/23 218/250/18
+f 218/250/18 223/178/23 224/181/24 219/251/19
+f 219/251/19 224/181/24 225/183/25 220/252/107
+f 220/252/107 225/183/25 221/185/21 216/248/16
+f 221/253/21 226/254/26 227/255/106 222/256/22
+f 222/131/22 227/130/106 228/257/122 223/258/23
+f 223/258/23 228/257/122 229/259/29 224/260/24
+f 224/260/24 229/259/29 230/261/104 225/262/25
+f 225/262/25 230/261/104 226/254/26 221/253/21
+f 226/254/26 231/263/31 232/264/32 227/255/106
+f 227/130/106 232/139/32 233/265/33 228/257/122
+f 228/257/122 233/265/33 234/266/34 229/259/29
+f 229/259/29 234/266/34 235/267/123 230/261/104
+f 230/261/104 235/267/123 231/263/31 226/254/26
+f 231/263/31 236/268/36 237/269/37 232/264/32
+f 232/139/32 237/144/37 238/270/124 233/265/33
+f 233/265/33 238/270/124 239/271/125 234/266/34
+f 234/266/34 239/271/125 240/272/40 235/267/123
+f 235/267/123 240/272/40 236/268/36 231/263/31
+f 236/268/36 201/233/1 204/236/119 237/269/37
+f 237/144/37 204/111/119 206/238/6 238/270/124
+f 238/270/124 206/238/6 208/240/8 239/271/125
+f 239/271/125 208/240/8 210/242/121 240/272/40
+f 201/233/1 236/268/36 240/272/40 210/242/121
diff --git a/mods/basic_materials/plastics.lua b/mods/basic_materials/plastics.lua
new file mode 100644
index 00000000..e29af53e
--- /dev/null
+++ b/mods/basic_materials/plastics.lua
@@ -0,0 +1,56 @@
+-- Translation support
+local S = minetest.get_translator("basic_materials")
+
+-- items
+
+minetest.register_craftitem("basic_materials:plastic_sheet", {
+ description = S("Plastic sheet"),
+ inventory_image = "basic_materials_plastic_sheet.png",
+})
+
+minetest.register_craftitem("basic_materials:plastic_strip", {
+ description = S("Plastic strips"),
+ groups = { strip = 1 },
+ inventory_image = "basic_materials_plastic_strip.png",
+})
+
+minetest.register_craftitem("basic_materials:empty_spool", {
+ description = S("Empty wire spool"),
+ inventory_image = "basic_materials_empty_spool.png"
+})
+
+-- crafts
+
+minetest.register_craft({
+ type = "cooking",
+ output = "basic_materials:plastic_sheet",
+ recipe = "basic_materials:paraffin",
+})
+
+minetest.register_craft({
+ type = "fuel",
+ recipe = "basic_materials:plastic_sheet",
+ burntime = 30,
+})
+
+minetest.register_craft( {
+ output = "basic_materials:plastic_strip 9",
+ recipe = {
+ { "basic_materials:plastic_sheet", "basic_materials:plastic_sheet", "basic_materials:plastic_sheet" }
+ },
+})
+
+minetest.register_craft( {
+ output = "basic_materials:empty_spool 3",
+ recipe = {
+ { "basic_materials:plastic_sheet", "basic_materials:plastic_sheet", "basic_materials:plastic_sheet" },
+ { "", "basic_materials:plastic_sheet", "" },
+ { "basic_materials:plastic_sheet", "basic_materials:plastic_sheet", "basic_materials:plastic_sheet" }
+ },
+})
+
+-- aliases
+
+minetest.register_alias("homedecor:plastic_sheeting", "basic_materials:plastic_sheet")
+minetest.register_alias("homedecor:plastic_strips", "basic_materials:plastic_strip")
+minetest.register_alias("homedecor:empty_spool", "basic_materials:empty_spool")
diff --git a/mods/basic_materials/textures/basic_materials_brass_block.png b/mods/basic_materials/textures/basic_materials_brass_block.png
new file mode 100644
index 00000000..c9378000
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_brass_block.png differ
diff --git a/mods/basic_materials/textures/basic_materials_brass_ingot.png b/mods/basic_materials/textures/basic_materials_brass_ingot.png
new file mode 100644
index 00000000..0bd030a3
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_brass_ingot.png differ
diff --git a/mods/basic_materials/textures/basic_materials_cement_block.png b/mods/basic_materials/textures/basic_materials_cement_block.png
new file mode 100644
index 00000000..6d30f477
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_cement_block.png differ
diff --git a/mods/basic_materials/textures/basic_materials_chain_brass.png b/mods/basic_materials/textures/basic_materials_chain_brass.png
new file mode 100644
index 00000000..e2fb20db
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_chain_brass.png differ
diff --git a/mods/basic_materials/textures/basic_materials_chain_brass_inv.png b/mods/basic_materials/textures/basic_materials_chain_brass_inv.png
new file mode 100644
index 00000000..8c2d554d
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_chain_brass_inv.png differ
diff --git a/mods/basic_materials/textures/basic_materials_chain_steel.png b/mods/basic_materials/textures/basic_materials_chain_steel.png
new file mode 100644
index 00000000..29af8dbd
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_chain_steel.png differ
diff --git a/mods/basic_materials/textures/basic_materials_chain_steel_inv.png b/mods/basic_materials/textures/basic_materials_chain_steel_inv.png
new file mode 100644
index 00000000..c552f7b4
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_chain_steel_inv.png differ
diff --git a/mods/basic_materials/textures/basic_materials_chainlink_brass.png b/mods/basic_materials/textures/basic_materials_chainlink_brass.png
new file mode 100644
index 00000000..9a1ad87e
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_chainlink_brass.png differ
diff --git a/mods/basic_materials/textures/basic_materials_chainlink_steel.png b/mods/basic_materials/textures/basic_materials_chainlink_steel.png
new file mode 100644
index 00000000..d7132c32
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_chainlink_steel.png differ
diff --git a/mods/basic_materials/textures/basic_materials_concrete_block.png b/mods/basic_materials/textures/basic_materials_concrete_block.png
new file mode 100644
index 00000000..5dd0d660
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_concrete_block.png differ
diff --git a/mods/basic_materials/textures/basic_materials_copper_strip.png b/mods/basic_materials/textures/basic_materials_copper_strip.png
new file mode 100644
index 00000000..22e572a1
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_copper_strip.png differ
diff --git a/mods/basic_materials/textures/basic_materials_copper_wire.png b/mods/basic_materials/textures/basic_materials_copper_wire.png
new file mode 100644
index 00000000..9df9f366
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_copper_wire.png differ
diff --git a/mods/basic_materials/textures/basic_materials_empty_spool.png b/mods/basic_materials/textures/basic_materials_empty_spool.png
new file mode 100644
index 00000000..017a94fd
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_empty_spool.png differ
diff --git a/mods/basic_materials/textures/basic_materials_energy_crystal.png b/mods/basic_materials/textures/basic_materials_energy_crystal.png
new file mode 100644
index 00000000..f1c28e80
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_energy_crystal.png differ
diff --git a/mods/basic_materials/textures/basic_materials_gear_steel.png b/mods/basic_materials/textures/basic_materials_gear_steel.png
new file mode 100644
index 00000000..584f9a51
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_gear_steel.png differ
diff --git a/mods/basic_materials/textures/basic_materials_gold_wire.png b/mods/basic_materials/textures/basic_materials_gold_wire.png
new file mode 100644
index 00000000..781de7b1
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_gold_wire.png differ
diff --git a/mods/basic_materials/textures/basic_materials_heating_element.png b/mods/basic_materials/textures/basic_materials_heating_element.png
new file mode 100644
index 00000000..42e00b7a
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_heating_element.png differ
diff --git a/mods/basic_materials/textures/basic_materials_ic.png b/mods/basic_materials/textures/basic_materials_ic.png
new file mode 100644
index 00000000..4c888945
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_ic.png differ
diff --git a/mods/basic_materials/textures/basic_materials_motor.png b/mods/basic_materials/textures/basic_materials_motor.png
new file mode 100644
index 00000000..f19ec0a0
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_motor.png differ
diff --git a/mods/basic_materials/textures/basic_materials_oil_extract.png b/mods/basic_materials/textures/basic_materials_oil_extract.png
new file mode 100644
index 00000000..e34623d0
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_oil_extract.png differ
diff --git a/mods/basic_materials/textures/basic_materials_padlock.png b/mods/basic_materials/textures/basic_materials_padlock.png
new file mode 100644
index 00000000..b05b7ef7
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_padlock.png differ
diff --git a/mods/basic_materials/textures/basic_materials_paraffin.png b/mods/basic_materials/textures/basic_materials_paraffin.png
new file mode 100644
index 00000000..77d2bbd1
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_paraffin.png differ
diff --git a/mods/basic_materials/textures/basic_materials_plastic_sheet.png b/mods/basic_materials/textures/basic_materials_plastic_sheet.png
new file mode 100644
index 00000000..034dcc2f
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_plastic_sheet.png differ
diff --git a/mods/basic_materials/textures/basic_materials_plastic_strip.png b/mods/basic_materials/textures/basic_materials_plastic_strip.png
new file mode 100644
index 00000000..1318dfc0
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_plastic_strip.png differ
diff --git a/mods/basic_materials/textures/basic_materials_silicon.png b/mods/basic_materials/textures/basic_materials_silicon.png
new file mode 100644
index 00000000..847b366c
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_silicon.png differ
diff --git a/mods/basic_materials/textures/basic_materials_silver_wire.png b/mods/basic_materials/textures/basic_materials_silver_wire.png
new file mode 100644
index 00000000..a38a45ea
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_silver_wire.png differ
diff --git a/mods/basic_materials/textures/basic_materials_steel_bar.png b/mods/basic_materials/textures/basic_materials_steel_bar.png
new file mode 100644
index 00000000..0673b6ee
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_steel_bar.png differ
diff --git a/mods/basic_materials/textures/basic_materials_steel_strip.png b/mods/basic_materials/textures/basic_materials_steel_strip.png
new file mode 100644
index 00000000..6384dc83
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_steel_strip.png differ
diff --git a/mods/basic_materials/textures/basic_materials_steel_wire.png b/mods/basic_materials/textures/basic_materials_steel_wire.png
new file mode 100644
index 00000000..0c96c8f3
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_steel_wire.png differ
diff --git a/mods/basic_materials/textures/basic_materials_terracotta_base.png b/mods/basic_materials/textures/basic_materials_terracotta_base.png
new file mode 100644
index 00000000..9f04aad5
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_terracotta_base.png differ
diff --git a/mods/basic_materials/textures/basic_materials_wet_cement.png b/mods/basic_materials/textures/basic_materials_wet_cement.png
new file mode 100644
index 00000000..6a7fbf1b
Binary files /dev/null and b/mods/basic_materials/textures/basic_materials_wet_cement.png differ
diff --git a/version.php b/version.php
index bbda97d0..bbc1bdff 100644
--- a/version.php
+++ b/version.php
@@ -1,5 +1,5 @@