change into modpack
clear_recipe added for moreblocks ( warning: crash on 0.4.16 )master
BIN
addon mods.zip
|
@ -0,0 +1 @@
|
|||
default
|
|
@ -0,0 +1,372 @@
|
|||
-- idea: explore button + require lot of essence to 'discover' certain items: sapling, iron, copper, tin, gold, mese, diamond, mithril
|
||||
|
||||
-- ALCHEMY by rnd (2017)
|
||||
|
||||
-- combine ingredients into new outputs
|
||||
|
||||
|
||||
alchemy = {};
|
||||
|
||||
dofile(minetest.get_modpath("alchemy").."/items.lua")
|
||||
|
||||
|
||||
local get_change = function( money ) -- returns change in greedy way
|
||||
local values = alchemy.essence;
|
||||
local p = {};
|
||||
for i = #values,1,-1 do
|
||||
p[i] = math.floor(money/values[i][2]);
|
||||
money = money - p[i]*values[i][2]
|
||||
end
|
||||
|
||||
return p
|
||||
end
|
||||
|
||||
|
||||
local lab_make = function(pos) --try to 'make' more material in 1st slot of 'in' using essences in 'out'
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
local stack = inv:get_stack("in", 1);
|
||||
local item = stack:get_name();
|
||||
if item == "" then return end
|
||||
local essence = 0;
|
||||
for i = 1,4 do
|
||||
local stack = inv:get_stack("out", i);
|
||||
local count = stack:get_count()
|
||||
local eitem = stack:get_name();
|
||||
--how much essence
|
||||
essence = essence + (alchemy.essence_values[eitem] or 0)*count;
|
||||
end
|
||||
|
||||
local cost = alchemy.items[item]; if not cost then return end
|
||||
--adjust cost depending on upgrades
|
||||
local upgrade = 0
|
||||
if inv:get_stack("upgrade", 1):get_name() == "alchemy:essence_upgrade" then
|
||||
upgrade = inv:get_stack("upgrade", 1):get_count();
|
||||
end
|
||||
cost = cost*math.max(1,(0.2 + 4.8/(1 + 0.05*upgrade))) -- 1 for upgrade 100, 5 for upgrade 0
|
||||
|
||||
local out_count = math.floor(essence/cost);
|
||||
if out_count < 1 then return end
|
||||
|
||||
local remainder = essence - cost*out_count;
|
||||
|
||||
inv:add_item("in",ItemStack(item.. " " .. out_count))
|
||||
local p = get_change( remainder );
|
||||
|
||||
-- set inventory to containt remainder of essence
|
||||
local values = alchemy.essence;
|
||||
for i = 1, #values do
|
||||
if p[i]>0 then
|
||||
inv:set_stack("out",i,ItemStack(values[i][1].. " " .. p[i]))
|
||||
else
|
||||
inv:set_stack("out",i,ItemStack(""))
|
||||
end
|
||||
end
|
||||
|
||||
for i = #values+1,4 do
|
||||
inv:set_stack("out",i,ItemStack(""))
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
|
||||
local lab_break = function(pos) -- break down materials in 'in'
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
for j = 1,4 do
|
||||
local stack = inv:get_stack("in", j);
|
||||
local item = stack:get_name();
|
||||
if item~="" then
|
||||
local essence = (alchemy.items[item] or 1)*stack:get_count();
|
||||
local p = get_change(essence ); -- essence of item in spot j
|
||||
-- require 1 energy to break 1 gold (250 essence)
|
||||
local fuel_cost = math.floor(essence/250); if fuel_cost<1 then fuel_cost = 1 end
|
||||
local fuel_stack = ItemStack("alchemy:essence_energy " .. fuel_cost);
|
||||
if not inv:contains_item("fuel", fuel_stack) then
|
||||
local text = "not enough energy, need " .. fuel_cost .. " cells."
|
||||
meta:set_string("infotext", text)
|
||||
minetest.show_formspec(
|
||||
meta:get_string("owner"),"alchemy_help","size[5.5,5]".."textarea[0.,0;6.1,6;alchemy_help;ALCHEMY;" .. minetest.formspec_escape(text) .. "]"
|
||||
)
|
||||
return
|
||||
else
|
||||
inv:remove_item("fuel", fuel_stack)
|
||||
meta:set_string("infotext","")
|
||||
end
|
||||
|
||||
inv:set_stack("in",j,ItemStack(""))
|
||||
|
||||
local values = alchemy.essence;
|
||||
for i = 1, #values do
|
||||
if p[i]>0 then
|
||||
inv:add_item("out",ItemStack(values[i][1].. " " .. p[i]))
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
minetest.register_abm({ -- very slowly create energy
|
||||
nodenames = {"alchemy:lab"},
|
||||
neighbors = {""},
|
||||
interval = 30,
|
||||
chance = 1,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
local inv = meta:get_inventory();
|
||||
local upgrade = 0;
|
||||
if inv:get_stack("upgrade", 1):get_name() == "alchemy:essence_upgrade" then
|
||||
upgrade = inv:get_stack("upgrade", 1):get_count();
|
||||
end
|
||||
|
||||
local count = 1 + upgrade
|
||||
local stack = ItemStack("alchemy:essence_energy " .. count)
|
||||
inv:add_item("fuel", stack)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
local lab_update_meta = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
|
||||
|
||||
local form =
|
||||
"size[8,6.75]"..
|
||||
"button[0,2;2.,0.75;ibreak;BREAK]"..
|
||||
"button[6,2;2.,0.75;fuel;FUEL]"..
|
||||
"button[3,2;2.,0.75;make;MAKE]"..
|
||||
"label[0,-0.4.;MATERIAL]"..
|
||||
"list[" .. list_name .. ";in;0,0;2,2;]"..
|
||||
"label[3,-0.4.;ESSENCE]"..
|
||||
"list[" .. list_name .. ";out;3,0;2,2;]"..
|
||||
"image[2,0.;1,1;gui_furnace_arrow_bg.png^[transformR270]"..
|
||||
"image[2,1.;1,1;gui_furnace_arrow_bg.png^[transformR90]"..
|
||||
"label[6,-0.4.;UPGRADE]"..
|
||||
"list[" .. list_name .. ";upgrade;6,0;1,1;]"..
|
||||
"button[6.,1;2,1;upgrade;".. minetest.colorize("red","HELP") .. "]"..
|
||||
"list[current_player;main;0,3;8,4;]"..
|
||||
"listring[context;in]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;out]"..
|
||||
"listring[current_player;main]"
|
||||
meta:set_string("formspec", form);
|
||||
end
|
||||
|
||||
|
||||
minetest.register_node("alchemy:lab", {
|
||||
description = "Alchemy laboratory",
|
||||
tiles = {"default_steel_block.png","default_steel_block.png","alchemy_lab.png","alchemy_lab.png","alchemy_lab.png","alchemy_lab.png"},
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_string("infotext", "Alchemy: To operate it insert materials or essences.")
|
||||
meta:set_string("owner", placer:get_player_name());
|
||||
local inv = meta:get_inventory();
|
||||
inv:set_size("in",4);
|
||||
inv:set_size("out",4); -- dusts here
|
||||
inv:set_size("upgrade",1);
|
||||
inv:set_size("fuel",32);
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler
|
||||
lab_update_meta(pos);
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
return 0;
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = function (pos, node,ttl)
|
||||
if type(ttl)~="number" then ttl = 1 end
|
||||
if ttl<0 then return end -- machines_TTL prevents infinite recursion
|
||||
local meta = minetest.get_meta(pos);
|
||||
if meta:get_int("mode") == 2 then
|
||||
lab_make(pos)
|
||||
else
|
||||
lab_break(pos)
|
||||
end
|
||||
end
|
||||
}
|
||||
},
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
if minetest.is_protected(pos, sender:get_player_name()) then return end
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
if fields.make then
|
||||
lab_make(pos);
|
||||
meta:set_int("mode",2)
|
||||
return
|
||||
end
|
||||
|
||||
if fields.ibreak then
|
||||
lab_break(pos);
|
||||
meta:set_int("mode",1)
|
||||
return
|
||||
end
|
||||
|
||||
if fields.fuel then
|
||||
local meta = minetest.get_meta(pos);
|
||||
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
|
||||
|
||||
local form =
|
||||
"size[8,8]"..
|
||||
"label[0,-0.4;INSERT ENERGY ESSENCE AS FUEL]"..
|
||||
"list[" .. list_name .. ";fuel;0,0;8,4;]"..
|
||||
"list[current_player;main;0,4.25;8,4;]"..
|
||||
"listring[context;fuel]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;fuel]"
|
||||
|
||||
minetest.show_formspec(
|
||||
sender:get_player_name(),"alchemy_fuel",form
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
if fields.upgrade then
|
||||
local text = minetest.colorize("yellow","1.BREAK") .."\nPlace items in left window (MATERIALS) and use 'break' to transmute them into essences.\n\n"..
|
||||
minetest.colorize("yellow","2.MADE") .."\nPlace essences in right window (ESSENCES), place item to be created in left window (1st position) and use 'make'.\n\n"..
|
||||
minetest.colorize("red","3.DISCOVER") .."\nif you insert enough essence you can discover new materials.\n\n"..
|
||||
"Make process will be more effective if you place upgrade essences in upgrade window. cost factor is 0.2 + 4.8/(1 + 0.05*upgrade)\n\n" ..
|
||||
"To break materials you need 1 energy essence for every 250 essence. Energy essence is produced at rate (1+upgrade) by alchemy lab every 1/2 minute\n\n"..
|
||||
"There are 4 kinds of essences: low (1), medium (50), high(2500) and upgrade(125000)."
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local level = meta:get_int("level");-- discovery level
|
||||
local discovery = alchemy.discoveries[level] or alchemy.discoveries[0]
|
||||
|
||||
minetest.show_formspec(
|
||||
sender:get_player_name(),"alchemy_help:" .. minetest.pos_to_string(pos), "size[5.5,5]".."textarea[0.,0;6.1,5.5;alchemy_help;ALCHEMY HELP;" .. minetest.formspec_escape(text) .. "]"..
|
||||
"button_exit[0,4.75;5.5,0.75;discover;" .. minetest.colorize("red","DISCOVER " .. level .. " : ".. discovery.item .. " (cost " .. discovery.cost .. ")") .."]"
|
||||
)
|
||||
return
|
||||
end
|
||||
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
|
||||
minetest.register_on_player_receive_fields(
|
||||
function(player, formname, fields)
|
||||
|
||||
local fname = "alchemy_help:";
|
||||
if string.sub(formname,1, string.len(fname)) ~= fname then return end
|
||||
|
||||
if fields.discover then
|
||||
local pos = minetest.string_to_pos(string.sub(formname, string.len(fname)+1));
|
||||
local meta = minetest.get_meta(pos);
|
||||
local level = meta:get_int("level") or 0;-- discovery level
|
||||
local discovery = alchemy.discoveries[level] or alchemy.discoveries[0];
|
||||
local cost = discovery.cost;
|
||||
|
||||
|
||||
local inv = meta:get_inventory();
|
||||
local item = discovery.item;
|
||||
|
||||
local essence = 0;
|
||||
for i = 1,4 do
|
||||
local stack = inv:get_stack("out", i);
|
||||
local count = stack:get_count()
|
||||
local eitem = stack:get_name();
|
||||
--how much essence
|
||||
essence = essence + (alchemy.essence_values[eitem] or 0)*count;
|
||||
end
|
||||
|
||||
if essence<cost then
|
||||
minetest.chat_send_player(player:get_player_name(),"#ALCHEMY: you need at least " .. cost .. " essence, you have only " .. essence)
|
||||
return
|
||||
end
|
||||
|
||||
local remainder = essence - cost;
|
||||
|
||||
inv:add_item("in",ItemStack(item))
|
||||
level = level+1;
|
||||
if alchemy.discoveries[level] then meta:set_int("level",level);minetest.chat_send_player(player:get_player_name(),"#ALCHEMY: successfuly discovered " .. item .. "!") end
|
||||
|
||||
local p = get_change( remainder );
|
||||
|
||||
-- set inventory to containt remainder of essence
|
||||
local values = alchemy.essence;
|
||||
for i = 1, #values do
|
||||
if p[i]>0 then
|
||||
inv:set_stack("out",i,ItemStack(values[i][1].. " " .. p[i]))
|
||||
else
|
||||
inv:set_stack("out",i,ItemStack(""))
|
||||
end
|
||||
end
|
||||
|
||||
for i = #values+1,4 do
|
||||
inv:set_stack("out",i,ItemStack(""))
|
||||
end
|
||||
|
||||
return;
|
||||
end
|
||||
end)
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "alchemy:lab",
|
||||
recipe = {
|
||||
{"default:steel_ingot","default:goldblock","default:steel_ingot"},
|
||||
{"default:steel_ingot","default:diamondblock","default:steel_ingot"},
|
||||
{"default:steel_ingot","default:steel_ingot","default:steel_ingot"},
|
||||
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
-- ESSENCES
|
||||
|
||||
minetest.register_craftitem("alchemy:essence_low", {
|
||||
description = "Low essence",
|
||||
inventory_image = "alchemy_essence_low.png",
|
||||
stack_max = 30000,
|
||||
})
|
||||
|
||||
minetest.register_craftitem("alchemy:essence_medium", {
|
||||
description = "Medium essence",
|
||||
inventory_image = "alchemy_essence_medium.png",
|
||||
stack_max = 30000,
|
||||
})
|
||||
|
||||
minetest.register_craftitem("alchemy:essence_high", {
|
||||
description = "High essence",
|
||||
inventory_image = "alchemy_essence_high.png",
|
||||
stack_max = 30000,
|
||||
})
|
||||
|
||||
minetest.register_craftitem("alchemy:essence_upgrade", {
|
||||
description = "Upgrade essence",
|
||||
inventory_image = "alchemy_essence_upgrade.png",
|
||||
stack_max = 30000,
|
||||
})
|
||||
|
||||
minetest.register_craftitem("alchemy:essence_energy", {
|
||||
description = "energy essence",
|
||||
inventory_image = "energy_essence.png",
|
||||
stack_max = 64000,
|
||||
})
|
|
@ -0,0 +1,54 @@
|
|||
-- define essences and their values
|
||||
alchemy.essence = {
|
||||
{"alchemy:essence_low",1},
|
||||
{"alchemy:essence_medium",50},
|
||||
{"alchemy:essence_high",2500},
|
||||
{"alchemy:essence_upgrade",125000}
|
||||
} -- values of low, medium, high, upgrade essences
|
||||
|
||||
|
||||
-- define item values for BREAK process
|
||||
alchemy.items = {
|
||||
["alchemy:essence_energy"]=0.05,
|
||||
["alchemy:essence_low"]=1,
|
||||
["alchemy:essence_medium"]=50,
|
||||
["alchemy:essence_high"]=2500,
|
||||
["alchemy:essence_upgrade"] = 125000,
|
||||
["default:cobble"] = 1,
|
||||
["default:diamond"] = 2500,
|
||||
["moreores:mithril_ingot"] = 5000,
|
||||
["default:mese_crystal"] = 1250,
|
||||
["default:gold_ingot"] = 125,
|
||||
["default:tree"] = 25,
|
||||
["default:steel_ingot"] = 25,
|
||||
["default:copper_ingot"] = 25,
|
||||
["default:tin_ingot"] = 25,
|
||||
["default:bronze_ingot"] = 50,
|
||||
["default:obsidian"] = 500,
|
||||
["default:dirt"] = 9,
|
||||
["default:gravel"] = 15,
|
||||
["default:sand"] = 12,
|
||||
["default:coal_lump"] = 12
|
||||
}
|
||||
|
||||
-- discovery levels: sequence of items player can discover and cost to do so
|
||||
alchemy.discoveries = {
|
||||
[0] = {item = "default:sapling", cost = 10},
|
||||
[1] = {item = "default:cobble", cost = 250},
|
||||
[2] = {item = "default:papyrus", cost = 300},
|
||||
[3] = {item = "default:sand 2", cost = 500},
|
||||
[4] = {item = "bucket:bucket_water", cost = 1000},
|
||||
[5] = {item = "default:coal_lump", cost = 1250},
|
||||
[6] = {item = "default:iron_lump", cost = 1500},
|
||||
[7] = {item = "default:pine_needles 6", cost = 2000},
|
||||
[8] = {item = "default:copper_lump", cost = 2500},
|
||||
[9] = {item = "default:tin_lump", cost = 3000},
|
||||
[10] = {item = "default:gold_lump", cost = 3500},
|
||||
[11] = {item = "default:mese_crystal", cost = 6250},
|
||||
[12] = {item = "default:diamond", cost = 10000},
|
||||
[13] = {item = "default:obsidian", cost = 15000},
|
||||
[14] = {item = "moreores:mithril_ingot", cost = 20000},
|
||||
}
|
||||
|
||||
alchemy.essence_values = {};
|
||||
for i=1,#alchemy.essence do alchemy.essence_values[alchemy.essence[i][1]] = alchemy.essence[i][2] end
|
After Width: | Height: | Size: 335 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 174 B |
After Width: | Height: | Size: 1.5 KiB |
After Width: | Height: | Size: 169 B |
|
@ -0,0 +1 @@
|
|||
Subproject commit e6e7defc9f5d9daa636f4ecfd24e9dd2ad8ba5fb
|
|
@ -0,0 +1,24 @@
|
|||
BASIC_MACHINES: lightweight automation mod for minetest
|
||||
minetest 0.4.14+
|
||||
(c) 2015-2016 rnd
|
||||
textures by rnd, new textures by SaKeL (2016) and Jozet (2017)
|
||||
|
||||
|
||||
MANUAL:
|
||||
1.WIKI PAGES: https://github.com/ac-minetest/basic_machines/wiki
|
||||
2.ingame help: right click mover/detector/keypad.. and click help button
|
||||
|
||||
---------------------------------------------------------------------
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
----------------------------------------------------------------------
|
|
@ -0,0 +1,366 @@
|
|||
-- modified and adapted from pipeworks mod by VanessaE
|
||||
-- by rnd
|
||||
-- disabled timers and on/off button, now autocrafter is only activated by signal
|
||||
|
||||
local autocrafterCache = {} -- caches some recipe data to avoid to call the slow function minetest.get_craft_result() every second
|
||||
|
||||
local craft_time = 1
|
||||
|
||||
local function count_index(invlist)
|
||||
local index = {}
|
||||
for _, stack in pairs(invlist) do
|
||||
if not stack:is_empty() then
|
||||
local stack_name = stack:get_name()
|
||||
index[stack_name] = (index[stack_name] or 0) + stack:get_count()
|
||||
end
|
||||
end
|
||||
return index
|
||||
end
|
||||
|
||||
local function get_item_info(stack)
|
||||
local name = stack:get_name()
|
||||
local def = minetest.registered_items[name]
|
||||
local description = def and def.description or "Unknown item"
|
||||
return description, name
|
||||
end
|
||||
|
||||
local function get_craft(pos, inventory, hash)
|
||||
local hash = hash or minetest.hash_node_position(pos)
|
||||
local craft = autocrafterCache[hash]
|
||||
if not craft then
|
||||
local recipe = inventory:get_list("recipe")
|
||||
local output, decremented_input = minetest.get_craft_result({method = "normal", width = 3, items = recipe})
|
||||
craft = {recipe = recipe, consumption=count_index(recipe), output = output, decremented_input = decremented_input}
|
||||
autocrafterCache[hash] = craft
|
||||
end
|
||||
return craft
|
||||
end
|
||||
|
||||
local function autocraft(inventory, craft)
|
||||
if not craft then return false end
|
||||
local output_item = craft.output.item
|
||||
|
||||
-- check if we have enough room in dst
|
||||
if not inventory:room_for_item("dst", output_item) then return false end
|
||||
local consumption = craft.consumption
|
||||
local inv_index = count_index(inventory:get_list("src"))
|
||||
-- check if we have enough material available
|
||||
for itemname, number in pairs(consumption) do
|
||||
if (not inv_index[itemname]) or inv_index[itemname] < number then return false end
|
||||
end
|
||||
-- consume material
|
||||
for itemname, number in pairs(consumption) do
|
||||
for i = 1, number do -- We have to do that since remove_item does not work if count > stack_max
|
||||
inventory:remove_item("src", ItemStack(itemname))
|
||||
end
|
||||
end
|
||||
|
||||
-- craft the result into the dst inventory and add any "replacements" as well
|
||||
inventory:add_item("dst", output_item)
|
||||
for i = 1, 9 do
|
||||
inventory:add_item("dst", craft.decremented_input.items[i])
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
-- returns false to stop the timer, true to continue running
|
||||
-- is started only from start_autocrafter(pos) after sanity checks and cached recipe
|
||||
local function run_autocrafter(pos, elapsed)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inventory = meta:get_inventory()
|
||||
local craft = get_craft(pos, inventory)
|
||||
local output_item = craft.output.item
|
||||
-- only use crafts that have an actual result
|
||||
if output_item:is_empty() then
|
||||
meta:set_string("infotext", "unconfigured Autocrafter: unknown recipe")
|
||||
return false
|
||||
end
|
||||
|
||||
for step = 1, math.floor(elapsed/craft_time) do
|
||||
local continue = autocraft(inventory, craft)
|
||||
if not continue then return false end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
local function start_crafter(pos) -- rnd we dont need timer anymore
|
||||
-- local meta = minetest.get_meta(pos)
|
||||
-- if meta:get_int("enabled") == 1 then
|
||||
-- local timer = minetest.get_node_timer(pos)
|
||||
-- if not timer:is_started() then
|
||||
-- timer:start(craft_time)
|
||||
-- end
|
||||
-- end
|
||||
end
|
||||
|
||||
local function after_inventory_change(pos)
|
||||
start_crafter(pos)
|
||||
end
|
||||
|
||||
-- note, that this function assumes allready being updated to virtual items
|
||||
-- and doesn't handle recipes with stacksizes > 1
|
||||
local function after_recipe_change(pos, inventory)
|
||||
local meta = minetest.get_meta(pos)
|
||||
-- if we emptied the grid, there's no point in keeping it running or cached
|
||||
if inventory:is_empty("recipe") then
|
||||
--minetest.get_node_timer(pos):stop()
|
||||
autocrafterCache[minetest.hash_node_position(pos)] = nil
|
||||
meta:set_string("infotext", "unconfigured Autocrafter")
|
||||
return
|
||||
end
|
||||
local recipe_changed = false
|
||||
local recipe = inventory:get_list("recipe")
|
||||
|
||||
local hash = minetest.hash_node_position(pos)
|
||||
local craft = autocrafterCache[hash]
|
||||
|
||||
if craft then
|
||||
-- check if it changed
|
||||
local cached_recipe = craft.recipe
|
||||
for i = 1, 9 do
|
||||
if recipe[i]:get_name() ~= cached_recipe[i]:get_name() then
|
||||
autocrafterCache[hash] = nil -- invalidate recipe
|
||||
craft = nil
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
craft = craft or get_craft(pos, inventory, hash)
|
||||
local output_item = craft.output.item
|
||||
local description, name = get_item_info(output_item)
|
||||
meta:set_string("infotext", string.format("'%s' Autocrafter (%s)", description, name))
|
||||
inventory:set_stack("output", 1, output_item)
|
||||
|
||||
after_inventory_change(pos)
|
||||
end
|
||||
|
||||
-- clean out unknown items and groups, which would be handled like unknown items in the crafting grid
|
||||
-- if minetest supports query by group one day, this might replace them
|
||||
-- with a canonical version instead
|
||||
local function normalize(item_list)
|
||||
for i = 1, #item_list do
|
||||
local name = item_list[i]
|
||||
if not minetest.registered_items[name] then
|
||||
item_list[i] = ""
|
||||
end
|
||||
end
|
||||
return item_list
|
||||
end
|
||||
|
||||
local function on_output_change(pos, inventory, stack)
|
||||
if not stack then
|
||||
inventory:set_list("output", {})
|
||||
inventory:set_list("recipe", {})
|
||||
else
|
||||
local input = minetest.get_craft_recipe(stack:get_name())
|
||||
if not input.items or input.type ~= "normal" then return end
|
||||
local items, width = normalize(input.items), input.width
|
||||
local item_idx, width_idx = 1, 1
|
||||
for i = 1, 9 do
|
||||
if width_idx <= width then
|
||||
inventory:set_stack("recipe", i, items[item_idx])
|
||||
item_idx = item_idx + 1
|
||||
else
|
||||
inventory:set_stack("recipe", i, ItemStack(""))
|
||||
end
|
||||
width_idx = (width_idx < 3) and (width_idx + 1) or 1
|
||||
end
|
||||
-- we'll set the output slot in after_recipe_change to the actual result of the new recipe
|
||||
end
|
||||
after_recipe_change(pos, inventory)
|
||||
end
|
||||
|
||||
-- returns false if we shouldn't bother attempting to start the timer again after this
|
||||
local function update_meta(meta, enabled)
|
||||
--local state = enabled and "on" or "off"
|
||||
--meta:set_int("enabled", enabled and 1 or 0)
|
||||
meta:set_string("formspec",
|
||||
"size[8,11]"..
|
||||
"list[context;recipe;0,0;3,3;]"..
|
||||
"image[3,1;1,1;gui_hb_bg.png^[colorize:#141318:255]"..
|
||||
"list[context;output;3,1;1,1;]"..
|
||||
--"image_button[3,2;1,1;pipeworks_button_" .. state .. ".png;" .. state .. ";;;false;pipeworks_button_interm.png]" .. -- rnd disable button
|
||||
"list[context;src;0,3.5;8,3;]"..
|
||||
"list[context;dst;4,0;4,3;]"..
|
||||
default.gui_bg..
|
||||
default.gui_bg_img..
|
||||
default.gui_slots..
|
||||
default.get_hotbar_bg(0,7)..
|
||||
"list[current_player;main;0,7;8,4;]"..
|
||||
"listring[context;dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;src]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring[context;recipe]"..
|
||||
"listring[current_player;main]"
|
||||
)
|
||||
|
||||
-- toggling the button doesn't quite call for running a recipe change check
|
||||
-- so instead we run a minimal version for infotext setting only
|
||||
-- this might be more written code, but actually executes less
|
||||
local output = meta:get_inventory():get_stack("output", 1)
|
||||
if output:is_empty() then -- doesn't matter if paused or not
|
||||
meta:set_string("infotext", "unconfigured Autocrafter: Place items for recipe top left. To operate place required items in bottom space (src inventory) and activated with keypad signal. Obtain crafted item from top right (dst inventory).")
|
||||
return false
|
||||
end
|
||||
|
||||
local description, name = get_item_info(output)
|
||||
local infotext = enabled and string.format("'%s' Autocrafter (%s)", description, name)
|
||||
or string.format("paused '%s' Autocrafter", description)
|
||||
|
||||
meta:set_string("infotext", infotext)
|
||||
return enabled
|
||||
end
|
||||
|
||||
-- 1st version of the autocrafter had actual items in the crafting grid
|
||||
-- the 2nd replaced these with virtual items, dropped the content on update and set "virtual_items" to string "1"
|
||||
-- the third added an output inventory, changed the formspec and added a button for enabling/disabling
|
||||
-- so we work out way backwards on this history and update each single case to the newest version
|
||||
local function upgrade_autocrafter(pos, meta)
|
||||
local meta = meta or minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
|
||||
if inv:get_size("output") == 0 then -- we are version 2 or 1
|
||||
inv:set_size("output", 1)
|
||||
-- migrate the old autocrafters into an "enabled" state
|
||||
update_meta(meta, true)
|
||||
|
||||
if meta:get_string("virtual_items") == "1" then -- we are version 2
|
||||
-- we allready dropped stuff, so lets remove the metadatasetting (we are not being called again for this node)
|
||||
meta:set_string("virtual_items", "")
|
||||
else -- we are version 1
|
||||
local recipe = inv:get_list("recipe")
|
||||
if not recipe then return end
|
||||
for idx, stack in ipairs(recipe) do
|
||||
if not stack:is_empty() then
|
||||
minetest.item_drop(stack, "", pos)
|
||||
stack:set_count(1)
|
||||
stack:set_wear(0)
|
||||
inv:set_stack("recipe", idx, stack)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- update the recipe, cache, and start the crafter
|
||||
autocrafterCache[minetest.hash_node_position(pos)] = nil
|
||||
after_recipe_change(pos, inv)
|
||||
end
|
||||
end
|
||||
|
||||
minetest.register_node("basic_machines:autocrafter", {
|
||||
description = "Autocrafter",
|
||||
drawtype = "normal",
|
||||
tiles = {"pipeworks_autocrafter.png"},
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
on_construct = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
inv:set_size("src", 3*8)
|
||||
inv:set_size("recipe", 3*3)
|
||||
inv:set_size("dst", 4*3)
|
||||
inv:set_size("output", 1)
|
||||
update_meta(meta, false)
|
||||
end,
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
--if not pipeworks.may_configure(pos, sender) then return end
|
||||
local meta = minetest.get_meta(pos)
|
||||
if fields.on then
|
||||
update_meta(meta, false)
|
||||
--minetest.get_node_timer(pos):stop()
|
||||
elseif fields.off then
|
||||
if update_meta(meta, true) then
|
||||
start_crafter(pos)
|
||||
end
|
||||
end
|
||||
end,
|
||||
can_dig = function(pos, player)
|
||||
upgrade_autocrafter(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local inv = meta:get_inventory()
|
||||
return (inv:is_empty("src") and inv:is_empty("dst"))
|
||||
end,
|
||||
after_place_node = function(pos, placer) -- rnd : set owner
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_string("owner", placer:get_player_name());
|
||||
end,
|
||||
--after_place_node = pipeworks.scan_for_tube_objects,
|
||||
--after_dig_node = function(pos)
|
||||
--pipeworks.scan_for_tube_objects(pos)
|
||||
--end,
|
||||
on_destruct = function(pos)
|
||||
autocrafterCache[minetest.hash_node_position(pos)] = nil
|
||||
end,
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
--if not pipeworks.may_configure(pos, player) then return 0 end
|
||||
local meta = minetest.get_meta(pos);if meta:get_string("owner")~=player:get_player_name() then return 0 end -- rnd
|
||||
|
||||
upgrade_autocrafter(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
if listname == "recipe" then
|
||||
stack:set_count(1)
|
||||
inv:set_stack(listname, index, stack)
|
||||
after_recipe_change(pos, inv)
|
||||
return 0
|
||||
elseif listname == "output" then
|
||||
on_output_change(pos, inv, stack)
|
||||
return 0
|
||||
end
|
||||
after_inventory_change(pos)
|
||||
return stack:get_count()
|
||||
end,
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
--if not pipeworks.may_configure(pos, player) then
|
||||
-- minetest.log("action", string.format("%s attempted to take from autocrafter at %s", player:get_player_name(), minetest.pos_to_string(pos)))
|
||||
-- return 0
|
||||
-- end
|
||||
local meta = minetest.get_meta(pos);if meta:get_string("owner")~=player:get_player_name() then return 0 end -- rnd
|
||||
|
||||
upgrade_autocrafter(pos)
|
||||
local inv = minetest.get_meta(pos):get_inventory()
|
||||
if listname == "recipe" then
|
||||
inv:set_stack(listname, index, ItemStack(""))
|
||||
after_recipe_change(pos, inv)
|
||||
return 0
|
||||
elseif listname == "output" then
|
||||
on_output_change(pos, inv, nil)
|
||||
return 0
|
||||
end
|
||||
after_inventory_change(pos)
|
||||
return stack:get_count()
|
||||
end,
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
return 0; -- no internal inventory moves!
|
||||
end,
|
||||
|
||||
mesecons = {effector = { -- rnd: run machine when activated by signal
|
||||
action_on = function (pos, node,ttl)
|
||||
if type(ttl)~="number" then ttl = 1 end
|
||||
if ttl<0 then return end -- machines_TTL prevents infinite recursion
|
||||
run_autocrafter(pos, craft_time);
|
||||
end,
|
||||
|
||||
can_dig = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
|
||||
if not (inv:is_empty("src")) or not (inv:is_empty("dst")) then return false end -- all inv must be empty to be dug
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
--on_timer = run_autocrafter -- rnd
|
||||
})
|
||||
|
||||
-- minetest.register_craft( {
|
||||
-- output = "basic_machines:autocrafter",
|
||||
-- recipe = {
|
||||
-- { "default:steel_ingot", "default:mese_crystal", "default:steel_ingot" },
|
||||
-- { "default:diamondblock", "default:steel_ingot", "default:diamondblock" },
|
||||
-- { "default:steel_ingot", "default:mese_crystal", "default:steel_ingot" }
|
||||
-- },
|
||||
-- })
|
|
@ -0,0 +1,670 @@
|
|||
-- BALL: energy ball that flies around, can bounce and activate stuff
|
||||
-- rnd 2016:
|
||||
|
||||
-- TO DO: move mode: ball just rolling around on ground without hopping, also if inside slope it would "roll down", just increased velocity in slope direction
|
||||
|
||||
-- SETTINGS
|
||||
|
||||
basic_machines.ball = {};
|
||||
basic_machines.ball.maxdamage = 10; -- player health 20
|
||||
basic_machines.ball.bounce_materials = { -- to be used with bounce setting 2 in ball spawner: 1: bounce in x direction, 2: bounce in z direction, otherwise it bounces in y direction
|
||||
["default:wood"]=1,
|
||||
["xpanes:bar_2"]=1,
|
||||
["xpanes:bar_10"]=1,
|
||||
["darkage:iron_bars"]=1,
|
||||
["default:glass"] = 2,
|
||||
};
|
||||
|
||||
-- END OF SETTINGS
|
||||
|
||||
local ballcount = {};
|
||||
local function round(x)
|
||||
if x < 0 then
|
||||
return -math.floor(-x+0.5);
|
||||
else
|
||||
return math.floor(x+0.5);
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local ball_spawner_update_form = function (pos)
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local x0,y0,z0;
|
||||
x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); -- direction of velocity
|
||||
|
||||
local energy,bounce,g,puncheable, gravity,hp,hurt,solid;
|
||||
local speed = meta:get_float("speed"); -- if positive sets initial ball speed
|
||||
energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing
|
||||
bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
||||
gravity = meta:get_float("gravity"); -- gravity
|
||||
hp = meta:get_float("hp");
|
||||
hurt = meta:get_float("hurt");
|
||||
puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone
|
||||
solid = meta:get_int("solid"); -- if 1 then entity is solid - cant be walked on
|
||||
|
||||
local texture = meta:get_string("texture") or "basic_machines_ball.png";
|
||||
local visual = meta:get_string("visual") or "sprite";
|
||||
local scale = meta:get_int("scale");
|
||||
|
||||
local form =
|
||||
"size[4.25,4.75]" .. -- width, height
|
||||
"field[0.25,0.5;1,1;x0;target;"..x0.."] field[1.25,0.5;1,1;y0;;"..y0.."] field[2.25,0.5;1,1;z0;;"..z0.."]"..
|
||||
"field[3.25,0.5;1,1;speed;speed;"..speed.."]"..
|
||||
--speed, jump, gravity,sneak
|
||||
"field[0.25,1.5;1,1;energy;energy;"..energy.."]"..
|
||||
"field[1.25,1.5;1,1;bounce;bounce;".. bounce.."]"..
|
||||
"field[2.25,1.5;1,1;gravity;gravity;"..gravity.."]"..
|
||||
"field[3.25,1.5;1,1;puncheable;puncheable;"..puncheable.."]"..
|
||||
"field[3.25,2.5;1,1;solid;solid;"..solid.."]"..
|
||||
"field[0.25,2.5;1,1;hp;hp;"..hp.."]".."field[1.25,2.5;1,1;hurt;hurt;"..hurt.."]"..
|
||||
"field[0.25,3.5;4,1;texture;texture;"..minetest.formspec_escape(texture).."]"..
|
||||
"field[0.25,4.5;1,1;scale;scale;"..scale.."]".."field[1.25,4.5;1,1;visual;visual;"..visual.."]"..
|
||||
"button_exit[3.25,4.25;1,1;OK;OK]";
|
||||
|
||||
|
||||
|
||||
if meta:get_int("admin")==1 then
|
||||
local lifetime = meta:get_int("lifetime");
|
||||
if lifetime <= 0 then lifetime = 20 end
|
||||
form = form .. "field[2.25,2.5;1,1;lifetime;lifetime;"..lifetime.."]"
|
||||
end
|
||||
|
||||
meta:set_string("formspec",form);
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
minetest.register_entity("basic_machines:ball",{
|
||||
timer = 0,
|
||||
lifetime = 20, -- how long it exists before disappearing
|
||||
energy = 0, -- if negative it will deactivate stuff, positive will activate, 0 wont do anything
|
||||
puncheable = 1, -- can be punched by players in protection
|
||||
bounce = 0, -- 0: absorbs in block, 1 = proper bounce=lag buggy, -- to do: 2 = line of sight bounce
|
||||
gravity = 0,
|
||||
speed = 5, -- velocity when punched
|
||||
hurt = 0, -- how much damage it does to target entity, if 0 damage disabled
|
||||
owner = "",
|
||||
state = false,
|
||||
origin = {x=0,y=0,z=0},
|
||||
lastpos = {x=0,y=0,z=0}, -- last not-colliding position
|
||||
hp_max = 100,
|
||||
elasticity = 0.9, -- speed gets multiplied by this after bounce
|
||||
visual="sprite",
|
||||
visual_size={x=.6,y=.6},
|
||||
collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5},
|
||||
physical=false,
|
||||
|
||||
--textures={"basic_machines_ball"},
|
||||
|
||||
on_activate = function(self, staticdata)
|
||||
self.object:set_properties({textures={"basic_machines_ball.png"}})
|
||||
self.object:set_properties({visual_size = {x=1, y=1}});
|
||||
self.timer = 0;self.owner = "";
|
||||
self.origin = self.object:getpos();
|
||||
self.lifetime = 20;
|
||||
end,
|
||||
|
||||
get_staticdata = function(self) -- this gets called before object put in world and before it hides
|
||||
if not self.state then return nil end
|
||||
self.object:remove();
|
||||
return nil
|
||||
end,
|
||||
|
||||
|
||||
on_punch = function (self, puncher, time_from_last_punch, tool_capabilities, dir)
|
||||
if self.puncheable == 0 then return end
|
||||
if self.puncheable == 1 then -- only those in protection
|
||||
local name = puncher:get_player_name();
|
||||
local pos = self.object:getpos();
|
||||
if minetest.is_protected(pos,name) then return end
|
||||
end
|
||||
--minetest.chat_send_all(minetest.pos_to_string(dir))
|
||||
if time_from_last_punch<0.5 then return end
|
||||
local v = self.speed or 1;
|
||||
|
||||
local velocity = dir;
|
||||
velocity.x = velocity.x*v;velocity.y = velocity.y*v;velocity.z = velocity.z*v;
|
||||
self.object:setvelocity(velocity)
|
||||
end,
|
||||
|
||||
on_step = function(self, dtime)
|
||||
self.timer=self.timer+dtime
|
||||
if self.timer>self.lifetime then
|
||||
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
if not self.state then self.state = true end
|
||||
local pos=self.object:getpos()
|
||||
|
||||
local origin = self.origin;
|
||||
|
||||
local r = 30;-- maximal distance when balls disappear
|
||||
local dist = math.max(math.abs(pos.x-origin.x), math.abs(pos.y-origin.y), math.abs(pos.z-origin.z));
|
||||
if dist>r then -- remove if it goes too far
|
||||
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
local nodename = minetest.get_node(pos).name;
|
||||
local walkable = false;
|
||||
if nodename ~= "air" then
|
||||
walkable = minetest.registered_nodes[nodename].walkable;
|
||||
if nodename == "basic_machines:ball_spawner" and dist>0.5 then walkable = true end -- ball can activate spawner, just not originating one
|
||||
end
|
||||
if not walkable then
|
||||
self.lastpos = pos
|
||||
if self.hurt~=0 then -- check for coliding nearby objects
|
||||
local objects = minetest.get_objects_inside_radius(pos,2);
|
||||
if #objects>1 then
|
||||
for _, obj in pairs(objects) do
|
||||
local p = obj:getpos();
|
||||
local d = math.sqrt((p.x-pos.x)^2+(p.y-pos.y)^2+(p.z-pos.z)^2);
|
||||
if d>0 then
|
||||
|
||||
--if minetest.is_protected(p,self.owner) then return end
|
||||
if math.abs(p.x)<32 and math.abs(p.y)<32 and math.abs(p.z)<32 then return end -- no damage around spawn
|
||||
|
||||
if obj:is_player() then --player
|
||||
if obj:get_player_name()==self.owner then break end -- dont hurt owner
|
||||
|
||||
local hp = obj:get_hp()
|
||||
local newhp = hp-self.hurt;
|
||||
if newhp<=0 and boneworld and boneworld.killxp then
|
||||
local killxp = boneworld.killxp[self.owner];
|
||||
if killxp then
|
||||
boneworld.killxp[self.owner] = killxp + 0.01;
|
||||
end
|
||||
end
|
||||
obj:set_hp(newhp)
|
||||
else -- non player
|
||||
local lua_entity = obj:get_luaentity();
|
||||
if lua_entity and lua_entity.itemstring then
|
||||
local entname = lua_entity.itemstring;
|
||||
if entname == "robot" then
|
||||
self.object:remove()
|
||||
return;
|
||||
end
|
||||
end
|
||||
local hp = obj:get_hp()
|
||||
local newhp = hp-self.hurt;
|
||||
minetest.chat_send_player(self.owner,"#ball: target hp " .. newhp)
|
||||
if newhp<=0 then obj:remove() else obj:set_hp(newhp) end
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
||||
self.object:remove();
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if walkable then -- we hit a node
|
||||
--minetest.chat_send_all(" hit node at " .. minetest.pos_to_string(pos))
|
||||
|
||||
|
||||
local node = minetest.get_node(pos);
|
||||
local table = minetest.registered_nodes[node.name];
|
||||
if table and table.mesecons and table.mesecons.effector then -- activate target
|
||||
|
||||
local energy = self.energy;
|
||||
if energy~=0 then
|
||||
if minetest.is_protected(pos,self.owner) then return end
|
||||
end
|
||||
local effector = table.mesecons.effector;
|
||||
|
||||
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
||||
self.object:remove();
|
||||
|
||||
if energy>0 then
|
||||
if not effector.action_on then return end
|
||||
effector.action_on(pos,node,16);
|
||||
elseif energy<0 then
|
||||
if not effector.action_off then return end
|
||||
effector.action_off(pos,node,16);
|
||||
end
|
||||
|
||||
|
||||
else -- bounce ( copyright rnd, 2016 )
|
||||
local bounce = self.bounce;
|
||||
if self.bounce == 0 then
|
||||
local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count;
|
||||
self.object:remove()
|
||||
return end
|
||||
|
||||
local n = {x=0,y=0,z=0}; -- this will be bounce normal
|
||||
local v = self.object:getvelocity();
|
||||
local opos = {x=round(pos.x),y=round(pos.y), z=round(pos.z)}; -- obstacle
|
||||
local bpos ={ x=(pos.x-opos.x),y=(pos.y-opos.y),z=(pos.z-opos.z)}; -- boundary position on cube, approximate
|
||||
|
||||
if bounce == 2 then -- uses special blocks for non buggy lag proof bouncing: by default it bounces in y direction
|
||||
local bounce_direction = basic_machines.ball.bounce_materials[node.name] or 0;
|
||||
|
||||
if bounce_direction == 0 then
|
||||
if v.y>=0 then n.y = -1 else n.y = 1 end
|
||||
elseif bounce_direction == 1 then
|
||||
if v.x>=0 then n.x = -1 else n.x = 1 end
|
||||
n.y = 0;
|
||||
elseif bounce_direction == 2 then
|
||||
if v.z>=0 then n.z = -1 else n.z = 1 end
|
||||
n.y = 0;
|
||||
end
|
||||
|
||||
else -- algorithm to determine bounce direction - problem: with lag its impossible to determine reliable which node was hit and which face ..
|
||||
|
||||
if v.x<=0 then n.x = 1 else n.x = -1 end -- possible bounce directions
|
||||
if v.y<=0 then n.y = 1 else n.y = -1 end
|
||||
if v.z<=0 then n.z = 1 else n.z = -1 end
|
||||
|
||||
local dpos = {};
|
||||
|
||||
dpos.x = 0.5*n.x; dpos.y = 0; dpos.z = 0; -- calculate distance to bounding surface midpoints
|
||||
|
||||
local d1 = (bpos.x-dpos.x)^2 + (bpos.y)^2 + (bpos.z)^2;
|
||||
dpos.x = 0; dpos.y = 0.5*n.y; dpos.z = 0;
|
||||
local d2 = (bpos.x)^2 + (bpos.y-dpos.y)^2 + (bpos.z)^2;
|
||||
dpos.x = 0; dpos.y = 0; dpos.z = 0.5*n.z;
|
||||
local d3 = (bpos.x)^2 + (bpos.y)^2 + (bpos.z-dpos.z)^2;
|
||||
local d = math.min(d1,d2,d3); -- we obtain bounce direction from minimal distance
|
||||
|
||||
if d1==d then --x
|
||||
n.y=0;n.z=0
|
||||
elseif d2==d then --y
|
||||
n.x=0;n.z=0
|
||||
elseif d3==d then --z
|
||||
n.x=0;n.y=0
|
||||
end
|
||||
|
||||
|
||||
nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal
|
||||
walkable = nodename ~= "air";
|
||||
if walkable then -- problem, nonempty node - incorrect normal, fix it
|
||||
if n.x ~=0 then -- x direction is wrong, try something else
|
||||
n.x=0;
|
||||
if v.y>=0 then n.y = -1 else n.y = 1 end -- try y
|
||||
nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal
|
||||
walkable = nodename ~= "air";
|
||||
if walkable then -- still problem, only remaining is z
|
||||
n.y=0;
|
||||
if v.z>=0 then n.z = -1 else n.z = 1 end
|
||||
nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal
|
||||
walkable = nodename ~= "air";
|
||||
if walkable then -- messed up, just remove the ball
|
||||
self.object:remove()
|
||||
return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
local elasticity = self.elasticity;
|
||||
|
||||
-- bounce
|
||||
if n.x~=0 then
|
||||
v.x=-elasticity*v.x
|
||||
elseif n.y~=0 then
|
||||
v.y=-elasticity*v.y
|
||||
elseif n.z~=0 then
|
||||
v.z=-elasticity*v.z
|
||||
end
|
||||
|
||||
local r = 0.2
|
||||
bpos = {x=pos.x+n.x*r,y=pos.y+n.y*r,z=pos.z+n.z*r}; -- point placed a bit further away from box
|
||||
self.object:setpos(bpos) -- place object at last known outside point
|
||||
|
||||
self.object:setvelocity(v);
|
||||
|
||||
minetest.sound_play("default_dig_cracky", {pos=pos,gain=1.0,max_hear_distance = 8,})
|
||||
|
||||
end
|
||||
end
|
||||
return
|
||||
end,
|
||||
})
|
||||
|
||||
|
||||
minetest.register_node("basic_machines:ball_spawner", {
|
||||
description = "Spawns energy ball one block above",
|
||||
tiles = {"basic_machines_ball.png"},
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
drawtype = "allfaces",
|
||||
paramtype = "light",
|
||||
param1=1,
|
||||
walkable = false,
|
||||
alpha = 150,
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("owner", placer:get_player_name());
|
||||
local privs = minetest.get_player_privs(placer:get_player_name()); if privs.privs then meta:set_int("admin",1) end
|
||||
|
||||
if privs.machines then meta:set_int("machines",1) end
|
||||
|
||||
meta:set_float("hurt",0);
|
||||
meta:set_string("texture", "basic_machines_ball.png");
|
||||
meta:set_float("hp",100);
|
||||
meta:set_float("speed",5); -- if positive sets initial ball speed
|
||||
meta:set_float("energy",1); -- if positive activates, negative deactivates, 0 does nothing
|
||||
meta:set_int("bounce",0); -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
||||
meta:set_float("gravity",0); -- gravity
|
||||
meta:set_int("puncheable",0); -- if 0 not puncheable, if 1 can be punched by players in protection, if 2 can be punched by anyone
|
||||
meta:set_int("scale",100);
|
||||
meta:set_string("visual","sprite"); -- sprite/cube OR particle
|
||||
ball_spawner_update_form(pos);
|
||||
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = function (pos, node,ttl)
|
||||
if type(ttl)~="number" then ttl = 1 end
|
||||
if ttl<0 then return end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local t0 = meta:get_int("t");
|
||||
local t1 = minetest.get_gametime();
|
||||
local T = meta:get_int("T"); -- temperature
|
||||
|
||||
if t0>t1-2 then -- activated before natural time
|
||||
T=T+1;
|
||||
else
|
||||
if T>0 then
|
||||
T=T-1
|
||||
if t1-t0>5 then T = 0 end
|
||||
end
|
||||
end
|
||||
meta:set_int("T",T);
|
||||
meta:set_int("t",t1); -- update last activation time
|
||||
|
||||
if T > 2 then -- overheat
|
||||
minetest.sound_play("default_cool_lava",{pos = pos, max_hear_distance = 16, gain = 0.25})
|
||||
meta:set_string("infotext","overheat: temperature ".. T)
|
||||
return
|
||||
end
|
||||
|
||||
if meta:get_int("machines")~=1 then -- no machines priv, limit ball count
|
||||
local owner = meta:get_string("owner");
|
||||
local count = ballcount[owner];
|
||||
if not count or count<0 then count = 0 end
|
||||
|
||||
if count>=2 then
|
||||
if t1-t0>10 then count = 0
|
||||
else return
|
||||
end
|
||||
end
|
||||
|
||||
count = count + 1;
|
||||
ballcount[owner]=count;
|
||||
--minetest.chat_send_all("count " .. count);
|
||||
end
|
||||
|
||||
pos.x = round(pos.x);pos.y = round(pos.y);pos.z = round(pos.z);
|
||||
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball");
|
||||
local luaent = obj:get_luaentity();
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
local speed,energy,bounce,gravity,puncheable,solid;
|
||||
speed = meta:get_float("speed");
|
||||
energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing
|
||||
bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
||||
gravity = meta:get_float("gravity"); -- gravity
|
||||
puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone
|
||||
solid = meta:get_int("solid");
|
||||
|
||||
if energy<0 then
|
||||
obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}})
|
||||
end
|
||||
|
||||
luaent.bounce = bounce;
|
||||
luaent.energy = energy;
|
||||
if gravity>0 then
|
||||
obj:setacceleration({x=0,y=-gravity,z=0});
|
||||
end
|
||||
luaent.puncheable = puncheable;
|
||||
luaent.owner = meta:get_string("owner");
|
||||
luaent.hurt = meta:get_float("hurt");
|
||||
if solid==1 then
|
||||
luaent.physical = true
|
||||
end
|
||||
|
||||
obj:set_hp( meta:get_float("hp") );
|
||||
|
||||
local x0,y0,z0;
|
||||
if speed>0 then luaent.speed = speed end
|
||||
|
||||
x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); -- direction of velocity
|
||||
if speed~=0 and (x0~=0 or y0~=0 or z0~=0) then -- set velocity direction
|
||||
local velocity = {x=x0,y=y0,z=z0};
|
||||
local v = math.sqrt(velocity.x^2+velocity.y^2+velocity.z^2); if v == 0 then v = 1 end
|
||||
v = v / speed;
|
||||
velocity.x=velocity.x/v;velocity.y=velocity.y/v;velocity.z=velocity.z/v;
|
||||
obj:setvelocity(velocity);
|
||||
end
|
||||
|
||||
if meta:get_int("admin")==1 then
|
||||
luaent.lifetime = meta:get_float("lifetime");
|
||||
end
|
||||
|
||||
|
||||
local visual = meta:get_string("visual")
|
||||
obj:set_properties({visual=visual});
|
||||
local texture = meta:get_string("texture");
|
||||
if visual=="sprite" then
|
||||
obj:set_properties({textures={texture}})
|
||||
elseif visual == "cube" then
|
||||
obj:set_properties({textures={texture,texture,texture,texture,texture,texture}})
|
||||
end
|
||||
local scale = meta:get_int("scale");if scale<=0 then scale = 1 else scale = scale/100 end
|
||||
obj:set_properties({visual_size = {x=scale, y=scale}});
|
||||
|
||||
|
||||
|
||||
end,
|
||||
|
||||
action_off = function (pos, node,ttl)
|
||||
if type(ttl)~="number" then ttl = 1 end
|
||||
if ttl<0 then return end
|
||||
pos.x = round(pos.x);pos.y = round(pos.y);pos.z = round(pos.z);
|
||||
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball");
|
||||
local luaent = obj:get_luaentity();
|
||||
luaent.energy = -1;
|
||||
obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}})
|
||||
end
|
||||
}
|
||||
},
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
local name = sender:get_player_name();if minetest.is_protected(pos,name) then return end
|
||||
|
||||
if fields.OK then
|
||||
local privs = minetest.get_player_privs(sender:get_player_name());
|
||||
local meta = minetest.get_meta(pos);
|
||||
local x0=0; local y0=0; local z0=0;
|
||||
--minetest.chat_send_all("form at " .. dump(pos) .. " fields " .. dump(fields))
|
||||
if fields.x0 then x0 = tonumber(fields.x0) or 0 end
|
||||
if fields.y0 then y0 = tonumber(fields.y0) or 0 end
|
||||
if fields.z0 then z0 = tonumber(fields.z0) or 0 end
|
||||
if not privs.privs and (math.abs(x0)>10 or math.abs(y0)>10 or math.abs(z0) > 10) then return end
|
||||
|
||||
meta:set_int("x0",x0);meta:set_int("y0",y0);meta:set_int("z0",z0);
|
||||
|
||||
local speed,energy,bounce,gravity,puncheable,solid;
|
||||
energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing
|
||||
bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
||||
gravity = meta:get_float("gravity"); -- gravity
|
||||
puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone
|
||||
solid = meta:get_int("solid");
|
||||
|
||||
|
||||
if fields.speed then
|
||||
local speed = tonumber(fields.speed) or 0;
|
||||
if (speed > 10 or speed < 0) and not privs.privs then return end
|
||||
meta:set_float("speed", speed)
|
||||
end
|
||||
|
||||
if fields.energy then
|
||||
local energy = tonumber(fields.energy) or 1;
|
||||
meta:set_float("energy", energy)
|
||||
end
|
||||
|
||||
if fields.bounce then
|
||||
local bounce = tonumber(fields.bounce) or 1;
|
||||
meta:set_int("bounce",bounce)
|
||||
end
|
||||
|
||||
if fields.gravity then
|
||||
local gravity = tonumber(fields.gravity) or 1;
|
||||
if (gravity<0 or gravity>30) and not privs.privs then return end
|
||||
meta:set_float("gravity", gravity)
|
||||
end
|
||||
if fields.puncheable then
|
||||
meta:set_int("puncheable", tonumber(fields.puncheable) or 0)
|
||||
end
|
||||
|
||||
if fields.solid then
|
||||
meta:set_int("solid", tonumber(fields.solid) or 0)
|
||||
end
|
||||
|
||||
if fields.lifetime then
|
||||
meta:set_int("lifetime", tonumber(fields.lifetime) or 0)
|
||||
end
|
||||
|
||||
if fields.hurt then
|
||||
meta:set_float("hurt", tonumber(fields.hurt) or 0)
|
||||
end
|
||||
|
||||
if fields.hp then
|
||||
meta:set_float("hp", math.abs(tonumber(fields.hp) or 0))
|
||||
end
|
||||
|
||||
if fields.texture then
|
||||
meta:set_string ("texture", fields.texture);
|
||||
end
|
||||
|
||||
if fields.scale then
|
||||
local scale = math.abs(tonumber(fields.scale) or 100);
|
||||
if scale>1000 and not privs.privs then scale = 1000 end
|
||||
meta:set_int("scale", scale)
|
||||
end
|
||||
|
||||
if fields.visual then
|
||||
local visual = fields.visual or "";
|
||||
if visual~="sprite" and visual~="cube" then return end
|
||||
meta:set_string ("visual", fields.visual);
|
||||
end
|
||||
|
||||
ball_spawner_update_form(pos);
|
||||
end
|
||||
end,
|
||||
|
||||
after_dig_node = function(pos, oldnode, oldmetadata, digger)
|
||||
local name = digger:get_player_name();
|
||||
local inv = digger:get_inventory();
|
||||
inv:remove_item("main", ItemStack("basic_machines:ball_spawner"));
|
||||
local stack = ItemStack("basic_machines:ball_spell");
|
||||
local meta = oldmetadata["fields"];
|
||||
meta["formspec"]=nil;
|
||||
stack:set_metadata(minetest.serialize(meta));
|
||||
inv:add_item("main",stack);
|
||||
end
|
||||
|
||||
})
|
||||
|
||||
|
||||
local spelltime = {};
|
||||
|
||||
-- ball as magic spell user can cast
|
||||
minetest.register_tool("basic_machines:ball_spell", {
|
||||
description = "ball spawner",
|
||||
inventory_image = "basic_machines_ball.png",
|
||||
tool_capabilities = {
|
||||
full_punch_interval = 2,
|
||||
max_drop_level=0,
|
||||
},
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
|
||||
local pos = user:getpos();pos.y=pos.y+1;
|
||||
local meta = minetest.deserialize(itemstack:get_metadata());
|
||||
if not meta then return end
|
||||
local owner = meta["owner"] or "";
|
||||
|
||||
--if minetest.is_protected(pos,owner) then return end
|
||||
|
||||
local t0 = spelltime[owner] or 0;
|
||||
local t1 = minetest.get_gametime();
|
||||
if t1-t0<2 then return end -- too soon
|
||||
spelltime[owner]=t1;
|
||||
|
||||
|
||||
local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball");
|
||||
local luaent = obj:get_luaentity();
|
||||
|
||||
|
||||
local speed,energy,bounce,gravity,puncheable;
|
||||
speed = tonumber(meta["speed"]) or 0;
|
||||
energy = tonumber(meta["energy"]) or 0; -- if positive activates, negative deactivates, 0 does nothing
|
||||
bounce = tonumber(meta["bounce"]) or 0; -- if nonzero bounces when hit obstacle, 0 gets absorbed
|
||||
gravity = tonumber(meta["gravity"]) or 0; -- gravity
|
||||
puncheable = tonumber(meta["puncheable"]) or 0; -- if 1 can be punched by players in protection, if 2 can be punched by anyone
|
||||
|
||||
if energy<0 then
|
||||
obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}})
|
||||
end
|
||||
|
||||
luaent.bounce = bounce;
|
||||
luaent.energy = energy;
|
||||
if gravity>0 then
|
||||
obj:setacceleration({x=0,y=-gravity,z=0});
|
||||
end
|
||||
luaent.puncheable = puncheable;
|
||||
luaent.owner = meta["owner"];
|
||||
luaent.hurt = math.min(tonumber(meta["hurt"]),basic_machines.ball.maxdamage);
|
||||
|
||||
obj:set_hp( tonumber(meta["hp"]) );
|
||||
|
||||
local x0,y0,z0;
|
||||
if speed>0 then luaent.speed = speed end
|
||||
|
||||
|
||||
|
||||
local v = user:get_look_dir();
|
||||
v.x=v.x*speed;v.y=v.y*speed;v.z=v.z*speed;
|
||||
obj:setvelocity(v);
|
||||
|
||||
|
||||
if tonumber(meta["admin"])==1 then
|
||||
luaent.lifetime = tonumber(meta["lifetime"]);
|
||||
end
|
||||
|
||||
|
||||
obj:set_properties({textures={meta["texture"]}})
|
||||
|
||||
|
||||
end,
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
-- minetest.register_craft({
|
||||
-- output = "basic_machines:ball_spawner",
|
||||
-- recipe = {
|
||||
-- {"basic_machines:power_cell"},
|
||||
-- {"basic_machines:keypad"}
|
||||
-- }
|
||||
-- })
|
|
@ -0,0 +1,230 @@
|
|||
-- rnd 2016:
|
||||
|
||||
-- CONSTRUCTOR machine: used to make all other basic_machines
|
||||
|
||||
basic_machines.craft_recipes = {
|
||||
["keypad"] = {item = "basic_machines:keypad", description = "Turns on/off lights and activates machines or opens doors", craft = {"default:wood","default:stick"}, tex = "keypad"},
|
||||
["light"]={item = "basic_machines:light_on", description = "Light in darkness", craft = {"default:torch 4"}, tex = "light"},
|
||||
["mover"]={item = "basic_machines:mover", description = "Can dig, harvest, plant, teleport or move items from/in inventories", craft = {"default:mese_crystal 6","default:stone 2", "basic_machines:keypad"}, tex = "basic_machine_mover_side"},
|
||||
|
||||
["detector"] = {item = "basic_machines:detector", description = "Detect and measure players, objects,blocks,light level", craft = {"default:mese_crystal 4","basic_machines:keypad"}, tex = "detector"},
|
||||
|
||||
["distributor"]= {item = "basic_machines:distributor", description = "Organize your circuits better", craft = {"default:steel_ingot","default:mese_crystal", "basic_machines:keypad"}, tex = "distributor"},
|
||||
|
||||
["clock_generator"]= {item = "basic_machines:clockgen", description = "For making circuits that run non stop", craft = {"default:diamondblock","basic_machines:keypad"}, tex = "basic_machine_clock_generator"},
|
||||
|
||||
["recycler"]= {item = "basic_machines:recycler", description = "Recycle old tools", craft = {"default:mese_crystal 8","default:diamondblock"}, tex = "recycler"},
|
||||
|
||||
["enviroment"] = {item = "basic_machines:enviro", description = "Change gravity and more", craft = {"basic_machines:generator 8","basic_machines:clockgen"}, tex = "enviro"},
|
||||
|
||||
["ball_spawner"]={item = "basic_machines:ball_spawner", description = "Spawn moving energy balls", craft = {"basic_machines:power_cell","basic_machines:keypad"}, tex = "basic_machines_ball"},
|
||||
|
||||
["battery"]={item = "basic_machines:battery_0", description = "Power for machines", craft = {"default:bronzeblock 2","default:mese","default:diamond"}, tex = "basic_machine_battery"},
|
||||
|
||||
["generator"]={item = "basic_machines:generator", description = "Generate power crystals", craft = {"default:diamondblock 5","basic_machines:battery 5","default:goldblock 5"}, tex = "basic_machine_generator"},
|
||||
|
||||
["autocrafter"] = {item = "basic_machines:autocrafter", description = "Automate crafting", craft = { "default:steel_ingot 5", "default:mese_crystal 2", "default:diamondblock 2"}, tex = "pipeworks_autocrafter"},
|
||||
|
||||
["grinder"] = {item = "basic_machines:grinder", description = "Makes dusts and grinds materials", craft = {"default:diamond 13","default:mese 4"}, tex = "grinder"},
|
||||
|
||||
["power_block"] = {item = "basic_machines:power_block 5", description = "Energy cell, contains 11 energy units", craft = {"basic_machines:power_rod"}, tex = "power_block"},
|
||||
|
||||
["power_cell"] = {item = "basic_machines:power_cell 5", description = "Energy cell, contains 1 energy unit", craft = {"basic_machines:power_block"}, tex = "power_cell"},
|
||||
|
||||
["coal_lump"] = {item = "default:coal_lump", description = "Coal lump, contains 1 energy unit", craft = {"basic_machines:power_cell 2"}, tex = "default_coal_lump"},
|
||||
|
||||
}
|
||||
|
||||
basic_machines.craft_recipe_order = { -- order in which nodes appear
|
||||
"keypad","light","grinder","mover", "battery","generator","detector", "distributor", "clock_generator","recycler","autocrafter","ball_spawner", "enviroment", "power_block", "power_cell", "coal_lump",
|
||||
}
|
||||
|
||||
|
||||
|
||||
local constructor_process = function(pos)
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local craft = basic_machines.craft_recipes[meta:get_string("craft")];
|
||||
if not craft then return end
|
||||
local item = craft.item;
|
||||
local craftlist = craft.craft;
|
||||
|
||||
local inv = meta:get_inventory();
|
||||
for _,v in pairs(craftlist) do
|
||||
if not inv:contains_item("main", ItemStack(v)) then
|
||||
meta:set_string("infotext", "#CRAFTING: you need " .. v .. " to craft " .. craft.item)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
for _,v in pairs(craftlist) do
|
||||
inv:remove_item("main", ItemStack(v));
|
||||
end
|
||||
inv:add_item("main", ItemStack(item));
|
||||
|
||||
end
|
||||
|
||||
local constructor_update_meta = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
|
||||
local craft = meta:get_string("craft");
|
||||
|
||||
local description = basic_machines.craft_recipes[craft];
|
||||
local tex;
|
||||
|
||||
if description then
|
||||
tex = description.tex;
|
||||
local i = 0;
|
||||
local itex;
|
||||
|
||||
local inv = meta:get_inventory(); -- set up craft list
|
||||
for _,v in pairs(description.craft) do
|
||||
i=i+1;
|
||||
inv:set_stack("recipe", i, ItemStack(v))
|
||||
end
|
||||
|
||||
for j = i+1,6 do
|
||||
inv:set_stack("recipe", j, ItemStack(""))
|
||||
end
|
||||
|
||||
description = description.description
|
||||
|
||||
else
|
||||
description = ""
|
||||
tex = ""
|
||||
end
|
||||
|
||||
|
||||
local textlist = " ";
|
||||
|
||||
local selected = meta:get_int("selected") or 1;
|
||||
for _,v in ipairs(basic_machines.craft_recipe_order) do
|
||||
textlist = textlist .. v .. ", ";
|
||||
|
||||
end
|
||||
|
||||
local form =
|
||||
"size[8,10]"..
|
||||
"textlist[0,0;3,1.5;craft;" .. textlist .. ";" .. selected .."]"..
|
||||
"button[3.5,1;1.25,0.75;CRAFT;CRAFT]"..
|
||||
"image[3.65,0;1,1;".. tex .. ".png]"..
|
||||
"label[0,1.85;".. description .. "]"..
|
||||
"list[context;recipe;5,0;3,2;]"..
|
||||
"label[0,2.3;Put crafting materials here]"..
|
||||
"list[context;main;0,2.7;8,3;]"..
|
||||
--"list[context;dst;5,0;3,2;]"..
|
||||
"label[0,5.5;player inventory]"..
|
||||
"list[current_player;main;0,6;8,4;]"..
|
||||
"listring[context;main]"..
|
||||
"listring[current_player;main]";
|
||||
meta:set_string("formspec", form);
|
||||
end
|
||||
|
||||
|
||||
minetest.register_node("basic_machines:constructor", {
|
||||
description = "Constructor: used to make machines",
|
||||
tiles = {"constructor.png"},
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_string("infotext", "Constructor: To operate it insert materials, select item to make and click craft button.")
|
||||
meta:set_string("owner", placer:get_player_name());
|
||||
meta:set_string("craft","keypad")
|
||||
meta:set_int("selected",1);
|
||||
local inv = meta:get_inventory();inv:set_size("main", 24);--inv:set_size("dst",6);
|
||||
inv:set_size("recipe",8);
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler
|
||||
constructor_update_meta(pos);
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if listname == "recipe" then return 0 end
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
if listname == "recipe" then return 0 end
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if listname == "recipe" then return 0 end
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
return 0;
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = function (pos, node,ttl)
|
||||
if type(ttl)~="number" then ttl = 1 end
|
||||
if ttl<0 then return end -- machines_TTL prevents infinite recursion
|
||||
constructor_process(pos);
|
||||
end
|
||||
}
|
||||
},
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
if minetest.is_protected(pos, sender:get_player_name()) then return end
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
if fields.craft then
|
||||
if string.sub(fields.craft,1,3)=="CHG" then
|
||||
local sel = tonumber(string.sub(fields.craft,5)) or 1
|
||||
meta:set_int("selected",sel);
|
||||
|
||||
local i = 0;
|
||||
for _,v in ipairs(basic_machines.craft_recipe_order) do
|
||||
i=i+1;
|
||||
if i == sel then meta:set_string("craft",v); break; end
|
||||
end
|
||||
else
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
if fields.CRAFT then
|
||||
constructor_process(pos);
|
||||
end
|
||||
|
||||
constructor_update_meta(pos);
|
||||
end,
|
||||
|
||||
can_dig = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
|
||||
if not (inv:is_empty("main")) then return false end -- main inv must be empty to be dug
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "basic_machines:constructor",
|
||||
recipe = {
|
||||
{"default:steel_ingot","default:steel_ingot","default:steel_ingot"},
|
||||
{"default:steel_ingot","default:copperblock","default:steel_ingot"},
|
||||
{"default:steel_ingot","default:steel_ingot","default:steel_ingot"},
|
||||
|
||||
}
|
||||
})
|
|
@ -0,0 +1,5 @@
|
|||
default
|
||||
protector?
|
||||
areas?
|
||||
boneworld?
|
||||
moreores?
|
|
@ -0,0 +1,388 @@
|
|||
-- ENVIRO block: change physics and skybox for players
|
||||
-- note: nonadmin players are limited in changes ( cant change skybox and have limits on other allowed changes)
|
||||
|
||||
-- rnd 2016:
|
||||
|
||||
local enviro = {};
|
||||
enviro.skyboxes = {
|
||||
["default"]={type = "regular", tex = {}},
|
||||
["space"]={type="skybox", tex={"sky_pos_y.png","sky_neg_y.png","sky_pos_z.png","sky_neg_z.png","sky_neg_x.png","sky_pos_x.png",}}, -- need textures installed!
|
||||
["caves"]={type = "cavebox", tex = {"black.png","black.png","black.png","black.png","black.png","black.png",}},
|
||||
};
|
||||
|
||||
local space_start = 1100;
|
||||
local ENABLE_SPACE_EFFECTS = false -- enable damage outside protected areas
|
||||
|
||||
local enviro_update_form = function (pos)
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
local x0,y0,z0;
|
||||
x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0");
|
||||
|
||||
local skybox = meta:get_string("skybox");
|
||||
local skylist = "";
|
||||
local sky_ind,j;
|
||||
j=1;sky_ind = 3;
|
||||
for i,_ in pairs(enviro.skyboxes) do
|
||||
if i == skybox then sky_ind = j end
|
||||
skylist = skylist .. i .. ",";
|
||||
j=j+1;
|
||||
end
|
||||
local r = meta:get_int("r");
|
||||
local speed,jump, g, sneak;
|
||||
speed = meta:get_float("speed");jump = meta:get_float("jump");
|
||||
g = meta:get_float("g"); sneak = meta:get_int("sneak");
|
||||
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z;
|
||||
|
||||
local form =
|
||||
"size[8,8.5]".. -- width, height
|
||||
"field[0.25,0.5;1,1;x0;target;"..x0.."] field[1.25,0.5;1,1;y0;;"..y0.."] field[2.25,0.5;1,1;z0;;"..z0.."]"..
|
||||
"field[3.25,0.5;1,1;r;radius;"..r.."]"..
|
||||
--speed, jump, gravity,sneak
|
||||
"field[0.25,1.5;1,1;speed;speed;"..speed.."]"..
|
||||
"field[1.25,1.5;1,1;jump;jump;"..jump.."]"..
|
||||
"field[2.25,1.5;1,1;g;gravity;"..g.."]"..
|
||||
"field[3.25,1.5;1,1;sneak;sneak;"..sneak.."]"..
|
||||
"label[0.,3.0;Skybox selection]"..
|
||||
"dropdown[0.,3.35;3,1;skybox;"..skylist..";"..sky_ind.."]"..
|
||||
"button_exit[3.25,3.25;1,1;OK;OK]"..
|
||||
"list["..list_name..";fuel;3.25,2.25;1,1;]"..
|
||||
"list[current_player;main;0,4.5;8,4;]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring["..list_name..";fuel]"..
|
||||
"listring[current_player;main]"
|
||||
meta:set_string("formspec",form)
|
||||
end
|
||||
|
||||
-- enviroment changer
|
||||
minetest.register_node("basic_machines:enviro", {
|
||||
description = "Changes enviroment for players around target location",
|
||||
tiles = {"enviro.png"},
|
||||
drawtype = "allfaces",
|
||||
paramtype = "light",
|
||||
param1=1,
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.env:get_meta(pos)
|
||||
meta:set_string("infotext", "Right click to set it. Activate by signal.")
|
||||
meta:set_string("owner", placer:get_player_name()); meta:set_int("public",1);
|
||||
meta:set_int("x0",0);meta:set_int("y0",0);meta:set_int("z0",0); -- target
|
||||
meta:set_int("r",5); meta:set_string("skybox","default");
|
||||
meta:set_float("speed",1);
|
||||
meta:set_float("jump",1);
|
||||
meta:set_float("g",1);
|
||||
meta:set_int("sneak",1);
|
||||
meta:set_int("admin",0);
|
||||
local name = placer:get_player_name();
|
||||
meta:set_string("owner",name);
|
||||
local privs = minetest.get_player_privs(name);
|
||||
if privs.privs then meta:set_int("admin",1) end
|
||||
if privs.machines then meta:set_int("machines",1) end
|
||||
|
||||
local inv = meta:get_inventory();
|
||||
inv:set_size("fuel",1*1);
|
||||
|
||||
enviro_update_form(pos);
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = function (pos, node,ttl)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local machines = meta:get_int("machines");
|
||||
if not machines == 1 then meta:set_string("infotext","Error. You need machines privs.") return end
|
||||
|
||||
local admin = meta:get_int("admin");
|
||||
|
||||
local inv = meta:get_inventory(); local stack = ItemStack("default:diamond 1");
|
||||
|
||||
if inv:contains_item("fuel", stack) then
|
||||
inv:remove_item("fuel", stack);
|
||||
else
|
||||
meta:set_string("infotext","Error. Insert diamond in fuel inventory")
|
||||
return
|
||||
end
|
||||
|
||||
local x0,y0,z0,r,skybox,speed,jump,g,sneak;
|
||||
x0=meta:get_int("x0"); y0=meta:get_int("y0");z0=meta:get_int("z0"); -- target
|
||||
r= meta:get_int("r",5); skybox=meta:get_string("skybox");
|
||||
speed=meta:get_float("speed");jump= meta:get_float("jump");
|
||||
g=meta:get_float("g");sneak=meta:get_int("sneak"); if sneak~=0 then sneak = true else sneak = false end
|
||||
|
||||
local players = minetest.get_connected_players();
|
||||
for _,player in pairs(players) do
|
||||
local pos1 = player:getpos();
|
||||
local dist = math.sqrt((pos1.x-pos.x)^2 + (pos1.y-pos.y)^2 + (pos1.z-pos.z)^2 );
|
||||
if dist<=r then
|
||||
|
||||
player:set_physics_override({speed=speed,jump=jump,gravity=g,sneak=sneak})
|
||||
|
||||
if admin == 1 then -- only admin can change skybox
|
||||
local sky = enviro.skyboxes[skybox];
|
||||
player:set_sky(0,sky["type"],sky["tex"]);
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- attempt to set acceleration to balls, if any around
|
||||
local objects = minetest.get_objects_inside_radius(pos, r)
|
||||
|
||||
for _,obj in pairs(objects) do
|
||||
if obj:get_luaentity() then
|
||||
local obj_name = obj:get_luaentity().name or ""
|
||||
if obj_name == "basic_machines:ball" then
|
||||
obj:setacceleration({x=0,y=-g,z=0});
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
|
||||
local name = sender:get_player_name();if minetest.is_protected(pos,name) then return end
|
||||
|
||||
if fields.OK then
|
||||
local privs = minetest.get_player_privs(sender:get_player_name());
|
||||
local meta = minetest.get_meta(pos);
|
||||
local x0=0; local y0=0; local z0=0;
|
||||
--minetest.chat_send_all("form at " .. dump(pos) .. " fields " .. dump(fields))
|
||||
if fields.x0 then x0 = tonumber(fields.x0) or 0 end
|
||||
if fields.y0 then y0 = tonumber(fields.y0) or 0 end
|
||||
if fields.z0 then z0 = tonumber(fields.z0) or 0 end
|
||||
if not privs.privs and (math.abs(x0)>10 or math.abs(y0)>10 or math.abs(z0) > 10) then return end
|
||||
|
||||
meta:set_int("x0",x0);meta:set_int("y0",y0);meta:set_int("z0",z0);
|
||||
if privs.privs then -- only admin can set sky
|
||||
if fields.skybox then meta:set_string("skybox", fields.skybox) end
|
||||
end
|
||||
if fields.r then
|
||||
local r = tonumber(fields.r) or 0;
|
||||
if r > 10 and not privs.privs then return end
|
||||
meta:set_int("r", r)
|
||||
end
|
||||
if fields.g then
|
||||
local g = tonumber(fields.g) or 1;
|
||||
if (g<0.1 or g>40) and not privs.privs then return end
|
||||
meta:set_float("g", g)
|
||||
end
|
||||
if fields.speed then
|
||||
local speed = tonumber(fields.speed) or 1;
|
||||
if (speed>1 or speed < 0) and not privs.privs then return end
|
||||
meta:set_float("speed", speed)
|
||||
end
|
||||
if fields.jump then
|
||||
local jump = tonumber(fields.jump) or 1;
|
||||
if (jump<0 or jump>2) and not privs.privs then return end
|
||||
meta:set_float("jump", jump)
|
||||
end
|
||||
if fields.sneak then
|
||||
meta:set_int("sneak", tonumber(fields.sneak) or 0)
|
||||
end
|
||||
|
||||
|
||||
enviro_update_form(pos);
|
||||
end
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
can_dig = function(pos, player) -- dont dig if fuel is inside, cause it will be destroyed
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
return inv:is_empty("fuel")
|
||||
end,
|
||||
|
||||
})
|
||||
|
||||
|
||||
-- DEFAULT (SPAWN) PHYSICS VALUE/SKYBOX
|
||||
|
||||
local reset_player_physics = function(player)
|
||||
if player then
|
||||
player:set_physics_override({speed=1,jump=1,gravity=1}) -- value set for extreme test space spawn
|
||||
local skybox = enviro.skyboxes["default"]; -- default skybox is "default"
|
||||
player:set_sky(0,skybox["type"],skybox["tex"]);
|
||||
end
|
||||
end
|
||||
|
||||
-- globally available function
|
||||
enviro_adjust_physics = function(player) -- adjust players physics/skybox 1 second after various events
|
||||
minetest.after(1, function()
|
||||
if player then
|
||||
local pos = player:getpos(); if not pos then return end
|
||||
if pos.y > space_start then -- is player in space or not?
|
||||
player:set_physics_override({speed=1,jump=0.5,gravity=0.1}) -- value set for extreme test space spawn
|
||||
local skybox = enviro.skyboxes["space"];
|
||||
player:set_sky(0,skybox["type"],skybox["tex"]);
|
||||
else
|
||||
player:set_physics_override({speed=1,jump=1,gravity=1}) -- value set for extreme test space spawn
|
||||
local skybox = enviro.skyboxes["default"];
|
||||
player:set_sky(0,skybox["type"],skybox["tex"]);
|
||||
end
|
||||
end
|
||||
end)
|
||||
end
|
||||
|
||||
|
||||
-- restore default values/skybox on respawn of player
|
||||
minetest.register_on_respawnplayer(reset_player_physics)
|
||||
|
||||
-- when player joins, check where he is and adjust settings
|
||||
minetest.register_on_joinplayer(enviro_adjust_physics)
|
||||
|
||||
|
||||
-- SERVER GLOBAL SPACE CODE: uncomment to enable it
|
||||
|
||||
local round = math.floor;
|
||||
local protector_position = function(pos)
|
||||
local r = 20;
|
||||
local ry = 2*r;
|
||||
return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r};
|
||||
end
|
||||
|
||||
local stimer = 0
|
||||
local enviro_space = {};
|
||||
minetest.register_globalstep(function(dtime)
|
||||
stimer = stimer + dtime;
|
||||
if stimer >= 5 then
|
||||
stimer = 0;
|
||||
local players = minetest.get_connected_players();
|
||||
for _,player in pairs(players) do
|
||||
local name = player:get_player_name();
|
||||
local pos = player:getpos();
|
||||
local inspace=0; if pos.y>space_start then inspace = 1 end
|
||||
local inspace0=enviro_space[name];
|
||||
if inspace~=inspace0 then -- only adjust player enviroment ONLY if change occured ( earth->space or space->earth !)
|
||||
enviro_space[name] = inspace;
|
||||
enviro_adjust_physics(player);
|
||||
end
|
||||
|
||||
if ENABLE_SPACE_EFFECTS and inspace==1 then -- special space code
|
||||
|
||||
|
||||
if pos.y<1500 and pos.y>1120 then
|
||||
local hp = player:get_hp();
|
||||
|
||||
if hp>0 then
|
||||
minetest.chat_send_player(name,"WARNING: you entered DEADLY RADIATION ZONE");
|
||||
local privs = minetest.get_player_privs(name)
|
||||
if not privs.kick then player:set_hp(hp-15) end
|
||||
end
|
||||
return
|
||||
else
|
||||
|
||||
local ppos = protector_position(pos);
|
||||
local populated = (minetest.get_node(ppos).name=="basic_protect:protector");
|
||||
if populated then
|
||||
if minetest.get_meta(ppos):get_int("space") == 1 then populated = false end
|
||||
end
|
||||
|
||||
if not populated then -- do damage if player found not close to protectors
|
||||
local hp = player:get_hp();
|
||||
local privs = minetest.get_player_privs(name);
|
||||
if hp>0 and not privs.kick then
|
||||
player:set_hp(hp-10); -- dead in 20/10 = 2 events
|
||||
minetest.chat_send_player(name,"WARNING: in space you must stay close to spawn or protected areas");
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
end)
|
||||
|
||||
-- END OF SPACE CODE
|
||||
|
||||
|
||||
-- AIR EXPERIMENT
|
||||
-- minetest.register_node("basic_machines:air", {
|
||||
-- description = "enables breathing in space",
|
||||
-- drawtype = "liquid",
|
||||
-- tiles = {"default_water_source_animated.png"},
|
||||
|
||||
-- drawtype = "glasslike",
|
||||
-- paramtype = "light",
|
||||
-- alpha = 150,
|
||||
-- sunlight_propagates = true, -- Sunlight shines through
|
||||
-- walkable = false, -- Would make the player collide with the air node
|
||||
-- pointable = false, -- You can't select the node
|
||||
-- diggable = false, -- You can't dig the node
|
||||
-- buildable_to = true,
|
||||
-- drop = "",
|
||||
-- groups = {not_in_creative_inventory=1},
|
||||
-- after_place_node = function(pos, placer, itemstack, pointed_thing)
|
||||
-- local r = 3;
|
||||
-- for i = -r,r do
|
||||
-- for j = -r,r do
|
||||
-- for k = -r,r do
|
||||
-- local p = {x=pos.x+i,y=pos.y+j,z=pos.z+k};
|
||||
-- if minetest.get_node(p).name == "air" then
|
||||
-- minetest.set_node(p,{name = "basic_machines:air"})
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
-- end
|
||||
|
||||
-- })
|
||||
|
||||
-- minetest.register_abm({
|
||||
-- nodenames = {"basic_machines:air"},
|
||||
-- neighbors = {"air"},
|
||||
-- interval = 10,
|
||||
-- chance = 1,
|
||||
-- action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
-- minetest.set_node(pos,{name = "air"})
|
||||
-- end
|
||||
-- });
|
||||
|
||||
|
||||
minetest.register_on_punchplayer( -- bring gravity closer to normal with each punch
|
||||
function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage)
|
||||
|
||||
if player:get_physics_override() == nil then return end
|
||||
local pos = player:getpos(); if pos.y>= space_start then return end
|
||||
|
||||
local gravity = player:get_physics_override().gravity;
|
||||
if gravity<1 then
|
||||
gravity = 1;
|
||||
player:set_physics_override({gravity=gravity})
|
||||
end
|
||||
end
|
||||
|
||||
)
|
||||
|
||||
|
||||
|
||||
-- RECIPE: extremely expensive
|
||||
|
||||
-- minetest.register_craft({
|
||||
-- output = "basic_machines:enviro",
|
||||
-- recipe = {
|
||||
-- {"basic_machines:generator", "basic_machines:clockgen","basic_machines:generator"},
|
||||
-- {"basic_machines:generator", "basic_machines:generator","basic_machines:generator"},
|
||||
-- {"basic_machines:generator", "basic_machines:generator", "basic_machines:generator"}
|
||||
-- }
|
||||
-- })
|
|
@ -0,0 +1,364 @@
|
|||
--todo: when grinding multiple items compare battery maxpower with number of items and attempt to grind as much as possible
|
||||
|
||||
-- rnd 2016:
|
||||
|
||||
-- this node works as technic grinder
|
||||
-- There is a certain fuel cost to operate
|
||||
|
||||
-- recipe list: [in] ={fuel cost, out, quantity of material required for processing}
|
||||
basic_machines.grinder_recipes = {
|
||||
["default:stone"] = {2,"default:sand",1},
|
||||
["default:desert_stone"] = {2,"default:desert_sand 4",1},
|
||||
["default:cobble"] = {1,"default:gravel",1},
|
||||
["default:gravel"] = {0.5,"default:dirt",1},
|
||||
["default:dirt"] = {0.5,"default:clay_lump 4",1},
|
||||
["es:aikerum_crystal"] ={16,"es:aikerum_dust 2",1}, -- added for es mod
|
||||
["es:ruby_crystal"] = {16,"es:ruby_dust 2",1},
|
||||
["es:emerald_crystal"] = {16,"es:emerald_dust 2",1},
|
||||
["es:purpellium_lump"] = {16,"es:purpellium_dust 2",1},
|
||||
["default:obsidian_shard"] = {199,"default:lava_source",1},
|
||||
["gloopblocks:basalt"] = {1, "default:cobble",1}, -- enable coble farms with gloopblocks mod
|
||||
["default:ice"] = {1, "default:snow 4",1},
|
||||
["darkage:silt_lump"]={1,"darkage:chalk_powder",1},
|
||||
};
|
||||
|
||||
-- es gems dust cooking
|
||||
local es_gems = function()
|
||||
local es_gems = {
|
||||
{name = "emerald", cooktime = 1200},{name = "ruby", cooktime = 1500},{name = "purpellium", cooktime = 1800},
|
||||
{name = "aikerum", cooktime = 2000}}
|
||||
|
||||
for _,v in pairs(es_gems) do
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
recipe = "es:"..v.name.."_dust",
|
||||
output = "es:"..v.name .."_crystal",
|
||||
cooktime = v.cooktime
|
||||
})
|
||||
end
|
||||
end
|
||||
minetest.after(0,es_gems);
|
||||
|
||||
|
||||
local grinder_process = function(pos)
|
||||
|
||||
local node = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name;
|
||||
local meta = minetest.get_meta(pos);local inv = meta:get_inventory();
|
||||
|
||||
|
||||
-- PROCESS: check out inserted items
|
||||
local stack = inv:get_stack("src",1);
|
||||
if stack:is_empty() then return end; -- nothing to do
|
||||
|
||||
local src_item = stack:to_string();
|
||||
local p=string.find(src_item," "); if p then src_item = string.sub(src_item,1,p-1) else p = 0 end -- take first word to determine what item was
|
||||
|
||||
local def = basic_machines.grinder_recipes[src_item];
|
||||
if not def then
|
||||
meta:set_string("infotext", "please insert valid materials"); return
|
||||
end-- unknown node
|
||||
|
||||
if stack:get_count()< def[3] then
|
||||
meta:set_string("infotext", "Recipe requires at least " .. def[3] .. " " .. src_item);
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- FUEL CHECK
|
||||
local fuel = meta:get_float("fuel");
|
||||
|
||||
|
||||
if fuel-def[1]<0 then -- we need new fuel, check chest below
|
||||
local fuellist = inv:get_list("fuel")
|
||||
if not fuellist then return end
|
||||
|
||||
local fueladd, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
|
||||
|
||||
local supply=0;
|
||||
if fueladd.time == 0 then -- no fuel inserted, try look for outlet
|
||||
-- No valid fuel in fuel list
|
||||
supply = basic_machines.check_power({x=pos.x,y=pos.y-1,z=pos.z} , def[1]) or 0; -- tweaked so 1 coal = 1 energy
|
||||
if supply>0 then
|
||||
fueladd.time = supply -- same as 10 coal
|
||||
else
|
||||
meta:set_string("infotext", "Please insert fuel");
|
||||
return;
|
||||
end
|
||||
else
|
||||
if supply==0 then -- Take fuel from fuel list if no supply available
|
||||
inv:set_stack("fuel",1,afterfuel.items[1])
|
||||
fueladd.time=fueladd.time*0.1/4 -- thats 1 for coal
|
||||
--minetest.chat_send_all("FUEL ADD TIME " .. fueladd.time)
|
||||
end
|
||||
end
|
||||
if fueladd.time>0 then
|
||||
fuel=fuel + fueladd.time
|
||||
meta:set_float("fuel",fuel);
|
||||
meta:set_string("infotext", "added fuel furnace burn time " .. fueladd.time .. ", fuel status " .. fuel);
|
||||
end
|
||||
if fuel-def[1]<0 then
|
||||
meta:set_string("infotext", "need at least " .. def[1]-fuel .. " fuel to complete operation "); return
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- process items
|
||||
|
||||
-- TO DO: check if there is room for item yyy
|
||||
local addstack = ItemStack(def[2]);
|
||||
if inv:room_for_item("dst", addstack) then
|
||||
inv:add_item("dst",addstack);
|
||||
else return
|
||||
end
|
||||
|
||||
--take 1 item from src inventory for each activation
|
||||
stack=stack:take_item(1); inv:remove_item("src", stack)
|
||||
|
||||
minetest.sound_play("grinder", {pos=pos,gain=0.5,max_hear_distance = 16,})
|
||||
|
||||
fuel = fuel-def[1]; -- burn fuel
|
||||
meta:set_float("fuel",fuel);
|
||||
meta:set_string("infotext", "fuel " .. fuel);
|
||||
|
||||
end
|
||||
|
||||
|
||||
local grinder_update_meta = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
|
||||
local form =
|
||||
"size[8,8]".. -- width, height
|
||||
--"size[6,10]".. -- width, height
|
||||
"label[0,0;IN] label[1,0;OUT] label[0,2;FUEL] "..
|
||||
"list["..list_name..";src;0.,0.5;1,1;]"..
|
||||
"list["..list_name..";dst;1.,0.5;3,3;]"..
|
||||
"list["..list_name..";fuel;0.,2.5;1,1;]"..
|
||||
"list[current_player;main;0,4;8,4;]"..
|
||||
"button[6.5,0.5;1,1;OK;OK]"..
|
||||
"button[6.5,1.5;1,1;help;help]"..
|
||||
"listring["..list_name..";dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring["..list_name..";src]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring["..list_name..";fuel]"..
|
||||
"listring[current_player;main]"
|
||||
meta:set_string("formspec", form)
|
||||
end
|
||||
|
||||
minetest.register_node("basic_machines:grinder", {
|
||||
description = "Grinder",
|
||||
tiles = {"grinder.png"},
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_string("infotext", "Grinder: To operate it insert fuel, then insert item to grind or use keypad to activate it.")
|
||||
meta:set_string("owner", placer:get_player_name());
|
||||
meta:set_float("fuel",0);
|
||||
local inv = meta:get_inventory();inv:set_size("src", 1);inv:set_size("dst",9);inv:set_size("fuel",1);
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler
|
||||
grinder_update_meta(pos);
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if listname =="dst" then return end
|
||||
grinder_process(pos);
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
return 0;
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = function (pos, node,ttl)
|
||||
if type(ttl)~="number" then ttl = 1 end
|
||||
if ttl<0 then return end -- machines_TTL prevents infinite recursion
|
||||
grinder_process(pos);
|
||||
end
|
||||
}
|
||||
},
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
if fields.quit then return end
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
if fields.help then
|
||||
--recipe list: [in] ={fuel cost, out, quantity of material required for processing}
|
||||
--basic_machines.grinder_recipes
|
||||
local text = "RECIPES\n\n";
|
||||
for key,v in pairs(basic_machines.grinder_recipes) do
|
||||
text = text .. "INPUT ".. key .. " " .. v[3] .. " OUTPUT " .. v[2] .. "\n"
|
||||
end
|
||||
|
||||
local form = "size [6,7] textarea[0,0;6.5,8.5;grinderhelp;GRINDER RECIPES;".. text.."]"
|
||||
minetest.show_formspec(sender:get_player_name(), "grinderhelp", form)
|
||||
|
||||
end
|
||||
grinder_update_meta(pos);
|
||||
end,
|
||||
|
||||
can_dig = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
|
||||
if not (inv:is_empty("fuel")) or not (inv:is_empty("src")) or not (inv:is_empty("dst")) then return false end -- all inv must be empty to be dug
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
})
|
||||
|
||||
|
||||
-- minetest.register_craft({
|
||||
-- output = "basic_machines:grinder",
|
||||
-- recipe = {
|
||||
-- {"default:diamond","default:mese","default:diamond"},
|
||||
-- {"default:mese","default:diamondblock","default:mese"},
|
||||
-- {"default:diamond","default:mese","default:diamond"},
|
||||
|
||||
-- }
|
||||
-- })
|
||||
|
||||
|
||||
|
||||
|
||||
-- REGISTER DUSTS
|
||||
|
||||
|
||||
local function register_dust(name,input_node_name,ingot,grindcost,cooktime,R,G,B)
|
||||
|
||||
if not R then R = "FF" end
|
||||
if not G then G = "FF" end
|
||||
if not B then B = "FF" end
|
||||
|
||||
local purity_table = {"33","66"};
|
||||
|
||||
for i = 1,#purity_table do
|
||||
local purity = purity_table[i];
|
||||
minetest.register_craftitem("basic_machines:"..name.."_dust_".. purity, {
|
||||
description = name.. " dust purity " .. purity .. "%" ,
|
||||
inventory_image = "basic_machines_dust.png^[colorize:#"..R..G..B..":180",
|
||||
})
|
||||
end
|
||||
|
||||
basic_machines.grinder_recipes[input_node_name] = {grindcost,"basic_machines:"..name.."_dust_".. purity_table[1].." 2",1} -- register grinder recipe
|
||||
|
||||
if ingot~="" then
|
||||
|
||||
for i = 1,#purity_table-1 do
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
recipe = "basic_machines:"..name.."_dust_".. purity_table[i],
|
||||
output = "basic_machines:"..name.."_dust_".. purity_table[i+1],
|
||||
cooktime = cooktime
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
recipe = "basic_machines:"..name.."_dust_".. purity_table[#purity_table],
|
||||
groups = {not_in_creative_inventory=1},
|
||||
output = ingot,
|
||||
cooktime = cooktime
|
||||
})
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
register_dust("iron","default:iron_lump","default:steel_ingot",4,8,"99","99","99")
|
||||
register_dust("copper","default:copper_lump","default:copper_ingot",4,8,"C8","80","0D") --c8800d
|
||||
register_dust("tin","default:tin_lump","default:tin_ingot",4,8,"9F","9F","9F")
|
||||
register_dust("gold","default:gold_lump","default:gold_ingot",6,25,"FF","FF","00")
|
||||
|
||||
-- grinding ingots gives dust too
|
||||
basic_machines.grinder_recipes["default:steel_ingot"] = {4,"basic_machines:iron_dust_33 2",1};
|
||||
basic_machines.grinder_recipes["default:copper_ingot"] = {4,"basic_machines:copper_dust_33 2",1};
|
||||
basic_machines.grinder_recipes["default:gold_ingot"] = {6,"basic_machines:gold_dust_33 2",1};
|
||||
basic_machines.grinder_recipes["default:tin_ingot"] = {4,"basic_machines:tin_dust_33 2",1};
|
||||
|
||||
-- are moreores (tin, silver, mithril) present?
|
||||
|
||||
local table = minetest.registered_items["moreores:tin_lump"]; if table then
|
||||
--register_dust("tin","moreores:tin_lump","moreores:tin_ingot",4,8,"FF","FF","FF")
|
||||
register_dust("silver","moreores:silver_lump","moreores:silver_ingot",5,15,"BB","BB","BB")
|
||||
register_dust("mithril","moreores:mithril_lump","moreores:mithril_ingot",16,750,"00","00","FF")
|
||||
|
||||
basic_machines.grinder_recipes["moreores:tin_ingot"] = {4,"basic_machines:tin_dust_33 2",1};
|
||||
basic_machines.grinder_recipes["moreores:silver_ingot"] = {5,"basic_machines:silver_dust_33 2",1};
|
||||
basic_machines.grinder_recipes["moreores:mithril_ingot"] = {16,"basic_machines:mithril_dust_33 2",1};
|
||||
end
|
||||
|
||||
|
||||
register_dust("mese","default:mese_crystal","default:mese_crystal",8,250,"CC","CC","00")
|
||||
register_dust("diamond","default:diamond","default:diamond",16,500,"00","EE","FF") -- 0.3hr cooking time to make diamond!
|
||||
|
||||
-- darkage recipes and ice
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
recipe = "default:ice",
|
||||
output = "default:water_flowing",
|
||||
cooktime = 4
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
recipe = "default:stone",
|
||||
output = "darkage:basalt",
|
||||
cooktime = 60
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
recipe = "darkage:slate",
|
||||
output = "darkage:schist",
|
||||
cooktime = 20
|
||||
})
|
||||
|
||||
-- dark age recipe: cook schist to get gneiss
|
||||
|
||||
minetest.register_craft({
|
||||
type = "cooking",
|
||||
recipe = "darkage:gneiss",
|
||||
output = "darkage:marble",
|
||||
cooktime = 20
|
||||
})
|
||||
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
output = "darkage:serpentine",
|
||||
recipe = {
|
||||
{"darkage:marble","default:cactus"}
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "darkage:mud",
|
||||
recipe = {
|
||||
{"default:dirt","default:water_flowing"}
|
||||
}
|
||||
})
|
|
@ -0,0 +1,91 @@
|
|||
-- BASIC_MACHINES: lightweight automation mod for minetest
|
||||
-- minetest 0.4.14
|
||||
-- (c) 2015-2016 rnd
|
||||
|
||||
-- This program is free software: you can redistribute it and/or modify
|
||||
-- it under the terms of the GNU General Public License as published by
|
||||
-- the Free Software Foundation, either version 3 of the License, or
|
||||
-- (at your option) any later version.
|
||||
|
||||
-- This program is distributed in the hope that it will be useful,
|
||||
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
-- GNU General Public License for more details.
|
||||
|
||||
-- You should have received a copy of the GNU General Public License
|
||||
-- along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
basic_machines = {};
|
||||
|
||||
|
||||
|
||||
dofile(minetest.get_modpath("basic_machines").."/mark.lua") -- used for markings, borrowed and adapted from worldedit mod
|
||||
dofile(minetest.get_modpath("basic_machines").."/mover.lua") -- mover, detector, keypad, distributor
|
||||
dofile(minetest.get_modpath("basic_machines").."/technic_power.lua") -- technic power for mover
|
||||
dofile(minetest.get_modpath("basic_machines").."/recycler.lua") -- recycle old used tools
|
||||
dofile(minetest.get_modpath("basic_machines").."/grinder.lua") -- grind materials into dusts
|
||||
dofile(minetest.get_modpath("basic_machines").."/autocrafter.lua") -- borrowed and adapted from pipeworks mod
|
||||
dofile(minetest.get_modpath("basic_machines").."/constructor.lua") -- enable crafting of all machines
|
||||
|
||||
dofile(minetest.get_modpath("basic_machines").."/protect.lua") -- enable interaction with players, adds local on protect/chat event handling
|
||||
|
||||
-- OPTIONAL ADDITIONAL STUFF ( comment to disable )
|
||||
|
||||
dofile(minetest.get_modpath("basic_machines").."/ball.lua") -- interactive flying ball, can activate blocks or be used as a weapon
|
||||
dofile(minetest.get_modpath("basic_machines").."/enviro.lua") -- enviro blocks that can change surrounding enviroment physics, uncomment spawn/join code to change global physics, disabled by default
|
||||
minetest.after(0, function()
|
||||
dofile(minetest.get_modpath("basic_machines").."/mesecon_doors.lua") -- if you want open/close doors with signal, also steel doors are made impervious to dig through, removal by repeat punch
|
||||
dofile(minetest.get_modpath("basic_machines").."/mesecon_lights.lua") -- adds ability for other light blocks to toggle light
|
||||
end)
|
||||
|
||||
|
||||
-- MACHINE PRIVILEGE
|
||||
minetest.register_privilege("machines", {
|
||||
description = "Player is expert basic_machine user: his machines work while not present on server, can spawn more than 2 balls at once",
|
||||
})
|
||||
|
||||
-- machines fuel related recipes
|
||||
-- CHARCOAL
|
||||
|
||||
minetest.register_craftitem("basic_machines:charcoal", {
|
||||
description = "Wood charcoal",
|
||||
inventory_image = "charcoal.png",
|
||||
})
|
||||
|
||||
|
||||
minetest.register_craft({
|
||||
type = 'cooking',
|
||||
recipe = "default:tree",
|
||||
cooktime = 30,
|
||||
output = "basic_machines:charcoal",
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
output = "default:coal_lump",
|
||||
recipe = {
|
||||
{"basic_machines:charcoal"},
|
||||
{"basic_machines:charcoal"},
|
||||
}
|
||||
})
|
||||
|
||||
minetest.register_craft({
|
||||
type = "fuel",
|
||||
recipe = "basic_machines:charcoal",
|
||||
-- note: to make it you need to use 1 tree block for fuel + 1 tree block, thats 2, caloric value 2*30=60
|
||||
burntime = 40, -- coal lump has 40, tree block 30, coal block 370 (9*40=360!)
|
||||
})
|
||||
|
||||
-- add since minetest doesnt have moreores tin ingot recipe
|
||||
minetest.register_craft({
|
||||
output = "default:tin_ingot",
|
||||
recipe = {
|
||||
{"moreores:tin_ingot"},
|
||||
}
|
||||
})
|
||||
|
||||
-- COMPATIBILITY
|
||||
minetest.register_alias("basic_machines:battery", "basic_machines:battery_0")
|
||||
|
||||
|
||||
print("[MOD] basic_machines " .. basic_machines.version .. " loaded.")
|
|
@ -0,0 +1,154 @@
|
|||
-- rnd: code borrowed from machines, mark.lua
|
||||
|
||||
-- need for marking
|
||||
machines = {};
|
||||
|
||||
machines.pos1 = {};machines.pos11 = {}; machines.pos2 = {};
|
||||
machines.marker1 = {}
|
||||
machines.marker11 = {}
|
||||
machines.marker2 = {}
|
||||
machines.marker_region = {}
|
||||
|
||||
|
||||
--marks machines region position 1
|
||||
machines.mark_pos1 = function(name)
|
||||
local pos1, pos2 = machines.pos1[name], machines.pos2[name]
|
||||
|
||||
if pos1 ~= nil then
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos1, pos1)
|
||||
end
|
||||
|
||||
if not machines[name] then machines[name]={} end
|
||||
machines[name].timer = 10;
|
||||
if machines.marker1[name] ~= nil then --marker already exists
|
||||
machines.marker1[name]:remove() --remove marker
|
||||
machines.marker1[name] = nil
|
||||
end
|
||||
if pos1 ~= nil then
|
||||
--add marker
|
||||
machines.marker1[name] = minetest.add_entity(pos1, "machines:pos1")
|
||||
if machines.marker1[name] ~= nil then
|
||||
machines.marker1[name]:get_luaentity().name = name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--marks machines region position 1
|
||||
machines.mark_pos11 = function(name)
|
||||
local pos11 = machines.pos11[name];
|
||||
|
||||
if pos11 ~= nil then
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos11, pos11)
|
||||
end
|
||||
|
||||
if not machines[name] then machines[name]={} end
|
||||
machines[name].timer = 10;
|
||||
if machines.marker11[name] ~= nil then --marker already exists
|
||||
machines.marker11[name]:remove() --remove marker
|
||||
machines.marker11[name] = nil
|
||||
end
|
||||
if pos11 ~= nil then
|
||||
--add marker
|
||||
machines.marker11[name] = minetest.add_entity(pos11, "machines:pos11")
|
||||
if machines.marker11[name] ~= nil then
|
||||
machines.marker11[name]:get_luaentity().name = name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
--marks machines region position 2
|
||||
machines.mark_pos2 = function(name)
|
||||
local pos1, pos2 = machines.pos1[name], machines.pos2[name]
|
||||
|
||||
if pos2 ~= nil then
|
||||
--make area stay loaded
|
||||
local manip = minetest.get_voxel_manip()
|
||||
manip:read_from_map(pos2, pos2)
|
||||
end
|
||||
|
||||
if not machines[name] then machines[name]={} end
|
||||
machines[name].timer = 10;
|
||||
if machines.marker2[name] ~= nil then --marker already exists
|
||||
machines.marker2[name]:remove() --remove marker
|
||||
machines.marker2[name] = nil
|
||||
end
|
||||
if pos2 ~= nil then
|
||||
--add marker
|
||||
machines.marker2[name] = minetest.add_entity(pos2, "machines:pos2")
|
||||
if machines.marker2[name] ~= nil then
|
||||
machines.marker2[name]:get_luaentity().name = name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
minetest.register_entity(":machines:pos1", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"machines_pos1.png", "machines_pos1.png",
|
||||
"machines_pos1.png", "machines_pos1.png",
|
||||
"machines_pos1.png", "machines_pos1.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
physical = false,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if not machines[self.name] then machines[self.name]={}; machines[self.name].timer = 10 end
|
||||
machines[self.name].timer = machines[self.name].timer - dtime
|
||||
if machines[self.name].timer<=0 or machines.marker1[self.name] == nil then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
self.object:remove()
|
||||
machines.marker1[self.name] = nil
|
||||
machines[self.name].timer = 10
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity(":machines:pos11", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"machines_pos11.png", "machines_pos11.png",
|
||||
"machines_pos11.png", "machines_pos11.png",
|
||||
"machines_pos11.png", "machines_pos11.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
physical = false,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if not machines[self.name] then machines[self.name]={}; machines[self.name].timer = 10 end
|
||||
machines[self.name].timer = machines[self.name].timer - dtime
|
||||
if machines[self.name].timer<=0 or machines.marker11[self.name] == nil then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
on_punch = function(self, hitter)
|
||||
self.object:remove()
|
||||
machines.marker11[self.name] = nil
|
||||
machines[self.name].timer = 10
|
||||
end,
|
||||
})
|
||||
|
||||
minetest.register_entity(":machines:pos2", {
|
||||
initial_properties = {
|
||||
visual = "cube",
|
||||
visual_size = {x=1.1, y=1.1},
|
||||
textures = {"machines_pos2.png", "machines_pos2.png",
|
||||
"machines_pos2.png", "machines_pos2.png",
|
||||
"machines_pos2.png", "machines_pos2.png"},
|
||||
collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55},
|
||||
physical = false,
|
||||
},
|
||||
on_step = function(self, dtime)
|
||||
if not machines[self.name] then machines[self.name]={}; machines[self.name].timer = 10 end
|
||||
if machines[self.name].timer<=0 or machines.marker2[self.name] == nil then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
})
|
|
@ -0,0 +1,102 @@
|
|||
-- make doors open/close with signal
|
||||
local function door_signal_overwrite(name)
|
||||
local table = minetest.registered_nodes[name]; if not table then return end
|
||||
local table2 = {}
|
||||
for i,v in pairs(table) do table2[i] = v end
|
||||
|
||||
-- 0.4.15: line 370 in doors defines thins
|
||||
local door_on_rightclick = table2.on_rightclick;
|
||||
|
||||
-- this will make door toggle whenever its used
|
||||
table2.mesecons = {effector = {
|
||||
action_on = function (pos,node)
|
||||
local meta = minetest.get_meta(pos);local name = meta:get_string("owner");
|
||||
-- create virtual player
|
||||
local clicker = {};
|
||||
function clicker:get_wielded_item()
|
||||
local item = {}
|
||||
function item:get_name() return "" end
|
||||
return item
|
||||
end
|
||||
function clicker:get_player_name() return name end; -- define method get_player_name() returning owner name so that we can call on_rightclick function in door
|
||||
function clicker:is_player() return true end; -- method needed for mods that check this: like denaid areas mod
|
||||
if door_on_rightclick then door_on_rightclick(pos, nil, clicker,ItemStack(""),{}) end -- safety if it doesnt exist
|
||||
--minetest.swap_node(pos, {name = "protector:trapdoor", param1 = node.param1, param2 = node.param2}) -- more direct approach?, need to set param2 then too
|
||||
end
|
||||
}
|
||||
};
|
||||
|
||||
minetest.register_node(":"..name, table2)
|
||||
end
|
||||
|
||||
minetest.after(0,function()
|
||||
door_signal_overwrite("doors:door_wood");door_signal_overwrite("doors:door_steel")
|
||||
door_signal_overwrite("doors:door_wood_a");door_signal_overwrite("doors:door_wood_b");
|
||||
door_signal_overwrite("doors:door_steel_a");door_signal_overwrite("doors:door_steel_b");
|
||||
door_signal_overwrite("doors:door_glass_a");door_signal_overwrite("doors:door_glass_b");
|
||||
door_signal_overwrite("doors:door_obsidian_glass_a");door_signal_overwrite("doors:door_obsidian_glass_b");
|
||||
|
||||
door_signal_overwrite("doors:trapdoor");door_signal_overwrite("doors:trapdoor_open");
|
||||
door_signal_overwrite("doors:trapdoor_steel");door_signal_overwrite("doors:trapdoor_steel_open");
|
||||
|
||||
end
|
||||
);
|
||||
|
||||
local function make_it_noclip(name)
|
||||
|
||||
local table = minetest.registered_nodes[name]; if not table then return end
|
||||
local table2 = {}
|
||||
for i,v in pairs(table) do
|
||||
table2[i] = v
|
||||
end
|
||||
table2.walkable = false; -- cant be walked on
|
||||
minetest.register_node(":"..name, table2)
|
||||
end
|
||||
|
||||
minetest.after(0,function()
|
||||
make_it_noclip("doors:trapdoor_open");
|
||||
make_it_noclip("doors:trapdoor_steel_open");
|
||||
end);
|
||||
|
||||
|
||||
|
||||
local function make_it_nondiggable_but_removable(name, dropname)
|
||||
|
||||
local table = minetest.registered_nodes[name]; if not table then return end
|
||||
local table2 = {}
|
||||
for i,v in pairs(table) do
|
||||
table2[i] = v
|
||||
end
|
||||
table2.groups.level = 99; -- cant be digged, but it can be removed by owner or if not protected
|
||||
table2.on_punch = function(pos, node, puncher, pointed_thing) -- remove node if owner repeatedly punches it 3x
|
||||
local pname = puncher:get_player_name();
|
||||
local meta = minetest.get_meta(pos);
|
||||
local owner = meta:get_string("doors_owner")
|
||||
if pname==owner or not minetest.is_protected(pos,pname) then -- can be dug by owner or if unprotected
|
||||
local t0 = meta:get_int("punchtime");local count = meta:get_int("punchcount");
|
||||
local t = minetest.get_gametime();
|
||||
|
||||
if t-t0<2 then count = (count +1 ) % 3 else count = 0 end
|
||||
|
||||
if count == 1 then
|
||||
minetest.chat_send_player(pname, "#steel door: punch me one more time to remove me");
|
||||
end
|
||||
if count == 2 then -- remove steel door and drop it
|
||||
minetest.set_node(pos, {name = "air"});
|
||||
local stack = ItemStack(dropname);minetest.add_item(pos,stack)
|
||||
end
|
||||
|
||||
meta:set_int("punchcount",count);meta:set_int("punchtime",t);
|
||||
--minetest.chat_send_all("punch by "..name .. " count " .. count)
|
||||
end
|
||||
end
|
||||
minetest.register_node(":"..name, table2)
|
||||
end
|
||||
|
||||
minetest.after(0,function()
|
||||
make_it_nondiggable_but_removable("doors:door_steel_a","doors:door_steel");
|
||||
make_it_nondiggable_but_removable("doors:door_steel_b","doors:door_steel");
|
||||
|
||||
make_it_nondiggable_but_removable("doors:trapdoor_steel","doors:trapdoor_steel");
|
||||
make_it_nondiggable_but_removable("doors:trapdoor_steel_open","doors:trapdoor_steel");
|
||||
end);
|
|
@ -0,0 +1,50 @@
|
|||
-- make other light blocks work with mesecon signals - can toggle on/off
|
||||
|
||||
local function enable_toggle_light(name)
|
||||
|
||||
local table = minetest.registered_nodes[name]; if not table then return end
|
||||
local table2 = {}
|
||||
for i,v in pairs(table) do
|
||||
table2[i] = v
|
||||
end
|
||||
|
||||
if table2.mesecons then return end -- we dont want to overwrite existing stuff!
|
||||
|
||||
local offname = "basic_machines:"..string.gsub(name, ":", "_").. "_OFF";
|
||||
|
||||
table2.mesecons = {effector = { -- action to toggle light off
|
||||
action_off = function (pos,node,ttl)
|
||||
minetest.swap_node(pos,{name = offname});
|
||||
end
|
||||
}
|
||||
};
|
||||
minetest.register_node(":"..name, table2) -- redefine item
|
||||
|
||||
-- STRANGE BUG1: if you dont make new table table3 and reuse table2 definition original node (definition one line above) is changed by below code too!???
|
||||
-- STRANGE BUG2: if you dont make new table3.. original node automatically changes to OFF node when placed ????
|
||||
|
||||
local table3 = {}
|
||||
for i,v in pairs(table) do
|
||||
table3[i] = v
|
||||
end
|
||||
|
||||
table3.light_source = 0; -- off block has light off
|
||||
table3.mesecons = {effector = {
|
||||
action_on = function (pos,node,ttl)
|
||||
minetest.swap_node(pos,{name = name});
|
||||
end
|
||||
}
|
||||
};
|
||||
|
||||
-- REGISTER OFF BLOCK
|
||||
minetest.register_node(":"..offname, table3);
|
||||
|
||||
end
|
||||
|
||||
|
||||
enable_toggle_light("xdecor:wooden_lightbox");
|
||||
enable_toggle_light("xdecor:iron_lightbox");
|
||||
enable_toggle_light("moreblocks:slab_meselamp_1");
|
||||
enable_toggle_light("moreblocks:slab_super_glow_glass");
|
||||
|
||||
enable_toggle_light("darkage:lamp");
|
|
@ -0,0 +1,24 @@
|
|||
outlet -> change to battery
|
||||
|
||||
battery: craft with meseblock and 3 steel and 1 diamond
|
||||
-upgradeable, start with 10 storage units, each upgrade requires 1.25x more mese blocks and adds 10 storage units ( 1 unit = 1 coal lump)??
|
||||
-display will use entity to show battery bar status on side of battery - determined by where player stands when placing it ( only if there is air there)
|
||||
-battery can be refueled using any valid furnace fuel or by power cells/rods - on fuel insert it will instantly convert to energy, if there is enough energy
|
||||
storage ofc
|
||||
-on activation it will power furnace on top of it, just like outlet does now
|
||||
-when digged and put in invetory it should retain its upgrade info
|
||||
|
||||
movers: instead of looking for locked chest with fuel it will look for nearby battery
|
||||
|
||||
power plant block: costly to make: 8 diamondblocks, battery in center
|
||||
-upgradeable, each diamondblock+lava_bucket will add ammount of energy cells produced in cycle ( 5 seconds) and increase its internal energy storage capacity
|
||||
initially produces 1 power cell, storage capacity 10, with each level of upgrade it adds 1 power cell, 10 storage capacity
|
||||
when it generates power it will also fill the battery on top of it ( if any), speed of refill equalls speed of power generation, it will put all energy
|
||||
possible in battery until is filled, will make recharge sound - electric discharge like in factorio game?
|
||||
|
||||
|
||||
power cell: produced by power plant, have caloric value of 1 coal lump
|
||||
9 power cells craft 1 power cell block: caloric value of coal block
|
||||
9 power cell blocks 1 power rod
|
||||
|
||||
machines will check power: if battery not sufficient capacity for operation it wont work ( it should be able to do at least 10 operations with its capacity)
|
|
@ -0,0 +1,54 @@
|
|||
-- adds event handler for attempt to dig in protected area
|
||||
|
||||
-- tries to activate specially configured nearby distributor at points with coordinates of form 20i, registers dig attempts in radius 10 around
|
||||
-- distributor must have first target filter set to 0 ( disabled ) to handle dig events
|
||||
|
||||
local old_is_protected = minetest.is_protected
|
||||
local round = math.floor;
|
||||
local machines_TTL = basic_machines.machines_TTL or 16
|
||||
|
||||
function minetest.is_protected(pos, digger)
|
||||
|
||||
local is_protected = old_is_protected(pos, digger);
|
||||
if is_protected then -- only if protected
|
||||
local r = 20;local p = {x=round(pos.x/r+0.5)*r,y=round(pos.y/r+0.5)*r+1,z=round(pos.z/r+0.5)*r}
|
||||
if minetest.get_node(p).name == "basic_machines:distributor" then -- attempt to activate distributor at special grid location: coordinates of the form 10+20*i
|
||||
local meta = minetest.get_meta(p);
|
||||
if meta:get_int("active1") == 0 then -- first output is disabled, indicating ready to be used as event handler
|
||||
if meta:get_int("x1") ~= 0 then -- trigger protection event
|
||||
meta:set_string("infotext",digger); -- record diggers name onto distributor
|
||||
local table = minetest.registered_nodes["basic_machines:distributor"];
|
||||
local effector=table.mesecons.effector;
|
||||
local node = nil;
|
||||
effector.action_on(p,node,machines_TTL);
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return is_protected;
|
||||
|
||||
end
|
||||
|
||||
minetest.register_on_chat_message(function(name, message)
|
||||
local player = minetest.get_player_by_name(name);
|
||||
if not player then return end
|
||||
local pos = player:getpos();
|
||||
local r = 20;local p = {x=round(pos.x/r+0.5)*r,y=round(pos.y/r+0.5)*r+1,z=round(pos.z/r+0.5)*r}
|
||||
--minetest.chat_send_all(minetest.pos_to_string(p))
|
||||
if minetest.get_node(p).name == "basic_machines:distributor" then -- attempt to activate distributor at special grid location: coordinates of the form 20*i
|
||||
local meta = minetest.get_meta(p);
|
||||
if meta:get_int("active1") == 0 then -- first output is disabled, indicating ready to be used as event handler
|
||||
local y1 = meta:get_int("y1");
|
||||
if y1 ~= 0 then -- chat event, positive relays message, negative drops it
|
||||
meta:set_string("infotext",message); -- record diggers message
|
||||
local table = minetest.registered_nodes["basic_machines:distributor"];
|
||||
local effector=table.mesecons.effector;
|
||||
local node = nil;
|
||||
effector.action_on(p,node,machines_TTL);
|
||||
if y1<0 then return true
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
)
|
|
@ -0,0 +1,255 @@
|
|||
-- rnd 2015:
|
||||
|
||||
-- this node works as a reverse of crafting process with a 25% loss of items (aka recycling). You can select which recipe to use when recycling.
|
||||
-- There is a fuel cost to recycle
|
||||
|
||||
-- prevent unrealistic recyclings
|
||||
local no_recycle_list = {
|
||||
["default:steel_ingot"]=1,["default:copper_ingot"]=1,["default:bronze_ingot"]=1,["default:gold_ingot"]=1,
|
||||
["dye:white"]=1,["dye:grey"]=1,["dye:dark_grey"]=1,["dye:black"]=1,
|
||||
["dye:violet"]=1,["dye:blue"]=1,["dye:cyan"]=1,["dye:dark_green"]=1,
|
||||
["dye:green"]=1,["dye:yellow"]=1,["dye:brown"]=1,["dye:orange"]=1,
|
||||
["dye:red"]=1,["dye:magenta"]=1,["dye:pink"]=1,
|
||||
}
|
||||
|
||||
|
||||
local recycler_process = function(pos)
|
||||
|
||||
local node = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name;
|
||||
local meta = minetest.get_meta(pos);local inv = meta:get_inventory();
|
||||
|
||||
-- FUEL CHECK
|
||||
local fuel = meta:get_float("fuel");
|
||||
|
||||
if fuel-1<0 then -- we need new fuel, check chest below
|
||||
local fuellist = inv:get_list("fuel")
|
||||
if not fuellist then return end
|
||||
|
||||
local fueladd, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
|
||||
|
||||
local supply=0;
|
||||
if fueladd.time == 0 then -- no fuel inserted, try look for outlet
|
||||
-- No valid fuel in fuel list
|
||||
supply = basic_machines.check_power({x=pos.x,y=pos.y-1,z=pos.z},1) or 0;
|
||||
if supply>0 then
|
||||
fueladd.time = 40*supply -- same as 10 coal
|
||||
else
|
||||
meta:set_string("infotext", "Please insert fuel.");
|
||||
return;
|
||||
end
|
||||
else
|
||||
if supply==0 then -- Take fuel from fuel list if no supply available
|
||||
inv:set_stack("fuel", 1, afterfuel.items[1])
|
||||
fueladd.time = fueladd.time*0.1; -- thats 4 for coal
|
||||
end
|
||||
end
|
||||
if fueladd.time>0 then
|
||||
fuel=fuel + fueladd.time
|
||||
meta:set_float("fuel",fuel);
|
||||
meta:set_string("infotext", "added fuel furnace burn time " .. fueladd.time .. ", fuel status " .. fuel);
|
||||
end
|
||||
if fuel-1<0 then return end
|
||||
end
|
||||
|
||||
|
||||
|
||||
-- RECYCLING: check out inserted items
|
||||
local stack = inv:get_stack("src",1);
|
||||
if stack:is_empty() then return end; -- nothing to do
|
||||
|
||||
local src_item = stack:to_string();
|
||||
local p=string.find(src_item," "); if p then src_item = string.sub(src_item,1,p-1) end -- take first word to determine what item was
|
||||
|
||||
-- look if we already handled this item
|
||||
local known_recipe=true;
|
||||
if src_item~=meta:get_string("node") then-- did we already handle this? if yes read from cache
|
||||
meta:set_string("node",src_item);
|
||||
meta:set_string("itemlist","{}");
|
||||
meta:set_int("reqcount",0);
|
||||
known_recipe=false;
|
||||
end
|
||||
|
||||
local itemlist, reqcount;
|
||||
reqcount = 1; -- needed count of materials for recycle to work
|
||||
|
||||
if not known_recipe then
|
||||
|
||||
if no_recycle_list[src_item] then meta:set_string("node","") return end -- dont allow recycling of forbidden items
|
||||
|
||||
local recipe = minetest.get_all_craft_recipes( src_item );
|
||||
local recipe_id = tonumber(meta:get_int("recipe")) or 1;
|
||||
|
||||
if not recipe then
|
||||
return
|
||||
else
|
||||
itemlist = recipe[recipe_id];
|
||||
if not itemlist then meta:set_string("node","") return end;
|
||||
itemlist=itemlist.items;
|
||||
end
|
||||
local output = recipe[recipe_id].output or "";
|
||||
if string.find(output," ") then
|
||||
local par = string.find(output," ");
|
||||
--if (tonumber(string.sub(output, par)) or 0)>1 then itemlist = {} end
|
||||
|
||||
if par then
|
||||
reqcount = tonumber(string.sub(output, par)) or 1;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
meta:set_string("itemlist",minetest.serialize(itemlist)); -- read cached itemlist
|
||||
meta:set_int("reqcount",reqcount);
|
||||
else
|
||||
itemlist=minetest.deserialize(meta:get_string("itemlist")) or {};
|
||||
reqcount = meta:get_int("reqcount") or 1;
|
||||
end
|
||||
|
||||
if stack:get_count()<reqcount then
|
||||
meta:set_string("infotext", "at least " .. reqcount .. " of " .. src_item .. " is needed ");
|
||||
return
|
||||
end
|
||||
|
||||
--empty dst inventory before proceeding
|
||||
-- local size = inv:get_size("dst");
|
||||
-- for i=1,size do
|
||||
-- inv:set_stack("dst", i, ItemStack(""));
|
||||
-- end
|
||||
|
||||
--take 1 item from src inventory for each activation
|
||||
stack=stack:take_item(reqcount); inv:remove_item("src", stack)
|
||||
|
||||
for _, v in pairs(itemlist) do
|
||||
if math.random(1, 4)<=3 then -- probability 3/4 = 75%
|
||||
if not string.find(v,"group") then -- dont add if item described with group
|
||||
local par = string.find(v,"\"") or 0;
|
||||
if inv:room_for_item("dst", ItemStack(v)) then -- can item be put in
|
||||
inv:add_item("dst",ItemStack(v));
|
||||
else return
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
|
||||
minetest.sound_play("recycler", {pos=pos,gain=0.5,max_hear_distance = 16,})
|
||||
|
||||
|
||||
fuel = fuel-1; -- burn fuel on succesful operation
|
||||
meta:set_float("fuel",fuel); meta:set_string("infotext", "fuel status " .. fuel .. ", recycling " .. meta:get_string("node"));
|
||||
end
|
||||
|
||||
|
||||
local recycler_update_meta = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
|
||||
local form =
|
||||
"size[8,8]".. -- width, heightinv:get_stack
|
||||
--"size[6,10]".. -- width, height
|
||||
"label[0,0;IN] label[1,0;OUT] label[0,2;FUEL] "..
|
||||
"list["..list_name..";src;0.,0.5;1,1;]"..
|
||||
"list["..list_name..";dst;1.,0.5;3,3;]"..
|
||||
"list["..list_name..";fuel;0.,2.5;1,1;]"..
|
||||
"list[current_player;main;0,4;8,4;]"..
|
||||
"field[4.5,0.75;2,1;recipe;select recipe: ;"..(meta:get_int("recipe")).."]"..
|
||||
"button[6.5,0.5;1,1;OK;OK]"..
|
||||
"listring["..list_name..";dst]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring["..list_name..";src]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring["..list_name..";fuel]"..
|
||||
"listring[current_player;main]"
|
||||
--"field[0.25,4.5;2,1;mode;mode;"..mode.."]"
|
||||
meta:set_string("formspec", form)
|
||||
end
|
||||
|
||||
minetest.register_node("basic_machines:recycler", {
|
||||
description = "Recycler - use to get some ingredients back from crafted things",
|
||||
tiles = {"recycler.png"},
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_string("infotext", "Recycler: put one item in it (src) and obtain 75% of raw materials (dst). To operate it insert fuel, then insert item to recycle or use keypad to activate it.")
|
||||
meta:set_string("owner", placer:get_player_name());
|
||||
meta:set_int("recipe",1);
|
||||
meta:set_float("fuel",0);
|
||||
local inv = meta:get_inventory();inv:set_size("src", 1);inv:set_size("dst",9);inv:set_size("fuel",1);
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler
|
||||
recycler_update_meta(pos);
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if listname =="dst" then return end
|
||||
recycler_process(pos);
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
return 0;
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = function (pos, node,ttl)
|
||||
if type(ttl)~="number" then ttl = 1 end
|
||||
if ttl<0 then return end -- machines_TTL prevents infinite recursion
|
||||
recycler_process(pos);
|
||||
end
|
||||
}
|
||||
},
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
if minetest.is_protected(pos, sender:get_player_name()) then return end
|
||||
if fields.quit then return end
|
||||
local meta = minetest.get_meta(pos);
|
||||
local recipe=1;
|
||||
if fields.recipe then
|
||||
recipe = tonumber(fields.recipe) or 1;
|
||||
else return;
|
||||
end
|
||||
meta:set_int("recipe",recipe);
|
||||
meta:set_string("node",""); -- this will force to reread recipe on next use
|
||||
recycler_update_meta(pos);
|
||||
end,
|
||||
|
||||
can_dig = function(pos)
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
|
||||
if not (inv:is_empty("fuel")) or not (inv:is_empty("src")) or not (inv:is_empty("dst")) then return false end -- all inv must be empty to be dug
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
})
|
||||
|
||||
|
||||
-- minetest.register_craft({
|
||||
-- output = "basic_machines:recycler",
|
||||
-- recipe = {
|
||||
-- {"default:mese_crystal","default:mese_crystal","default:mese_crystal"},
|
||||
-- {"default:mese_crystal","default:diamondblock","default:mese_crystal"},
|
||||
-- {"default:mese_crystal","default:mese_crystal","default:mese_crystal"},
|
||||
|
||||
-- }
|
||||
-- })
|
|
@ -0,0 +1,589 @@
|
|||
local machines_timer = basic_machines.machines_timer or 5
|
||||
local machines_minstep = basic_machines.machines_minstep or 1
|
||||
|
||||
-- BATTERY
|
||||
|
||||
local battery_update_meta = function(pos)
|
||||
local meta = minetest.get_meta(pos)
|
||||
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
|
||||
local capacity = meta:get_float("capacity")
|
||||
local maxpower = meta:get_float("maxpower")
|
||||
local energy = math.ceil(10*meta:get_float("energy"))/10
|
||||
|
||||
local form =
|
||||
"size[8,6.5]".. -- width, height
|
||||
"bgcolor[#333333;false]"..
|
||||
"label[0,0;FUEL] ".."label[6,0;UPGRADE] "..
|
||||
"box[1.45,0.48;2.15,1.01;#222222]"..
|
||||
"list["..list_name..";fuel;0.,0.5;1,1;]"..
|
||||
"list["..list_name..";upgrade;6.,0.5;2,2;]"..
|
||||
"list[current_player;main;0,2.5;8,4;]"..
|
||||
|
||||
"image_button[4.3,0.5;1.6,0.5;wool_black.png;help;help]"..
|
||||
"image_button[4.3,1;1.6,0.5;wool_black.png;OK;refresh]"..
|
||||
|
||||
"image[1.5,0.5;0.5,0.5;basic_machine_generator.png]"..
|
||||
"image[1.5,1;0.5,0.5;power_cell.png]"..
|
||||
|
||||
"label[2,0.5;Power: 10]"..
|
||||
"label[2,1;Capacity: 30]"..
|
||||
|
||||
"button_exit[4.1,7.4;2,0.5;OK;close]"..
|
||||
"listring["..list_name..";fuel]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring["..list_name..";upgrade]"..
|
||||
"listring[current_player;main]"
|
||||
|
||||
-- local form =
|
||||
-- "size[8,6.5]".. -- width, height
|
||||
-- "label[0,0;FUEL] ".."label[6,0;UPGRADE] "..
|
||||
-- "label[0.,1.5;UPGRADE: diamond/mese block for power/capacity]"..
|
||||
-- "label[0.,1.75;UPGRADE: maxpower ".. maxpower .. " / CAPACITY " .. meta:get_int("capacity") .. "]"..
|
||||
-- "list["..list_name..";fuel;0.,0.5;1,1;]".. "list["..list_name..";upgrade;6.,0.5;2,2;]" ..
|
||||
-- "list[current_player;main;0,2.5;8,4;]"..
|
||||
-- "button[4.5,0.35;1.5,1;OK;REFRESH]"..
|
||||
-- "listring["..list_name..";upgrade]"..
|
||||
-- "listring[current_player;main]"..
|
||||
-- "listring["..list_name..";fuel]"..
|
||||
-- "listring[current_player;main]"
|
||||
meta:set_string("formspec", form)
|
||||
end
|
||||
|
||||
--[power crystal name] = energy provided
|
||||
basic_machines.energy_crystals = {
|
||||
["basic_machines:power_cell"]=1,
|
||||
["basic_machines:power_block"]=11,
|
||||
["basic_machines:power_rod"]=100,
|
||||
}
|
||||
|
||||
|
||||
battery_recharge = function(pos)
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local energy = meta:get_float("energy");
|
||||
local capacity = meta:get_float("capacity");
|
||||
local inv = meta:get_inventory();
|
||||
local stack = inv:get_stack("fuel", 1); local item = stack:get_name();
|
||||
local crystal = false;
|
||||
|
||||
local add_energy=0;
|
||||
add_energy = basic_machines.energy_crystals[item] or 0;
|
||||
|
||||
if add_energy>0 then
|
||||
if pos.y>1500 then add_energy=2*add_energy end -- in space recharge is more efficient
|
||||
crystal = true;
|
||||
if add_energy<=capacity then
|
||||
stack:take_item(1);
|
||||
inv:set_stack("fuel", 1, stack)
|
||||
else
|
||||
meta:set_string("infotext", "recharge problem: capacity " .. capacity .. ", needed " .. energy+add_energy)
|
||||
return energy
|
||||
end
|
||||
else -- try do determine caloric value of fuel inside battery
|
||||
local fuellist = inv:get_list("fuel");if not fuellist then return energy end
|
||||
local fueladd, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist})
|
||||
if fueladd.time > 0 then
|
||||
add_energy = fueladd.time/40;
|
||||
if energy+add_energy<=capacity then
|
||||
inv:set_stack("fuel", 1, afterfuel.items[1]);
|
||||
else
|
||||
meta:set_string("infotext", "recharge problem: capacity " .. capacity .. ", needed " .. energy+add_energy)
|
||||
return energy
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if add_energy>0 then
|
||||
energy=energy+add_energy
|
||||
if energy<0 then energy = 0 end
|
||||
if energy>capacity then energy = capacity end -- excess energy is wasted
|
||||
meta:set_float("energy",energy);
|
||||
meta:set_string("infotext", "(R) energy: " .. math.ceil(energy*10)/10 .. " / ".. capacity);
|
||||
minetest.sound_play("electric_zap", {pos=pos,gain=0.05,max_hear_distance = 8,})
|
||||
end
|
||||
|
||||
local full_coef = math.floor(energy/capacity*3); if full_coef > 2 then full_coef = 2 end
|
||||
minetest.swap_node(pos,{name = "basic_machines:battery_".. full_coef}) -- graphic energy
|
||||
|
||||
return energy; -- new battery energy level
|
||||
end
|
||||
|
||||
battery_upgrade = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
local count1,count2;count1=0;count2=0;
|
||||
local stack,item,count;
|
||||
for i=1,4 do
|
||||
stack = inv:get_stack("upgrade", i);item = stack:get_name();count= stack:get_count();
|
||||
if item == "default:mese" then
|
||||
count1=count1+count
|
||||
elseif item == "default:diamondblock" then
|
||||
count2=count2+count
|
||||
end
|
||||
end
|
||||
--if count1<count2 then count =count1 else count=count2 end
|
||||
|
||||
if pos.y>1500 then count1 = 2*count1; count2=2*count2 end -- space increases efficiency
|
||||
|
||||
|
||||
meta:set_int("upgrade",count2); -- diamond for power
|
||||
-- adjust capacity
|
||||
--yyy
|
||||
local capacity = 3+3*count1; -- mese for capacity
|
||||
local maxpower = 1+count2*2; -- old 99 upgrade -> 200 power
|
||||
|
||||
capacity = math.ceil(capacity*10)/10;
|
||||
local energy = 0;
|
||||
meta:set_float("capacity",capacity)
|
||||
meta:set_float("maxpower",maxpower)
|
||||
meta:set_float("energy",0)
|
||||
meta:set_string("infotext", "energy: " .. math.ceil(energy*10)/10 .. " / ".. capacity);
|
||||
end
|
||||
|
||||
local machines_activate_furnace = minetest.registered_nodes["default:furnace"].on_metadata_inventory_put; -- this function will activate furnace
|
||||
|
||||
minetest.register_node("basic_machines:battery_0", {
|
||||
description = "battery - stores energy, generates energy from fuel, can power nearby machines, or accelerate/run furnace above it. Its upgradeable.",
|
||||
tiles = {"basic_machine_outlet.png","basic_machine_battery.png","basic_machine_battery_0.png"},
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_string("infotext","battery - stores energy, generates energy from fuel, can power nearby machines, or accelerate/run furnace above it when activated by keypad");
|
||||
meta:set_string("owner",placer:get_player_name());
|
||||
local inv = meta:get_inventory();inv:set_size("fuel", 1*1); -- place to put crystals
|
||||
inv:set_size("upgrade", 2*2);
|
||||
meta:set_int("upgrade",0); -- upgrade level determines energy storage capacity and max energy output
|
||||
meta:set_float("capacity",3);meta:set_float("maxpower",1);
|
||||
meta:set_float("energy",0);
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = function (pos, node,ttl)
|
||||
if type(ttl)~="number" then ttl = 1 end
|
||||
if ttl<0 then return end -- machines_TTL prevents infinite recursion
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local energy = meta:get_float("energy");
|
||||
local capacity = meta:get_float("capacity");
|
||||
local full_coef = math.floor(energy/capacity*3);
|
||||
|
||||
-- try to power furnace on top of it
|
||||
if energy>=1 then -- need at least 1 energy
|
||||
pos.y=pos.y+1; local node = minetest.get_node(pos).name;
|
||||
|
||||
if node== "default:furnace" or node=="default:furnace_active" then
|
||||
local fmeta = minetest.get_meta(pos);
|
||||
local fuel_totaltime = fmeta:get_float("fuel_totaltime") or 0;
|
||||
local fuel_time = fmeta:get_float("fuel_time") or 0;
|
||||
local t0 = meta:get_int("ftime"); -- furnace time
|
||||
local t1 = minetest.get_gametime();
|
||||
|
||||
if t1-t0<machines_minstep then -- to prevent too quick furnace acceleration, punishment is cooking reset
|
||||
fmeta:set_float("src_time",0); return
|
||||
end
|
||||
meta:set_int("ftime",t1);
|
||||
|
||||
local upgrade = meta:get_int("upgrade");upgrade=upgrade*0.1;
|
||||
|
||||
--if fuel_time>4 then -- accelerated cooking
|
||||
local src_time = fmeta:get_float("src_time") or 0
|
||||
energy = energy - 0.25*upgrade; -- use energy to accelerate burning
|
||||
|
||||
|
||||
if fuel_time>40 or fuel_totaltime == 0 or node=="default:furnace" then -- to add burn time: must burn for at least 40 secs or furnace out of fuel
|
||||
|
||||
fmeta:set_float("fuel_totaltime",60);fmeta:set_float("fuel_time",0) -- add 60 second burn time to furnace
|
||||
energy=energy-0.5; -- use up energy to add fuel
|
||||
|
||||
-- make furnace start if not already started
|
||||
if node~="default:furnace_active" and machines_activate_furnace then machines_activate_furnace(pos) end
|
||||
-- update energy display
|
||||
end
|
||||
|
||||
|
||||
if energy<0 then
|
||||
energy = 0
|
||||
else -- only accelerate if we had enough energy, note: upgrade*0.1*0.25<power_rod is limit upgrade, so upgrade = 40*100 = 4000
|
||||
fmeta:set_float("src_time",src_time+machines_timer*upgrade); -- accelerated smelt: with 99 upgrade battery furnace works 11x faster
|
||||
end
|
||||
|
||||
meta:set_float("energy",energy);
|
||||
meta:set_string("infotext", "energy: " .. math.ceil(energy*10)/10 .. " / ".. capacity);
|
||||
|
||||
|
||||
if energy>=1 then -- no need to recharge yet, will still work next time
|
||||
local full_coef_new = math.floor(energy/capacity*3); if full_coef_new>2 then full_coef_new = 2 end
|
||||
pos.y = pos.y-1;
|
||||
if full_coef_new ~= full_coef then minetest.swap_node(pos,{name = "basic_machines:battery_".. full_coef_new}) end
|
||||
return
|
||||
else
|
||||
local infotext = meta:get_string("infotext");
|
||||
local new_infotext = "furnace needs at least 1 energy";
|
||||
if new_infotext~=infotext then -- dont update unnecesarilly
|
||||
meta:set_string("infotext", new_infotext);
|
||||
pos.y=pos.y-1; -- so that it points to battery again!
|
||||
end
|
||||
end
|
||||
else
|
||||
pos.y=pos.y-1;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
-- try to recharge by converting inserted fuel/power cells into energy
|
||||
|
||||
if energy<capacity then -- not full, try to recharge
|
||||
battery_recharge(pos);
|
||||
return
|
||||
end
|
||||
|
||||
local full_coef_new = math.floor(energy/capacity*3); if full_coef_new>2 then full_coef_new = 2 end
|
||||
if full_coef_new ~= full_coef then minetest.swap_node(pos,{name = "basic_machines:battery_".. full_coef_new}) end
|
||||
|
||||
end
|
||||
}},
|
||||
|
||||
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler
|
||||
battery_update_meta(pos);
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
if fields.quit then return end
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
|
||||
if fields.OK then battery_update_meta(pos) return end
|
||||
if fields.help then
|
||||
local name = sender:get_player_name();
|
||||
local text = "Battery provides power to machines or furnace. It can either "..
|
||||
"use power cells or convert ordinary furnace fuels into energy. 1 coal lump gives 1 energy.\n\n"..
|
||||
"UPGRADE with diamondblocks for more available power output or with "..
|
||||
"meseblocks for more power storage capacity"
|
||||
|
||||
local form = "size [6,7] textarea[0,0;6.5,8.5;help;BATTERY HELP;".. text.."]"
|
||||
minetest.show_formspec(name, "basic_machines:help_battery", form)
|
||||
end
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if listname=="fuel" then
|
||||
battery_recharge(pos);
|
||||
battery_update_meta(pos);
|
||||
elseif listname == "upgrade" then
|
||||
battery_upgrade(pos);
|
||||
battery_update_meta(pos);
|
||||
end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
if listname == "upgrade" then
|
||||
battery_upgrade(pos);
|
||||
battery_update_meta(pos);
|
||||
end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
return 0;
|
||||
end,
|
||||
|
||||
can_dig = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
|
||||
if not (inv:is_empty("fuel")) or not (inv:is_empty("upgrade")) then return false end -- fuel AND upgrade inv must be empty to be dug
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
})
|
||||
|
||||
|
||||
|
||||
-- GENERATOR
|
||||
|
||||
local generator_update_meta = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z
|
||||
|
||||
local form =
|
||||
"size[8,6.5]" .. -- width, height
|
||||
"label[0,0;POWER CRYSTALS] ".."label[6,0;UPGRADE] "..
|
||||
"label[1,1;UPGRADE LEVEL ".. meta:get_int("upgrade").." (generator)]"..
|
||||
"list["..list_name..";fuel;0.,0.5;1,1;]"..
|
||||
"list["..list_name..";upgrade;6.,0.5;2,1;]"..
|
||||
"list[current_player;main;0,2.5;8,4;]"..
|
||||
"button[4.5,1.5;1.5,1;OK;REFRESH]"..
|
||||
"button[6,1.5;1.5,1;help;help]"..
|
||||
"listring["..list_name..";fuel]"..
|
||||
"listring[current_player;main]"..
|
||||
"listring["..list_name..";upgrade]"..
|
||||
"listring[current_player;main]"
|
||||
meta:set_string("formspec", form)
|
||||
end
|
||||
|
||||
|
||||
|
||||
generator_upgrade = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
local stack,item,count;
|
||||
count = 0
|
||||
for i=1,2 do
|
||||
stack = inv:get_stack("upgrade", i);item = stack:get_name();
|
||||
if item == "basic_machines:generator" then
|
||||
count= count + stack:get_count();
|
||||
end
|
||||
end
|
||||
meta:set_int("upgrade",count);
|
||||
end
|
||||
|
||||
minetest.register_node("basic_machines:generator", {
|
||||
description = "Generator - very expensive, generates power crystals that provide power. Its upgradeable.",
|
||||
tiles = {"basic_machine_generator.png"},
|
||||
groups = {cracky=3, mesecon_effector_on = 1},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
after_place_node = function(pos, placer)
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_string("infotext","generator - generates power crystals that provide power. Upgrade with up to 50 generators.");
|
||||
meta:set_string("owner",placer:get_player_name());
|
||||
local inv = meta:get_inventory();
|
||||
inv:set_size("fuel", 1*1); -- here generated power crystals are placed
|
||||
inv:set_size("upgrade", 2*1);
|
||||
meta:set_int("upgrade",0); -- upgrade level determines quality of produced crystals
|
||||
|
||||
end,
|
||||
|
||||
on_rightclick = function(pos, node, player, itemstack, pointed_thing)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler
|
||||
generator_update_meta(pos);
|
||||
end,
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, sender)
|
||||
if fields.quit then return end
|
||||
if fields.help then
|
||||
local text = "Generator slowly produces power crystals. Those can be used to recharge batteries and come in 3 flavors:\n\n low level (0-4), medium level (5-19) and high level (20+). Upgrading the generator (upgrade with generators) will increase the rate at which the crystals are produced.\n\nYou can automate the process of battery recharging by using mover in inventory mode, taking from inventory \"fuel\"";
|
||||
local form = "size [6,7] textarea[0,0;6.5,8.5;help;GENERATOR HELP;".. text.."]"
|
||||
minetest.show_formspec(sender:get_player_name(), "basic_machines:help_mover", form)
|
||||
return
|
||||
end
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
|
||||
generator_update_meta(pos);
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local privs = minetest.get_player_privs(player:get_player_name());
|
||||
if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return 0 end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
on_metadata_inventory_put = function(pos, listname, index, stack, player)
|
||||
if listname == "upgrade" then
|
||||
generator_upgrade(pos);
|
||||
generator_update_meta(pos);
|
||||
end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
on_metadata_inventory_take = function(pos, listname, index, stack, player)
|
||||
if listname == "upgrade" then
|
||||
generator_upgrade(pos);
|
||||
generator_update_meta(pos);
|
||||
end
|
||||
return stack:get_count();
|
||||
end,
|
||||
|
||||
allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player)
|
||||
return 0;
|
||||
end,
|
||||
|
||||
can_dig = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local inv = meta:get_inventory();
|
||||
|
||||
if not inv:is_empty("upgrade") then return false end -- fuel inv is not so important as generator generates it
|
||||
|
||||
return true
|
||||
|
||||
end
|
||||
|
||||
})
|
||||
|
||||
local genstat = {}; -- generator statistics for each player
|
||||
|
||||
minetest.register_abm({
|
||||
nodenames = {"basic_machines:generator"},
|
||||
neighbors = {""},
|
||||
interval = 19,
|
||||
chance = 1,
|
||||
action = function(pos, node, active_object_count, active_object_count_wider)
|
||||
local meta = minetest.get_meta(pos);
|
||||
|
||||
-- checks
|
||||
local owner = meta:get_string("owner");
|
||||
local gendata = genstat[owner];
|
||||
local t = minetest.get_gametime();
|
||||
if not gendata then genstat[owner] = {t,0} gendata = genstat[owner] end -- init: time, count
|
||||
|
||||
if t-gendata[1] >= 19 then -- more than 19s elapsed since last time
|
||||
gendata[1] = t; -- reset timer
|
||||
gendata[2] = 0; -- reset activation count
|
||||
end
|
||||
|
||||
gendata[2] = gendata[2] + 1 -- increase activation count
|
||||
if gendata[2]>5 then
|
||||
meta:set_string("infotext","error: more than 5 active generators")
|
||||
return
|
||||
end
|
||||
|
||||
local upgrade = meta:get_int("upgrade");
|
||||
local inv = meta:get_inventory();
|
||||
local stack = inv:get_stack("fuel", 1);
|
||||
local crystal, text;
|
||||
|
||||
if upgrade > 50 then meta:set_string("infotext","error: max upgrade is 50"); return end
|
||||
|
||||
if upgrade >= 20 then
|
||||
crystal = "basic_machines:power_rod " .. math.floor(1+(upgrade-20)*9/178)
|
||||
text = "high upgrade: power rod";
|
||||
elseif upgrade >=5 then
|
||||
crystal ="basic_machines:power_block " .. math.floor(1+(upgrade-5)*9/15);
|
||||
text = "medium upgrade: power block";
|
||||
else
|
||||
crystal ="basic_machines:power_cell " .. math.floor(1+2*upgrade);
|
||||
text = "low upgrade: power cell";
|
||||
end
|
||||
local morecrystal = ItemStack(crystal)
|
||||
stack:add_item(morecrystal);
|
||||
inv:set_stack("fuel", 1, stack)
|
||||
meta:set_string("infotext",text)
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
|
||||
|
||||
-- API for power distribution
|
||||
function basic_machines.check_power(pos, power_draw) -- mover checks power source - battery
|
||||
|
||||
--minetest.chat_send_all(" battery: check_power " .. minetest.pos_to_string(pos) .. " " .. power_draw)
|
||||
local batname = "basic_machines:battery";
|
||||
if not string.find(minetest.get_node(pos).name,batname) then
|
||||
return -1 -- battery not found!
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(pos);
|
||||
local energy = meta:get_float("energy");
|
||||
local capacity = meta:get_float("capacity");
|
||||
local maxpower = meta:get_float("maxpower");
|
||||
local full_coef = math.floor(energy/capacity*3); -- 0,1,2
|
||||
|
||||
if power_draw>maxpower then
|
||||
meta:set_string("infotext", "Power draw required : " .. power_draw .. " maximum power output " .. maxpower .. ". Please upgrade battery")
|
||||
return 0;
|
||||
end
|
||||
|
||||
if power_draw>energy then
|
||||
energy = battery_recharge(pos); -- try recharge battery and continue operation immidiately
|
||||
if not energy then return 0 end
|
||||
end
|
||||
|
||||
energy = energy-power_draw;
|
||||
if energy<0 then
|
||||
meta:set_string("infotext", "used fuel provides too little power for current power draw ".. power_draw);
|
||||
return 0
|
||||
end -- recharge wasnt enough, needs to be repeated manually, return 0 power available
|
||||
meta:set_float("energy", energy);
|
||||
-- update energy display
|
||||
meta:set_string("infotext", "energy: " .. math.ceil(energy*10)/10 .. " / ".. capacity);
|
||||
|
||||
local full_coef_new = math.floor(energy/capacity*3); if full_coef_new>2 then full_coef_new = 2 end
|
||||
if full_coef_new ~= full_coef then minetest.swap_node(pos,{name = "basic_machines:battery_".. full_coef_new}) end -- graphic energy level display
|
||||
|
||||
return power_draw;
|
||||
|
||||
end
|
||||
|
||||
------------------------
|
||||
-- CRAFTS
|
||||
------------------------
|
||||
|
||||
-- minetest.register_craft({
|
||||
-- output = "basic_machines:battery",
|
||||
-- recipe = {
|
||||
-- {"","default:steel_ingot",""},
|
||||
-- {"default:steel_ingot","default:mese","default:steel_ingot"},
|
||||
-- {"","default:diamond",""},
|
||||
|
||||
-- }
|
||||
-- })
|
||||
|
||||
-- minetest.register_craft({
|
||||
-- output = "basic_machines:generator",
|
||||
-- recipe = {
|
||||
-- {"","",""},
|
||||
-- {"default:diamondblock","basic_machines:battery","default:diamondblock"},
|
||||
-- {"default:diamondblock","default:diamondblock","default:diamondblock"}
|
||||
|
||||
-- }
|
||||
-- })
|
||||
|
||||
minetest.register_craftitem("basic_machines:power_cell", {
|
||||
description = "Power cell - provides 1 power",
|
||||
inventory_image = "power_cell.png",
|
||||
stack_max = 25
|
||||
})
|
||||
|
||||
minetest.register_craftitem("basic_machines:power_block", {
|
||||
description = "Power block - provides 11 power",
|
||||
inventory_image = "power_block.png",
|
||||
stack_max = 25
|
||||
})
|
||||
|
||||
minetest.register_craftitem("basic_machines:power_rod", {
|
||||
description = "Power rod - provides 100 power",
|
||||
inventory_image = "power_rod.png",
|
||||
stack_max = 25
|
||||
})
|
||||
|
||||
-- various battery levels: 0,1,2 (2 >= 66%, 1 >= 33%,0>=0%)
|
||||
local batdef = {};
|
||||
for k,v in pairs(minetest.registered_nodes["basic_machines:battery_0"]) do batdef[k] = v end
|
||||
|
||||
for i = 1,2 do
|
||||
batdef.tiles[3] = "basic_machine_battery_" .. i ..".png"
|
||||
minetest.register_node("basic_machines:battery_"..i, batdef)
|
||||
end
|
After Width: | Height: | Size: 242 B |
After Width: | Height: | Size: 393 B |
After Width: | Height: | Size: 398 B |
After Width: | Height: | Size: 242 B |
After Width: | Height: | Size: 274 B |
After Width: | Height: | Size: 240 B |
After Width: | Height: | Size: 239 B |
After Width: | Height: | Size: 348 B |
After Width: | Height: | Size: 292 B |
After Width: | Height: | Size: 119 B |
After Width: | Height: | Size: 219 B |
After Width: | Height: | Size: 239 B |
After Width: | Height: | Size: 258 B |
After Width: | Height: | Size: 253 B |
After Width: | Height: | Size: 238 B |
After Width: | Height: | Size: 302 B |
After Width: | Height: | Size: 325 B |
After Width: | Height: | Size: 315 B |
After Width: | Height: | Size: 218 B |
After Width: | Height: | Size: 214 B |
After Width: | Height: | Size: 123 B |
After Width: | Height: | Size: 123 B |
After Width: | Height: | Size: 134 B |
After Width: | Height: | Size: 316 B |
After Width: | Height: | Size: 133 B |
After Width: | Height: | Size: 133 B |
After Width: | Height: | Size: 133 B |
After Width: | Height: | Size: 234 B |
|
@ -0,0 +1 @@
|
|||
default
|
|
@ -0,0 +1,681 @@
|
|||
--Basic protect by rnd, 2016 -- adapted for skyblock!
|
||||
|
||||
-- features:
|
||||
-- super fast protection checks with caching
|
||||
-- no slowdowns due to large protection radius or larger protector counts
|
||||
-- shared protection: just write players names in list, separated by spaces
|
||||
|
||||
--local protector = {};
|
||||
basic_protect = {};
|
||||
basic_protect.radius = 32; -- by default protects 20x10x20 chunk, protector placed in center at positions that are multiplier of 20,20 (x,y,z)
|
||||
local enable_craft = true; -- enable crafting of protector
|
||||
|
||||
|
||||
-- GHOST TOWN: OLD AREAS SWAPPING
|
||||
local enable_swapping = false
|
||||
basic_protect.swap = {}; -- swap area for old houses that are not "nice", data is as table {owner_name, timestamp}
|
||||
-- after 1 week if build area inside -200,200 around spawn is not rated nice it gets moved to swap area and original area is flattened
|
||||
-- if this is full oldest build there gets replaced by new one
|
||||
-- implementation details:
|
||||
-- insertion : by looping and selecting first free to insert, if none it selects oldest one
|
||||
-- removal: just replace swap data with {"",0}
|
||||
|
||||
basic_protect.swap_size = 15 -- 15x15 = 225 free positions in swap area (ghost town)
|
||||
basic_protect.swap_pos = {x=380,y=0,z=80}; -- where first protector in swap area will be positioned, coordinates should be multiples of protect radius (default 20), vertically 2x20 = 40
|
||||
basic_protect.swap_range = 200; -- all protectors inside this around 0 0 are subject to this
|
||||
basic_protect.swap_timeout = 2*3600*24*7; -- time after which protector is considered "old", default 2 weeks
|
||||
--------
|
||||
|
||||
|
||||
basic_protect.cache = {};
|
||||
local round = math.floor;
|
||||
local protector_position = function(pos)
|
||||
local r = basic_protect.radius;
|
||||
local ry = 2*r;
|
||||
return {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r};
|
||||
end
|
||||
|
||||
|
||||
local function check_protector (p, digger) -- is it protected for digger at this protector?
|
||||
|
||||
local meta = minetest.get_meta(p);
|
||||
local owner = meta:get_string("owner");
|
||||
if digger~=owner then
|
||||
--check for shared protection
|
||||
local shares = meta:get_string("shares");
|
||||
for word in string.gmatch(shares, "%S+") do
|
||||
if digger == word then
|
||||
return false;
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(digger,"#PROTECTOR: this area is owned by " .. owner);
|
||||
return true;
|
||||
else
|
||||
return false;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
--check if position is protected
|
||||
local old_is_protected = minetest.is_protected
|
||||
function minetest.is_protected(pos, digger)
|
||||
local p = protector_position(pos);
|
||||
local is_protected = true;
|
||||
|
||||
if not basic_protect.cache[digger] then -- cache current check for faster future lookups
|
||||
|
||||
local updatecache = true;
|
||||
|
||||
if minetest.get_node(p).name == "basic_protect:protector" then
|
||||
is_protected = check_protector (p, digger)
|
||||
else
|
||||
if minetest.get_node(p).name == "ignore" then
|
||||
is_protected=true
|
||||
updatecache = false
|
||||
else
|
||||
is_protected = old_is_protected(pos, digger);
|
||||
end
|
||||
end
|
||||
if updatecache then
|
||||
basic_protect.cache[digger] = {pos = {x=p.x,y=p.y,z=p.z}, is_protected = is_protected}
|
||||
end
|
||||
|
||||
else -- look up cached result
|
||||
|
||||
local p0 = basic_protect.cache[digger].pos;
|
||||
if (p0.x==p.x and p0.y==p.y and p0.z==p.z) then -- already checked, just lookup
|
||||
is_protected = basic_protect.cache[digger].is_protected;
|
||||
else -- another block, we need to check again
|
||||
|
||||
local updatecache = true;
|
||||
if minetest.get_node(p).name == "basic_protect:protector" then
|
||||
is_protected = check_protector (p, digger)
|
||||
else
|
||||
if minetest.get_node(p).name == "ignore" then -- area not yet loaded
|
||||
is_protected=true; updatecache = false;
|
||||
minetest.chat_send_player(digger,"#PROTECTOR: chunk " .. p.x .. " " .. p.y .. " " .. p.z .. " is not yet completely loaded");
|
||||
else
|
||||
is_protected = old_is_protected(pos, digger);
|
||||
end
|
||||
end
|
||||
if updatecache then
|
||||
basic_protect.cache[digger] = {pos = {x=p.x,y=p.y,z=p.z}, is_protected = is_protected}; -- refresh cache;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if is_protected then -- DEFINE action for trespassers here
|
||||
|
||||
--teleport offender
|
||||
if basic_protect.cache[digger] then
|
||||
local tpos = basic_protect.cache[digger].tpos;
|
||||
if not tpos then
|
||||
local meta = minetest.get_meta(p);
|
||||
local xt = meta:get_int("xt"); local yt = meta:get_int("yt"); local zt = meta:get_int("zt");
|
||||
tpos = {x=xt,y=yt,z=zt};
|
||||
end
|
||||
|
||||
|
||||
if (tpos.x~=p.x or tpos.y~=p.y or tpos.z~=p.z) then
|
||||
local player = minetest.get_player_by_name(digger);
|
||||
if minetest.get_node(p).name == "basic_protect:protector" then
|
||||
if player then player:setpos(tpos) end;
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
return is_protected;
|
||||
end
|
||||
|
||||
local update_formspec = function(pos)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local shares = meta:get_string("shares");
|
||||
local tpos = meta:get_string("tpos");
|
||||
--local subfree = meta:get_string("subfree");
|
||||
--if subfree == "" then subfree = "0 0 0 0 0 0" end
|
||||
|
||||
if tpos == "" then
|
||||
tpos = "0 0 0"
|
||||
end
|
||||
meta:set_string("formspec",
|
||||
"size[5,5]"..
|
||||
"label[-0.25,-0.25; PROTECTOR]"..
|
||||
"field[0.25,1;5,1;shares;Write in names of players you want to add in protection ;".. shares .."]"..
|
||||
"field[0.25,2;5,1;tpos;where to teleport intruders - default 0 0 0 ;".. tpos .."]"..
|
||||
--"field[0.25,3;5,1;subfree;specify free to dig sub area x1 y1 z1 x2 y2 z2 - default 0 0 0 0 0 0;".. subfree .."]"..
|
||||
"button_exit[4,4.5;1,1;OK;OK]"
|
||||
);
|
||||
end
|
||||
|
||||
basic_protect.protect_new = function(p,name)
|
||||
|
||||
--skyblock by rnd, check if player tries to protect other ppl island
|
||||
if skyblock and skyblock.players and skyblock.get_island_pos then
|
||||
local skyid = skyblock.players[name].id;
|
||||
local skypos = skyblock.get_island_pos(skyid);
|
||||
local dist = math.max(math.abs(p.x-skypos.x),math.abs(p.z-skypos.z));
|
||||
if dist>=skyblock.islands_gap then
|
||||
local privs = minetest.get_player_privs(name);
|
||||
if not privs.kick then
|
||||
minetest.chat_send_player(name, "#PROTECTOR: you can only protect empty space or your island or above it")
|
||||
minetest.set_node(p,{name = "air"}) -- clear protector
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
local meta = minetest.get_meta(p);
|
||||
meta:set_string("owner",name);
|
||||
meta:set_int("xt",p.x);meta:set_int("yt",p.y);meta:set_int("zt",p.z);
|
||||
meta:set_string("tpos", "0 0 0");
|
||||
meta:set_int("timestamp", minetest.get_gametime());
|
||||
-- yyy testing!
|
||||
--if minetest.get_player_privs(name).kick then meta:set_int("nice",1) end -- moderator buildings are automatically "nice"
|
||||
|
||||
minetest.chat_send_player(name, "#PROTECTOR: protected new area, protector placed at(" .. p.x .. "," .. p.y .. "," .. p.z .. "), area size " .. basic_protect.radius .. "x" .. basic_protect.radius .. " , 2x more in vertical direction. Say /unprotect to unclaim area.. ");
|
||||
if p.y == 0 and math.abs(p.x) < basic_protect.swap_range and math.abs(p.z) < basic_protect.swap_range then
|
||||
minetest.chat_send_player(name, "** WARNING ** you placed protector inside ( limit " .. basic_protect.swap_range .. " ) spawn area. Make a nice building and tell moderator about it or it will be moved to ghost town after long time. ");
|
||||
end
|
||||
|
||||
meta:set_string("infotext", "property of " .. name);
|
||||
|
||||
if #minetest.get_objects_inside_radius(p, 1)==0 then
|
||||
minetest.add_entity({x=p.x,y=p.y,z=p.z}, "basic_protect:display")
|
||||
end
|
||||
local shares = "";
|
||||
update_formspec(p);
|
||||
basic_protect.cache = {}; -- reset cache
|
||||
end
|
||||
|
||||
minetest.register_node("basic_protect:protector", {
|
||||
description = "Protects a rectangle area of size " .. basic_protect.radius,
|
||||
tiles = {"basic_protector.png","basic_protector_down.png","basic_protector_down.png","basic_protector_down.png","basic_protector_down.png","basic_protector_down.png"},
|
||||
--drawtype = "allfaces",
|
||||
--paramtype = "light",
|
||||
param1=1,
|
||||
groups = {oddly_breakable_by_hand=2},
|
||||
sounds = default.node_sound_wood_defaults(),
|
||||
on_place = function(itemstack, placer, pointed_thing)
|
||||
local pos = pointed_thing.under;
|
||||
local name = placer:get_player_name();
|
||||
local r = basic_protect.radius;
|
||||
local p = protector_position(pos);
|
||||
if minetest.get_node(p).name == "basic_protect:protector" then
|
||||
local meta = minetest.get_meta(p);
|
||||
minetest.chat_send_player(name,"#PROTECTOR: protector already at " .. minetest.pos_to_string(p) .. ", owned by " .. meta:get_string("owner"));
|
||||
local obj = minetest.add_entity({x=p.x,y=p.y,z=p.z}, "basic_protect:display");
|
||||
local luaent = obj:get_luaentity(); luaent.timer = 15; -- just 15 seconds display
|
||||
return itemstack
|
||||
end
|
||||
|
||||
minetest.set_node(p, {name = "basic_protect:protector"});
|
||||
basic_protect.protect_new(p,name);
|
||||
|
||||
pos.y=pos.y+1;
|
||||
minetest.set_node(pos, {name = "air"});
|
||||
itemstack:take_item(); return itemstack
|
||||
end,
|
||||
|
||||
on_punch = function(pos, node, puncher, pointed_thing) -- for unknown reason texture is unknown
|
||||
local meta = minetest.get_meta(pos);
|
||||
local owner = meta:get_string("owner");
|
||||
local name = puncher:get_player_name();
|
||||
if owner == name or not minetest.is_protected(pos, name) then
|
||||
if #minetest.get_objects_inside_radius(pos, 1)==0 then
|
||||
minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_protect:display")
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
|
||||
on_use = function(itemstack, user, pointed_thing)
|
||||
local ppos = pointed_thing.under;
|
||||
if not ppos then return end
|
||||
local pos = protector_position(ppos);
|
||||
local meta = minetest.get_meta(pos);
|
||||
local owner = meta:get_string("owner");
|
||||
local name = user:get_player_name();
|
||||
|
||||
if owner == name then
|
||||
if #minetest.get_objects_inside_radius(pos, 1)==0 then
|
||||
minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_protect:display")
|
||||
end
|
||||
minetest.chat_send_player(name,"#PROTECTOR: this is your area, protector placed at(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ". say /unprotect to unclaim area. ");
|
||||
elseif owner~=name and minetest.get_node(pos).name=="basic_protect:protector" then
|
||||
minetest.chat_send_player(name,"#PROTECTOR: this area is owned by " .. owner .. ", protector placed at(" .. pos.x .. "," .. pos.y .. "," .. pos.z .. ")");
|
||||
else
|
||||
minetest.chat_send_player(name,"#PROTECTOR: this area is FREE. place protector to claim it. Center is at (" .. pos.x .. "," .. pos.y .. "," .. pos.z.. ")");
|
||||
end
|
||||
end,
|
||||
|
||||
mesecons = {effector = {
|
||||
action_on = function (pos, node,ttl)
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_int("space",0)
|
||||
end,
|
||||
|
||||
action_off = function (pos, node,ttl)
|
||||
local meta = minetest.get_meta(pos);
|
||||
meta:set_int("space",1)
|
||||
end,
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
on_receive_fields = function(pos, formname, fields, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local owner = meta:get_string("owner");
|
||||
local name = player:get_player_name();
|
||||
local privs = minetest.get_player_privs(name);
|
||||
|
||||
if owner~= name and not privs.privs then return end
|
||||
|
||||
if fields.OK then
|
||||
if fields.shares then
|
||||
meta:set_string("shares",fields.shares);
|
||||
basic_protect.cache = {}
|
||||
end
|
||||
|
||||
if fields.tpos then
|
||||
meta:set_string("tpos", fields.tpos)
|
||||
local words = {}
|
||||
for word in string.gmatch(fields.tpos, "%S+") do
|
||||
words[#words+1] = tonumber(word) or 0
|
||||
end
|
||||
|
||||
local xt = (words[1] or 0); if math.abs(xt)>basic_protect.radius then xt = 0 end
|
||||
local yt = (words[2] or 0); if math.abs(yt)>basic_protect.radius then yt = 0 end
|
||||
local zt = (words[3] or 0); if math.abs(zt)>basic_protect.radius then zt = 0 end
|
||||
|
||||
meta:set_int("xt", xt+pos.x)
|
||||
meta:set_int("yt", yt+pos.y)
|
||||
meta:set_int("zt", zt+pos.z)
|
||||
end
|
||||
|
||||
update_formspec(pos)
|
||||
end
|
||||
end,
|
||||
|
||||
can_dig = function(pos, player)
|
||||
local meta = minetest.get_meta(pos);
|
||||
local owner = meta:get_string("owner");
|
||||
local name = player:get_player_name();
|
||||
local privs = minetest.get_player_privs(name)
|
||||
if owner~= player:get_player_name() and not privs.privs then return false end
|
||||
return true
|
||||
end
|
||||
});
|
||||
|
||||
|
||||
|
||||
-- entities used to display area when protector is punched
|
||||
|
||||
local x = basic_protect.radius/2;
|
||||
local y = 2*x;
|
||||
minetest.register_node("basic_protect:display_node", {
|
||||
tiles = {"area_display.png"},
|
||||
use_texture_alpha = false,
|
||||
walkable = false,
|
||||
drawtype = "nodebox",
|
||||
node_box = {
|
||||
type = "fixed",
|
||||
fixed = {
|
||||
|
||||
{-(x+.55), -(y+.55), -(x+.55), -(x+.45), (y-1+.55), (x-1+.55)},-- sides
|
||||
{-(x+.55), -(y+.55), (x-1+.45), (x-1+.55), (y-1+.55), (x-1+.55)},
|
||||
{(x-1+.45), -(y+.55), -(x+.55), (x-1+.55), (y-1+.55), (x-1+.55)},
|
||||
{-(x+.55), -(y+.55), -(x+.55), (x-1+.55), (y-1+.55), -(x+.45)},
|
||||
|
||||
{-(x+.55), (y-1+.45), -(x+.55), (x-1+.55), (y-1+.55), (x-1+.55)},-- top
|
||||
|
||||
{-(x+.55), -(y+.55), -(x+.55), (x-1+.55), -(y+.45), (x-1+.55)},-- bottom
|
||||
|
||||
{-.55,-.55,-.55, .55,.55,.55},-- middle (surround protector)
|
||||
},
|
||||
},
|
||||
selection_box = {
|
||||
type = "regular",
|
||||
},
|
||||
paramtype = "light",
|
||||
groups = {dig_immediate = 3, not_in_creative_inventory = 1},
|
||||
drop = "",
|
||||
})
|
||||
|
||||
|
||||
|
||||
minetest.register_entity("basic_protect:display", {
|
||||
physical = false,
|
||||
collisionbox = {0, 0, 0, 0, 0, 0},
|
||||
visual = "wielditem",
|
||||
visual_size = {x = 1.0 / 1.5, y = 1.0 / 1.5},
|
||||
textures = {"basic_protect:display_node"},
|
||||
timer = 30,
|
||||
|
||||
on_step = function(self, dtime)
|
||||
|
||||
self.timer = self.timer - dtime
|
||||
|
||||
if self.timer < 0 then
|
||||
self.object:remove()
|
||||
end
|
||||
end,
|
||||
})
|
||||
|
||||
-- CRAFTING
|
||||
if enable_craft then
|
||||
minetest.register_craft({
|
||||
output = "basic_protect:protector",
|
||||
recipe = {
|
||||
{"default:stone", "default:stone","default:stone"},
|
||||
{"default:stone", "default:steel_ingot","default:stone"},
|
||||
{"default:stone", "default:stone", "default:stone"}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
|
||||
minetest.register_chatcommand("unprotect", {
|
||||
description = "Unprotects current area",
|
||||
privs = {
|
||||
interact = true
|
||||
},
|
||||
func = function(name, param)
|
||||
local privs = minetest.get_player_privs(name);
|
||||
local player = minetest.get_player_by_name(name);
|
||||
local pos = player:getpos();
|
||||
local ppos = protector_position(pos);
|
||||
|
||||
if minetest.get_node(ppos).name == "basic_protect:protector" then
|
||||
local meta = minetest.get_meta(ppos);
|
||||
local owner = meta:get_string("owner");
|
||||
if owner == name then
|
||||
minetest.set_node(ppos,{name = "air"});
|
||||
local inv = player:get_inventory();
|
||||
inv:add_item("main",ItemStack("basic_protect:protector"));
|
||||
minetest.chat_send_player(name, "#PROTECTOR: area unprotected ");
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
minetest.register_chatcommand("protect", {
|
||||
description = "Protects current area",
|
||||
privs = {
|
||||
interact = true
|
||||
},
|
||||
func = function(name, param)
|
||||
local privs = minetest.get_player_privs(name);
|
||||
local player = minetest.get_player_by_name(name);
|
||||
if not player then return end
|
||||
local pos = player:getpos();
|
||||
local ppos = protector_position(pos);
|
||||
|
||||
if minetest.get_node(ppos).name == "basic_protect:protector" then
|
||||
local meta = minetest.get_meta(ppos);
|
||||
local owner = meta:get_string("owner");
|
||||
if owner == name then
|
||||
if #minetest.get_objects_inside_radius(ppos, 1)==0 then
|
||||
minetest.add_entity({x=ppos.x,y=ppos.y,z=ppos.z}, "basic_protect:display")
|
||||
end
|
||||
minetest.chat_send_player(name,"#PROTECTOR: this is your area, protector placed at(" .. ppos.x .. "," .. ppos.y .. "," .. ppos.z .. "). say /unprotect to unclaim area. ");
|
||||
end
|
||||
else
|
||||
local inv = player:get_inventory();
|
||||
local item = ItemStack("basic_protect:protector");
|
||||
if inv:contains_item("main",item) then
|
||||
minetest.set_node(ppos,{name = "basic_protect:protector"})
|
||||
basic_protect.protect_new(ppos,name);
|
||||
inv:remove_item("main",item)
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
})
|
||||
|
||||
|
||||
|
||||
-- GHOST TOWN : swapping of older areas
|
||||
|
||||
local swap = {};
|
||||
swap.manip = {};
|
||||
|
||||
--TODO: perhaps use buffer with voxelmanip to prevent memory leaks?
|
||||
--local swap_buffer = {};
|
||||
|
||||
swap.paste = function(pos_start,pos_end, reset) -- copy area around start and paste at end position. if reset = true then reset original area too
|
||||
-- place area to new location
|
||||
local r = basic_protect.radius*0.5; local ry = 2*r;
|
||||
local ppos = protector_position(pos_start);
|
||||
local pos1 = {x=ppos.x-r,y=ppos.y-2*r,z=ppos.z-r}
|
||||
local pos2 = {x=ppos.x+r-1,y=ppos.y+2*r-1,z=ppos.z+r-1}
|
||||
|
||||
-- load area data
|
||||
local manip1 = minetest.get_voxel_manip() -- VoxelManip object
|
||||
local emerged_pos1, emerged_pos2 = manip1:read_from_map(pos1, pos2) -- --Reads a chunk of map from the map containing the region formed by pos1 and pos2
|
||||
local area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2}) -- create new VoxelArea instance, needed for iterating over area in loop
|
||||
|
||||
local data = manip1:get_data() -- Gets the data read into the VoxelManip object
|
||||
local param2data = manip1:get_param2_data();
|
||||
local cdata = {}; -- copy data used for pasting later
|
||||
local c_air = minetest.get_content_id("air");local c_dirt = minetest.get_content_id("default:dirt");
|
||||
|
||||
-- copy
|
||||
local count=0;
|
||||
for i in area:iterp(pos1, pos2) do -- returns an iterator that returns indices inside VoxelArea
|
||||
local p = area:position(i);
|
||||
count = count+1; cdata[count] = {data[i],param2data[i],minetest.get_meta(p):to_table()}
|
||||
if reset then
|
||||
if p.y>ppos.y+1 then data[i] = c_air else data[i] = c_dirt end -- flatten original area
|
||||
end
|
||||
end
|
||||
manip1:set_data(data);manip1:write_to_map();manip1:update_map()
|
||||
|
||||
-- PASTING
|
||||
|
||||
ppos = protector_position(pos_end);
|
||||
pos1 = {x=ppos.x-r,y=ppos.y-2*r,z=ppos.z-r}
|
||||
pos2 = {x=ppos.x+r-1,y=ppos.y+2*r-1,z=ppos.z+r-1}
|
||||
|
||||
local manip2 = minetest.get_voxel_manip() -- VoxelManip object
|
||||
emerged_pos1, emerged_pos2 = manip2:read_from_map(pos1, pos2)
|
||||
area = VoxelArea:new({MinEdge=emerged_pos1, MaxEdge=emerged_pos2})
|
||||
data = manip2:get_data(); param2data = manip2:get_param2_data();
|
||||
|
||||
-- paste
|
||||
count = 0;
|
||||
for i in area:iterp(pos1, pos2) do
|
||||
local p = area:position(i);
|
||||
count = count +1; local cdataentry = cdata[count];
|
||||
if cdataentry then
|
||||
data[i] = cdataentry[1]
|
||||
param2data[i] = cdataentry[2]
|
||||
minetest.get_meta(p):from_table(cdataentry[3])
|
||||
end
|
||||
|
||||
end
|
||||
manip2:set_data(data); manip2:set_param2_data(param2data)
|
||||
manip2:write_to_map(); manip2:update_map()
|
||||
end
|
||||
|
||||
|
||||
swap.get_free_idx = function()
|
||||
local data = basic_protect.swap;
|
||||
if data == {} then return 1 end
|
||||
if #data< basic_protect.swap_size^2 then return 1+#data end -- not yet full, select next one
|
||||
|
||||
local t=minetest.get_gametime(); local idx=1;
|
||||
for i = 1,#data do
|
||||
-- owner, timestamp, start_pos, end_pos
|
||||
if data[i] == {} then return i end
|
||||
if data[i][2]<t then t = data[i][2]; idx = i end -- select oldest
|
||||
end
|
||||
return idx
|
||||
end
|
||||
|
||||
swap.idx2pos = function(idx) -- return position in ghosttown, idx from 1..n^2
|
||||
local i,j;
|
||||
local r = basic_protect.radius; local n = basic_protect.swap_size;
|
||||
idx = idx - 1; i = idx % n; j = (idx-i)/n;
|
||||
return {x = basic_protect.swap_pos.x + i*r, y = basic_protect.swap_pos.y, z = basic_protect.swap_pos.z + (j)*r};
|
||||
end
|
||||
|
||||
swap.insert = function(pos,idx) -- copy area to new position & flatten original area
|
||||
local r = basic_protect.radius; local ry = 2*r;
|
||||
local ppos = {x=round(pos.x/r+0.5)*r,y=round(pos.y/ry+0.5)*ry,z=round(pos.z/r+0.5)*r}; -- protector position
|
||||
-- protected area is coordinates in [ppos, ppos+r), 2r for vertical
|
||||
-- determine paste position
|
||||
--TODO
|
||||
if not idx then idx = swap.get_free_idx() else idx = idx % (basic_protect.swap_size^2) end
|
||||
local n = swap.swap_size; -- n x n
|
||||
local pos_end = swap.idx2pos(idx);
|
||||
|
||||
local meta = minetest.get_meta(ppos); local owner = meta:get_string("owner");
|
||||
if owner == "" then return end
|
||||
|
||||
basic_protect.swap[idx] = {owner, meta:get_int("timestamp"), pos, pos_end}
|
||||
swap.paste(pos,pos_end, true)
|
||||
minetest.chat_send_all("#protector: " .. owner .. "'s area at " .. ppos.x .. " " .. ppos.y .. " " .. ppos.z .. " moved to ghost town at " .. pos_end.x .. " " .. pos_end.y .. " " .. pos_end.z )
|
||||
|
||||
end
|
||||
|
||||
-- load data on server start
|
||||
--local modpath = minetest.get_modpath("basic_protect")
|
||||
local modpath = minetest.get_worldpath();
|
||||
local f = io.open(modpath .. "\\swap.dat", "r"); local swapstring = "";
|
||||
if f then swapstring = f:read("*all") or "";f:close() end
|
||||
|
||||
basic_protect.swap = minetest.deserialize(swapstring) or {};
|
||||
|
||||
minetest.register_on_shutdown(function() -- save swap data on shutdown
|
||||
local f = assert(io.open(modpath .. "\\swap.dat", "w"))
|
||||
local swapstring = f:write(minetest.serialize(basic_protect.swap))
|
||||
f:close()
|
||||
end
|
||||
)
|
||||
|
||||
-- lbm without run_at_every_load = true didnt run at all.. weird
|
||||
|
||||
if enable_swapping then
|
||||
minetest.register_abm({
|
||||
name = "basic_protect:swap",
|
||||
nodenames = {"basic_protect:protector"},
|
||||
interval = 60,
|
||||
chance = 1,
|
||||
--run_at_every_load = true,
|
||||
action = function(pos, node)
|
||||
--minetest.chat_send_all("D lbm swap attempt at " .. pos.x .. " " .. pos.y .. " " .. pos.z)
|
||||
if pos.y ~= 0 then return end -- only at ground level
|
||||
local absx = math.abs(pos.x); local absz = math.abs(pos.z);
|
||||
if absx > basic_protect.swap_range or absz > basic_protect.swap_range then return end -- no swap far from spawn
|
||||
if absx < 50 and absz < 50 then return end -- skip for central spawn
|
||||
|
||||
|
||||
-- check the "age" of protector and do swap if its old and not "nice"
|
||||
local meta = minetest.get_meta(pos);
|
||||
local timestamp = meta:get_int("timestamp");
|
||||
|
||||
local nice = meta:get_int("nice");
|
||||
local t = minetest.get_gametime();
|
||||
if nice == 0 and t-timestamp> basic_protect.swap_timeout then
|
||||
local owner = meta:get_string("owner");
|
||||
if minetest.get_player_privs(owner).kick then -- skip moving moderator's area
|
||||
minetest.chat_send_all("#protector: skipped moving non-nice area at " .. pos.x .. " " .. pos.y .. " " .. pos.z .. " to ghost town, owner is moderator " .. owner)
|
||||
meta:set_int("nice",1)
|
||||
return
|
||||
end
|
||||
minetest.chat_send_all("#protector: moving non-nice old (age " .. (t-timestamp)/basic_protect.swap_timeout .. " timeouts, owner " .. owner .. ") into ghost town");
|
||||
swap.insert(pos);
|
||||
return;
|
||||
end
|
||||
end,
|
||||
})
|
||||
end
|
||||
|
||||
minetest.register_chatcommand("mark_nice", {
|
||||
description = "Mark nearby protector as nice",
|
||||
privs = {
|
||||
kick = true
|
||||
},
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
local pos = player:getpos();
|
||||
local r = basic_protect.radius; local ry = 2*r;
|
||||
local ppos = protector_position(pos); -- protector position
|
||||
if minetest.get_node(ppos).name ~= "basic_protect:protector" then return end
|
||||
local meta = minetest.get_meta(ppos); meta:set_int("nice", 1)
|
||||
minetest.chat_send_player(name,"#protector: area " .. ppos.x .. " " .. ppos.y .. " " .. ppos.z .. " marked as nice.");
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
minetest.register_chatcommand("swap_insert", {
|
||||
description = "swap_insert idx, Insert nearby protector into swap area at position idx and reset original area",
|
||||
privs = {
|
||||
privs = true
|
||||
},
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
local pos = player:getpos();
|
||||
local r = basic_protect.radius; local ry = 2*r;
|
||||
local ppos = protector_position(pos)
|
||||
if minetest.get_node(ppos).name ~= "basic_protect:protector" then return end
|
||||
swap.insert(pos, tonumber(param));
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
minetest.register_chatcommand("swap_paste", {
|
||||
description = "swap_paste x y z, Paste nearby area to location containing x y z",
|
||||
privs = {
|
||||
privs = true
|
||||
},
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
local words = {};
|
||||
for word in string.gmatch(param, "%S+") do words[#words+1] = tonumber(word) end
|
||||
if words[1] and words[2] and words[3] then else return end
|
||||
local pos1 = player:getpos();
|
||||
local pos2 = {x = words[1], y = words[2], z= words[3]}
|
||||
swap.paste(pos1,pos2);
|
||||
minetest.chat_send_all("#protector: area at " .. pos1.x .. " " .. pos1.y .. " " .. pos2.z .. " pasted to " .. pos2.x .. " " .. pos2.y .. " " .. pos2.z )
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
minetest.register_chatcommand("swap_restore", {
|
||||
description = "swap_restore, regenerate nearby area with mapgen",
|
||||
privs = {
|
||||
privs = true
|
||||
},
|
||||
func = function(name, param)
|
||||
local player = minetest.get_player_by_name(name); if not player then return end
|
||||
local r = basic_protect.radius*0.5; local ry = 2*r;
|
||||
local pos = player:getpos();
|
||||
local ppos = protector_position(pos);
|
||||
local pos1 = {x=ppos.x-r,y=ppos.y-2*r,z=ppos.z-r}
|
||||
local pos2 = {x=ppos.x+r-1,y=ppos.y+2*r-1,z=ppos.z+r-1}
|
||||
minetest.delete_area(pos1, pos2)
|
||||
--minetest.emerge_area(pos1, pos2)
|
||||
end
|
||||
}
|
||||
)
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
--TODO
|
||||
-- minetest.register_chatcommand("restore_nice", {
|
||||
-- description = "Restore nearby area from swap back to the world",
|
||||
-- privs = {
|
||||
-- interact = true
|
||||
-- },
|
||||
-- func = function(name, param)
|
||||
-- end
|
||||
-- }
|
||||
-- )
|
After Width: | Height: | Size: 96 B |
After Width: | Height: | Size: 743 B |
After Width: | Height: | Size: 1.2 KiB |
|
@ -0,0 +1,29 @@
|
|||
BASIC_ROBOT: lightweight robot mod for multiplayer
|
||||
minetest 0.4.14
|
||||
(c) 2016 rnd
|
||||
|
||||
MANUAL:
|
||||
1. ingame help: right click spawner (basic_robot:spawner) and click help button
|
||||
|
||||
---------------------------------------------------------------------
|
||||
This program is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
This program is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
----------------------------------------------------------------------
|
||||
|
||||
|
||||
GAMEPLAY:
|
||||
|
||||
- robot has limited operations available every run ( 1 run per 1 second).
|
||||
- while using for loops, while loops or function calls it is limited to default 48 such code executions per run
|
||||
- while using 'physical' operations like move/dig robot has (default) 10 operations available per run. Default costs are
|
||||
move=2, dig = 6, insert = 2, place = 2, machine.generate = 6, machine.smelt = 6, machine.grind = 6,
|
|
@ -0,0 +1 @@
|
|||
default
|
|
@ -0,0 +1,54 @@
|
|||
//lua minetest.get_player_by_name("rnd"):set_properties({visual_size = {x=1,y=1}})
|
||||
|
||||
local name = "rnd"; local player = minetest.get_player_by_name(name); player:set_properties({visual = "upright_sprite"});player:set_properties({textures={"default_tool_diamondpick.png"}})
|
||||
|
||||
|
||||
// change to robot
|
||||
local name = "rnd"; local player = minetest.get_player_by_name(name); player:set_properties({visual = "cube"});player:set_properties({textures={"arrow.png^[transformR90","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png","face.png","basic_machine_side.png"}});player:set_properties({collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}})
|
||||
|
||||
//LawnGreen
|
||||
|
||||
local name = "rnd"; local player = minetest.get_player_by_name(name); player:set_properties({visual = "sprite"});player:set_properties({textures={"farming_bottle_ethanol.png"}});player:set_properties({collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}});player:set_properties({visual_size = {x=2,y=2}})
|
||||
|
||||
//farming_blueberry_muffin
|
||||
local name = "rnd"; local player = minetest.get_player_by_name(name); player:set_properties({visual = "cube"});player:set_properties({textures={"farming_pumpkin_face_off.png","farming_pumpkin_face_off.png","farming_pumpkin_face_off.png","farming_pumpkin_face_off.png","farming_pumpkin_face_off.png","farming_pumpkin_face_off.png"}});player:set_properties({collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}})
|
||||
|
||||
--nyan cat
|
||||
//lua local name = "rnd"; local player = minetest.get_player_by_name(name); player:set_properties({visual = "cube"});player:set_properties({textures = {"nyancat_side.png", "nyancat_side.png", "nyancat_side.png","nyancat_side.png", "nyancat_front.png", "nyancat_back.png"}});player:set_properties({collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}})
|
||||
|
||||
|
||||
|
||||
local name = "rnd1"; local player = minetest.get_player_by_name(name); player:set_nametag_attributes({text = "Friend of Giorge"});
|
||||
|
||||
//lua local player = minetest.get_player_by_name("pro2");minetest.sound_play("nyan",{object = player,gain = 1.0,max_hear_distance = 8,loop = false})
|
||||
|
||||
|
||||
//lua local player = minetest.get_player_by_name("rnd");player:set_properties({visual = "mesh",textures = {"mobs_spider.png"},mesh = "mobs_spider.x",visual_size = {x=7,y=7}})
|
||||
|
||||
|
||||
//lua local player = minetest.get_player_by_name("rnd");player:set_properties({visual = "mesh",textures = {"mobs_dungeon_master.png"},mesh = "mobs_dungeon_master.b3d",visual_size = {x=1,y=1}})
|
||||
|
||||
//lua local player = minetest.get_player_by_name("best");player:set_properties({visual = "mesh",textures = {"zmobs_mese_monster.png"},mesh = "zmobs_mese_monster.x",visual_size = {x=1,y=1}})
|
||||
|
||||
//lua local player = minetest.get_player_by_name("rnd1");player:set_properties({visual = "mesh",textures = {"mobs_oerkki.png"},mesh = "mobs_oerkki.b3d",visual_size = {x=1,y=1}})
|
||||
|
||||
//lua local player = minetest.get_player_by_name("rnd1");player:set_properties({visual = "mesh",textures = {"mobs_stone_monster.png"},mesh = "mobs_stone_monster.b3d",visual_size = {x=1,y=1}})
|
||||
|
||||
|
||||
mesh = "zmobs_lava_flan.x",
|
||||
textures = {
|
||||
{"zmobs_lava_flan.png"},
|
||||
{"zmobs_lava_flan2.png"},
|
||||
{"zmobs_lava_flan3.png"},
|
||||
},
|
||||
----------------------------------------------
|
||||
|
||||
|
||||
|
||||
//lua local player = minetest.get_player_by_name("towner");player:set_physics_override({speed=0.05})
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,350 @@
|
|||
local robogui = basic_robot.gui;
|
||||
|
||||
-- GUI
|
||||
|
||||
-- robogui GUI START ==================================================
|
||||
-- a simple table of entries: [guiName] = {getForm = ... , show = ... , response = ... , guidata = ...}
|
||||
robogui.register = function(def)
|
||||
robogui[def.guiName] = {getForm = def.getForm, show = def.show, response = def.response, guidata = def.guidata or {}}
|
||||
end
|
||||
minetest.register_on_player_receive_fields(
|
||||
function(player, formname, fields)
|
||||
local gui = robogui[formname];
|
||||
if gui then gui.response(player,formname,fields) end
|
||||
end
|
||||
)
|
||||
-- robogui GUI END ====================================================
|
||||
|
||||
|
||||
--- DEMO of simple form registration, all in one place, clean and tidy
|
||||
-- adapted for use with basic_robot
|
||||
|
||||
|
||||
-- if not basic_gui then
|
||||
-- basic_gui = _G.basic_gui; minetest = _G.minetest;
|
||||
-- basic_gui.register({
|
||||
-- guiName = "mainWindow", -- formname
|
||||
|
||||
-- getForm = function(form_id, update) -- actual form design
|
||||
-- local gui = basic_gui["mainWindow"];
|
||||
-- local formdata = gui.guidata[form_id]
|
||||
|
||||
-- if not formdata then -- init
|
||||
-- gui.guidata[form_id] = {}; formdata = gui.guidata[form_id]
|
||||
-- formdata.sel_tab = 1;
|
||||
-- formdata.text = "default";
|
||||
-- formdata.form = "";
|
||||
-- end
|
||||
-- if not update then return formdata.form end
|
||||
|
||||
-- local sel_tab = formdata.sel_tab;
|
||||
-- local text = formdata.text;
|
||||
|
||||
-- formdata.form = "size[8,9]"..
|
||||
-- "label[0,0;basic_gui_DEMO, form_id " .. form_id .. ", tab " .. sel_tab .. "]"..
|
||||
-- "button[0,1;2,1;gui_button;CLICK ME]"..
|
||||
-- "textarea[0.25,2;2,1;gui_textarea;text;" .. text .. "]"..
|
||||
-- "tabheader[0,0;tabs;tab1,table demo,tab3;".. sel_tab .. ";true;true]"..
|
||||
-- "list[current_player;main;0,5;8,4;]";
|
||||
|
||||
-- if sel_tab == 2 then
|
||||
-- formdata.form = "size[12,6.5;true]" ..
|
||||
-- "tablecolumns[color;tree;text,width=32;text]" ..
|
||||
-- "tableoptions[background=#00000000;border=false]" ..
|
||||
-- "field[0.3,0.1;10.2,1;search_string;;" .. minetest.formspec_escape(text) .. "]" ..
|
||||
-- "field_close_on_enter[search_string;false]" ..
|
||||
-- "button[10.2,-0.2;2,1;search;" .. "Search" .. "]" ..
|
||||
-- "table[0,0.8;12,4.5;list_settings;"..
|
||||
-- "#FFFF00,1,TEST A,,"..
|
||||
-- "#FFFF00,2,TEST A,,"..
|
||||
-- ",3,test a,value A,"..
|
||||
-- "#FFFF00,1,TEST B,,"..
|
||||
-- ",2,test b,value B,"
|
||||
-- end
|
||||
|
||||
-- formdata.info = "This information comes with the form post"; -- extra data set
|
||||
-- end,
|
||||
|
||||
-- show = function(player_name,update) -- this is used to show form to user
|
||||
-- local formname = "mainWindow";
|
||||
-- local form_id = player_name; -- each player has his own window!
|
||||
-- local gui = basic_gui[formname];
|
||||
-- local formdata = gui.guidata[form_id]; -- all form data for this id gets stored here
|
||||
-- if update then gui.getForm(form_id,true); formdata = gui.guidata[form_id]; end
|
||||
-- minetest.show_formspec(player_name, "mainWindow", formdata.form)
|
||||
-- end,
|
||||
|
||||
-- response = function(player,formname, fields) -- this handles response
|
||||
|
||||
-- local player_name = player:get_player_name();
|
||||
-- local form_id = player_name;
|
||||
-- local gui = basic_gui[formname];
|
||||
-- local formdata = gui.guidata[form_id]; --gui.guidata[form_id];
|
||||
-- if not formdata then say("err") return end --error!
|
||||
|
||||
-- if fields.gui_textarea then
|
||||
-- formdata.text = fields.gui_textarea or ""
|
||||
-- end
|
||||
|
||||
|
||||
-- if fields.tabs then
|
||||
-- formdata.sel_tab = tonumber(fields.tabs) or 1;
|
||||
|
||||
-- gui.show(player_name,true) -- update and show form
|
||||
-- else
|
||||
|
||||
-- local form = "size[5,5]" ..
|
||||
-- "label[0,0;you interacted with demo form, fields : " ..
|
||||
-- _G.minetest.formspec_escape(_G.dump(fields)) .. "]"..
|
||||
-- "label[0,4;" .. formdata.info .. "]"
|
||||
-- _G.minetest.show_formspec(player_name,"basic_response", form);
|
||||
-- end
|
||||
-- end,
|
||||
|
||||
|
||||
-- })
|
||||
-- end
|
||||
|
||||
local help_address = {}; -- array containing current page name for player
|
||||
local help_pages = {
|
||||
["main"] = {
|
||||
" === ROBOT HELP - MAIN SCREEN === ","",
|
||||
"[Commands reference] display list of robot commands",
|
||||
"[Lua basics] short introduction to lua","",
|
||||
"INSTRUCTIONS: double click links marked with []",
|
||||
"------------------------------------------","",
|
||||
"basic_robot version " .. basic_robot.version,
|
||||
"(c) 2016 rnd",
|
||||
},
|
||||
|
||||
["Lua basics"] = {
|
||||
"back to [main] menu",
|
||||
"BASIC LUA SYNTAX","",
|
||||
" IF CONDITIONAL: if x==1 then A else B end",
|
||||
" FOR LOOP: for i = 1, 5 do something end",
|
||||
" WHILE LOOP: while i<6 do A; i=i+1; end",
|
||||
' ARRAYS: myTable1 = {1,2,3}, myTable2 = {["entry1"]=5, ["entry2"]=1}',
|
||||
' access table entries with myTable1[1] or myTable2.entry1 or',
|
||||
' myTable2["entry1"]',
|
||||
" FUNCTIONS: f = function(x) return 2*x end, call like f(3)",
|
||||
" STRINGS: name = \"rnd\" or name = [[rnd ]] (multiline string)",
|
||||
" string.concat({string1,string2,...}, separator) returns",
|
||||
" concatenated string with maxlength is 1024",
|
||||
},
|
||||
|
||||
["Commands reference"] = {
|
||||
"back to [main] menu",
|
||||
|
||||
"ROBOT COMMANDS","",
|
||||
" 1. [MOVEMENT DIGGING PLACING NODE SENSING]",
|
||||
" 2. [TAKE INSERT AND INVENTORY]",
|
||||
" 3. [BOOKS CODE TEXT WRITE OR READ]",
|
||||
" 4. [PLAYERS]",
|
||||
" 5. [ROBOT SPEAK LABEL APPEARANCE OTHER]",
|
||||
" 6. [KEYBOARD AND USER INTERACTIONS]",
|
||||
" 7. [TECHNIC FUNCTIONALITY]",
|
||||
" 8. [CRYPTOGRAPHY]",
|
||||
" 9. [PUZZLE]",
|
||||
},
|
||||
|
||||
["MOVEMENT DIGGING PLACING NODE SENSING"] = {
|
||||
"back to [Commands reference]",
|
||||
"MOVEMENT,DIGGING, PLACING, NODE SENSING","",
|
||||
" move.direction(), where direction is: left, right, forward, backward,",
|
||||
" up, down, left_down, right_down, forward_down, backward_down,",
|
||||
" left_up, right_up, forward_up, backward_up",
|
||||
" boost(v) sets robot velocity, -6<v<6, if v = 0 then stop",
|
||||
" turn.left(), turn.right(), turn.angle(45)",
|
||||
" dig.direction()",
|
||||
" place.direction(\"default:dirt\", optional orientation param)",
|
||||
" read_node.direction() tells you names of nodes",
|
||||
},
|
||||
|
||||
["TAKE INSERT AND INVENTORY"] = {
|
||||
"back to [Commands reference]",
|
||||
"TAKE INSERT AND INVENTORY","",
|
||||
" insert.direction(item, inventory) inserts item from robot inventory to",
|
||||
" target inventory",
|
||||
" check_inventory.direction(itemname, inventory,index) looks at node and ",
|
||||
" returns false/true, direction can be self, if index>0 it returns itemname.",
|
||||
" if itemname == \"\" it checks if inventory empty",
|
||||
" activate.direction(mode) activates target block",
|
||||
" pickup(r) picks up all items around robot in radius r<8 and returns list",
|
||||
" or nil",
|
||||
" craft(item,idx,mode) crafts item if required materials are present in",
|
||||
" inventory, mode = 1 returns recipe, optional recipe idx",
|
||||
" take.direction(item, inventory) takes item from target inventory into",
|
||||
" robot inventory",
|
||||
},
|
||||
|
||||
["BOOKS CODE TEXT WRITE OR READ"] = {
|
||||
"back to [Commands reference]",
|
||||
"BOOKS CODE TEXT WRITE OR READ","",
|
||||
" title,text=book.read(i) returns title,contents of book at i-th position in",
|
||||
" library",
|
||||
" book.write(i,title,text) writes book at i-th position at spawner library",
|
||||
" code.run(text) compiles and runs the code in sandbox (privs only)",
|
||||
" code.set(text) replaces current bytecode of robot",
|
||||
" find_nodes(\"default:dirt\",3) returns distance to node in radius 3 around",
|
||||
" robot, or false if none found",
|
||||
" read_text.direction(stringname,mode) reads text of signs, chests and",
|
||||
" other blocks, optional stringname for other meta, mode 1 to read number",
|
||||
" write_text.direction(text,mode) writes text to target block as infotext",
|
||||
},
|
||||
|
||||
["PLAYERS"] = {
|
||||
"back to [Commands reference]",
|
||||
"PLAYERS","",
|
||||
" find_player(3,pos) finds players in radius 3 around robot(position) and",
|
||||
" returns list of found player names, if none returns nil",
|
||||
" attack(target) attempts to attack target player if nearby",
|
||||
" grab(target) attempt to grab target player if nearby and returns",
|
||||
" true if succesful",
|
||||
" player.getpos(name) return position of player, player.connected()",
|
||||
" returns list of connected players names",
|
||||
},
|
||||
|
||||
["ROBOT SPEAK LABEL APPEARANCE OTHER"] = {
|
||||
"back to [Commands reference]",
|
||||
"ROBOT","",
|
||||
" say(\"hello\") will speak",
|
||||
" self.listen(0/1) (de)attaches chat listener to robot",
|
||||
" speaker, msg = self.listen_msg() retrieves last chat message if robot",
|
||||
" has listener attached",
|
||||
" self.send_mail(target,mail) sends mail to target robot",
|
||||
" sender,mail = self.read_mail() reads mail, if any",
|
||||
" self.pos() returns table {x=pos.x,y=pos.y,z=pos.z}",
|
||||
" self.name() returns robot name",
|
||||
" self.operations() returns remaining robot operations",
|
||||
" self.set_properties({textures=.., visual=..,visual_size=.., , ) sets visual",
|
||||
" appearance",
|
||||
" self.set_animation(anim_start,anim_end,anim_speed,anim_stand_start)",
|
||||
" set mesh,animation",
|
||||
" self.spam(0/1) (dis)enable message repeat to all",
|
||||
" self.remove() stops program and removes robot object",
|
||||
" self.reset() resets robot position",
|
||||
" self.spawnpos() returns position of spawner block",
|
||||
" self.viewdir() returns vector of view for robot",
|
||||
" self.fire(speed, pitch,gravity, texture, is_entity) fires a projectile",
|
||||
" from robot. if is_entity false (default) it fires particle.",
|
||||
" self.fire_pos() returns last hit position",
|
||||
" self.label(text) changes robot label",
|
||||
" self.display_text(text,linesize,size) displays text instead of robot face,",
|
||||
" if no size just return texture string",
|
||||
" self.sound(sample,volume, opt. pos) plays sound named 'sample' at",
|
||||
" robot, location (optional pos)",
|
||||
" rom is aditional table that can store persistent data, like rom.x=1",
|
||||
},
|
||||
|
||||
["KEYBOARD AND USER INTERACTIONS"] = {
|
||||
"back to [Commands reference]",
|
||||
"KEYBOARD","",
|
||||
" EVENTS : place spawner at coordinates (r*i,2*r*j+1,r*k) to monitor",
|
||||
" events. value of r is ".. basic_robot.radius,
|
||||
" keyboard.get() returns table {x=..,y=..,z=..,puncher = .. , type = .. }",
|
||||
" for keyboard event",
|
||||
" keyboard.set(pos,type) set key at pos of type 0=air,1-6,7-15,16-271,",
|
||||
" limited to range 10 around spawner",
|
||||
" keyboard.read(pos) return node name at pos",
|
||||
},
|
||||
|
||||
["TECHNIC FUNCTIONALITY"] = {
|
||||
"back to [Commands reference]",
|
||||
"TECHNIC FUNCTIONALITY","",
|
||||
" All commands are in namespace 'machine', for example machine.energy()",
|
||||
" most functions return: ok, error = true or nil, error",
|
||||
" To use some commands fully robot must be upgraded. 1 upgrade is",
|
||||
" goldblock+meseblock+diamonblock.",
|
||||
" energy() displays available energy",
|
||||
" generate_power(fuel, amount) = energy, attempt to generate power",
|
||||
" from fuel material. If amount>0 try generate amount of power",
|
||||
" using builtin generator - this requires 40 upgrades for each",
|
||||
" 1 amount",
|
||||
" smelt(input,amount) = progress/true. works as a furnace, if amount>0",
|
||||
" try to use power to smelt - requires 10 upgrades for each 1 amount,",
|
||||
" energy cost of smelt is: 1/40*(1+amount)",
|
||||
" grind(input) - grinds input material, requires upgrades for harder",
|
||||
" materials",
|
||||
" compress(input) - requires upgrades - energy intensive process",
|
||||
" transfer_power(amount,target_robot_name)",
|
||||
},
|
||||
|
||||
["CRYPTOGRAPHY"] = {
|
||||
"back to [Commands reference]",
|
||||
"CRYPTOGRAPHY","",
|
||||
" namespace 'crypto'",
|
||||
" encrypt(input,password) returns encrypted text, password is any string",
|
||||
" decrypt(input,password) attempts to decrypt encrypted text",
|
||||
" scramble(input,randomseed,sgn) (de)permutes text randomly according",
|
||||
" to sgn = -1,1",
|
||||
" basic_hash(input,n) returns simple mod hash from string input within",
|
||||
" range 0...n-1",
|
||||
},
|
||||
|
||||
["PUZZLE"] = {
|
||||
"back to [Commands reference]",
|
||||
"PUZZLE","",
|
||||
" namespace 'puzzle' - need puzzle priv",
|
||||
" set_triggers({trigger1, trigger2,...}) sets and initializes spatial triggers",
|
||||
" check_triggers(pname) check if player is close to any trigger and run",
|
||||
" that trigger",
|
||||
" set_node(pos,node) - set any node, limited to current protector",
|
||||
" region",
|
||||
" get_player(pname) return player objRef in current protector region",
|
||||
" chat_send_player(pname, text)",
|
||||
" get_node_inv(pos) / get_player_inv(pname) - return inventories of nodes",
|
||||
" /players in current mapblock",
|
||||
" get_meta(pos) - return meta of target position",
|
||||
" get_gametime() - return current gametime",
|
||||
" ItemStack(itemname) returns ItemRef to be used with inventory",
|
||||
" count_objects(pos,radius)",
|
||||
" pdata contains puzzle data like .triggers and .gamedata",
|
||||
" add_particle(def)"
|
||||
}
|
||||
|
||||
}
|
||||
for k,v in pairs(help_pages) do
|
||||
local pages = help_pages[k]; for i = 1,#pages do pages[i] = minetest.formspec_escape(pages[i]) end
|
||||
end
|
||||
|
||||
|
||||
local robot_show_help = function(pname) --formname: robot_help
|
||||
local address = help_address[pname] or "main";
|
||||
|
||||
--minetest.chat_send_all("D DISPLAY HELP for ".. address )
|
||||
local pages = help_pages[address];
|
||||
|
||||
local content = table.concat(pages,",")
|
||||
local size = 9; local vsize = 8.75;
|
||||
|
||||
local form = "size[" .. size .. "," .. size .. "] textlist[-0.25,-0.25;" .. (size+1) .. "," .. (vsize+1) .. ";wiki;".. content .. ";1]";
|
||||
--minetest.chat_send_all("D " .. form)
|
||||
minetest.show_formspec(pname, "robot_help", form)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
robogui["robot_help"] = {
|
||||
response = function(player,formname,fields)
|
||||
local name = player:get_player_name()
|
||||
|
||||
local fsel = fields.wiki;
|
||||
if fsel and string.sub(fsel,1,3) == "DCL" then
|
||||
local sel = tonumber(string.sub(fsel,5)) or 1; -- selected line
|
||||
local address = help_address[name] or "main";
|
||||
local pages = help_pages[address];
|
||||
|
||||
local link = string.match(pages[sel] or "", "\\%[([%w%s]+)\\%]")
|
||||
if help_pages[link] then
|
||||
help_address[name] = link;
|
||||
robot_show_help(name)
|
||||
end
|
||||
end
|
||||
end,
|
||||
|
||||
getForm = function(player_name)
|
||||
|
||||
end,
|
||||
|
||||
show = robot_show_help,
|
||||
};
|
|
@ -0,0 +1,175 @@
|
|||
-- 1. MEMORY MANAGEMENT using array with addresses
|
||||
-- note: this is all done "automatic" in c with malloc
|
||||
|
||||
mem = {};
|
||||
mem.size = 10; -- how many available addresses in memory for each of internally used arrays
|
||||
mem.freestk = {}; -- stack of free addresses in memory
|
||||
mem.stkidx = mem.size;
|
||||
for i = 1,mem.size do mem.freestk[i]=mem.size-i+1 end -- so: freestk = {memsize, memsize-1,...., 2, 1 } and head is at last one
|
||||
|
||||
mem.allocate = function() -- pop free spot from stack
|
||||
if mem.stkidx>0 then
|
||||
mem.stkidx = mem.stkidx -1
|
||||
return mem.freestk[mem.stkidx+1]
|
||||
end
|
||||
end
|
||||
|
||||
mem.free = function(addr) -- release spot and mark it as free
|
||||
if mem.stkidx>=mem.size then -- cant release anymore, all is free
|
||||
return
|
||||
end
|
||||
mem.stkidx = mem.stkidx +1;
|
||||
mem.freestk[mem.stkidx ] = addr;
|
||||
end
|
||||
|
||||
|
||||
-- 2. BALANCED BINARY SEARCH TREES USING POINTERS created with above MEMORY MANAGEMENT
|
||||
|
||||
idx = mem.allocate();
|
||||
|
||||
tree = {};
|
||||
tree.data = { root = 0, count = 0, -- current root, element count
|
||||
left = {0}, parent = {0}, right = {0}, -- links
|
||||
key = {0}, -- value
|
||||
heightl = {0}, heightr = {0} -- needed for balancing
|
||||
};
|
||||
-- root: idx of root element, count: how many elements in tree
|
||||
-- 6 arrays with data for node stored at address i in memory:
|
||||
-- left[i] == left child, right[i] = right child, parent[i] = parent, key[i] = stored value, if value left[i]== 0 then no left child...
|
||||
-- heightl[i] = height of left subtree, heightr[i] = height of right subtree
|
||||
-- NOTE: initially a root node is added
|
||||
|
||||
tree.insert = function (value)
|
||||
local data = tree.data;
|
||||
local idx = data.root;
|
||||
if data.count == 0 then data.key[idx] = key; data.count = 1 return true end -- just set root key and exit
|
||||
|
||||
local key;
|
||||
local cidx; -- child idx
|
||||
|
||||
|
||||
while (true) do -- insert into tree by walking deeper and doing comparisons
|
||||
|
||||
key = data.key[idx];
|
||||
if value<key then
|
||||
cidx = data.left[idx];
|
||||
else
|
||||
cidx = data.right[idx];
|
||||
end
|
||||
|
||||
if cidx == 0 then -- we hit nonexistent child, lets insert here
|
||||
cidx = mem.allocate();
|
||||
if not cidx then return "out of space" end
|
||||
data.count = data.count+1
|
||||
data.key[cidx] = value;
|
||||
data.parent[cidx]=idx;data.left[cidx]=0;data.right[cidx]=0;
|
||||
data.heightl[cidx]=0;data.heightr[cidx]=0;
|
||||
|
||||
-- update all heights for parents and find possible violation of balance property
|
||||
local vidx = 0; -- index where violation happens
|
||||
local vdir = 0; -- type of violation: 1: in left tree , 2: in right tree
|
||||
while (idx~=0) do
|
||||
if data.left[idx] == cidx then
|
||||
data.heightl[idx]=data.heightl[idx]+1
|
||||
else
|
||||
data.heightr[idx]=data.heightr[idx]+1;
|
||||
end
|
||||
|
||||
if data.heightl[idx]>data.heightr[idx]+1 then
|
||||
vidx = idx; vdir = 1
|
||||
elseif data.heightr[idx]>data.heightl[idx]+1 then
|
||||
vidx = idx; vdir = 2
|
||||
end
|
||||
|
||||
cidx = idx; -- set new child
|
||||
idx = data.parent[idx]; -- set new parent
|
||||
end
|
||||
if vidx~=0 then
|
||||
say("violation vidx " .. vidx .. " direction " .. vdir)
|
||||
-- TO DO: apply internal tree rotation to restore balance
|
||||
if vdir == 1 then -- left violation
|
||||
--[[ need to reconnect 3 nodes:
|
||||
D <- vidx C
|
||||
/ \ / \
|
||||
C E => A D
|
||||
/ \ / \
|
||||
A B B E
|
||||
CHANGES:
|
||||
C: new parent is old parent of D, new children are A,D
|
||||
B: new parent D,
|
||||
D: new parent is C, new children are B,E
|
||||
--]]
|
||||
local Didx = vidx; local Dpar = data.parent[Didx];
|
||||
local Cidx = data.left[Didx];
|
||||
local Bidx = data.right[Cidx];
|
||||
local Aidx = data.left[Cidx];
|
||||
|
||||
data.parent[Cidx] = Dpar;data.left[Cidx] = Aidx;data.right[Cidx] = Didx;
|
||||
data.parent[Bidx] = Didx;
|
||||
data.parent[Didx] = Cidx;data.left[Didx] = Bidx;
|
||||
|
||||
|
||||
else -- right violation
|
||||
--[[ need to reconnect 3 nodes:
|
||||
B <-vidx C
|
||||
/ \ / \
|
||||
A C => B E
|
||||
/ \ / \
|
||||
D E A D
|
||||
CHANGES:
|
||||
B: new parent C, new children A,D
|
||||
C: new parent is old parent of B, new children are B,E
|
||||
D: new parent is B
|
||||
--]]
|
||||
local Bidx = vidx; local Bpar = data.parent[Bidx];
|
||||
local Cidx = data.right[Bidx];
|
||||
local Didx = data.left[Cidx];
|
||||
|
||||
data.parent[Bidx] = Cidx;data.right[Bidx] = data.left[Cidx]
|
||||
data.parent[Cidx] = Bpar;data.left[Cidx] = Bidx;
|
||||
data.parent[Didx] = Bidx;
|
||||
end
|
||||
end
|
||||
|
||||
else
|
||||
-- we go deeper
|
||||
idx = cidx;
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
tree.find = function(value)
|
||||
local idx = data.root;
|
||||
while (idx~=0) do
|
||||
key = data.key[idx];
|
||||
if key == value then return idx end
|
||||
if value<key then
|
||||
idx = data.left[idx];
|
||||
else
|
||||
idx = data.right[idx];
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tree.next = function(idx)
|
||||
local right = data.right[idx];
|
||||
local nidx = 0;
|
||||
if right~=0 then
|
||||
--TO DO: smallest idx in right subtree
|
||||
else
|
||||
if data.parent[idx]==0 then
|
||||
return 0
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
tree.remove = function(idx)
|
||||
--TO DO :
|
||||
-- 1.find next element
|
||||
-- put it where idx was and move subtree..
|
||||
end
|
||||
|
||||
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,242 @@
|
|||
--COMMAND ROBOT by rnd, v2, adapted for skyblock
|
||||
if not s then
|
||||
self.listen(1)
|
||||
s=1;_G.minetest.forceload_block(self.pos(),true)
|
||||
self.spam(1)
|
||||
users = {["rnd"]=3,["rnd1"]=3,["Giorge"]=1,["quater"]=1,["UltimateNoob"]=1,["reandh3"]=1,["karo"]=1,["Fedon"]=1,["DS"]=2,
|
||||
["Arcelmi"]=1,["Gregorro"]=1,Mokk = 1, Evandro010 = 1}
|
||||
cmdlvl = {["kill"]=2,["tp"]=3,["heal"]=1, ["rename"]=1,["jump"]=1,["givediamond"]=3, ["msg"]=1,["calc"]=0, ["acalc"]=3,["run"]=3, ["shutdown"] = 3,["sayhi"]=0, ["day"] = 1};
|
||||
|
||||
tpr = {};
|
||||
|
||||
cmds = {
|
||||
help =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
local arg = param[2];
|
||||
if not arg then
|
||||
say(colorize("red","OPEN INVENTORY AND READ 'Quests'. YOU GET REWARD AND BETTER STUFF FOR COMPLETING QUESTS. Say /spawn to get to spawn area."))
|
||||
return
|
||||
end
|
||||
|
||||
if arg and cmds[arg] then
|
||||
local text = cmds[arg]["docs"];
|
||||
if text then say(text) end
|
||||
else
|
||||
--say(colorize("red","commands") .. colorize("LawnGreen",": 0 status, 2 kill $name, 3 tp $name1 $name2, 1 heal $name, 1 day, 1 rename $name $newname, 3 givediamond $name, 1 msg $name $message, 0 sayhi, 0 calc $formula, 3 acalc $formula, 3 run $expr"))
|
||||
local msg = ""
|
||||
for k,v in pairs(cmds) do
|
||||
msg = msg .. v.level .. " " .. k .. ", "
|
||||
end
|
||||
say(colorize("red","commands") .. " " .. colorize("LawnGreen", msg))
|
||||
end
|
||||
end,
|
||||
docs = "Displays available commands. 'help command' shows help about particular command"
|
||||
},
|
||||
|
||||
status =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
local usr = param[2] or speaker;
|
||||
local id = _G.skyblock.players[usr].id;
|
||||
local pos = _G.skyblock.get_island_pos(id)
|
||||
minetest.chat_send_all(minetest.colorize("yellow",
|
||||
usr .. " data : permission level " .. (users[usr] or 0).. ", island " .. id .." (at " .. pos.x .. " " .. pos.z .. "), skyblock level " .. _G.skyblock.players[usr].level
|
||||
))
|
||||
end,
|
||||
docs = "status name, Show target(speaker if none) level, which determines access privilege."
|
||||
},
|
||||
|
||||
kill =
|
||||
{
|
||||
level = 2,
|
||||
run = function(param)
|
||||
local name = param[2]; if not name then return end
|
||||
local player = _G.minetest.get_player_by_name(name);
|
||||
if player then
|
||||
if (users[name] or 0)<=(users[speaker] or 0) then player:set_hp(0) end
|
||||
end
|
||||
end,
|
||||
docs = "kill name; kills target player",
|
||||
},
|
||||
|
||||
tp =
|
||||
{
|
||||
level = 2,
|
||||
run = function(param)
|
||||
local player1 = _G.minetest.get_player_by_name(param[2] or "");
|
||||
local player2 = _G.minetest.get_player_by_name(param[3] or "");
|
||||
if player1 and player2 then if (users[param[2]] or 0)<=(users[speaker] or 0) then player1:setpos(player2:getpos()) end end
|
||||
end,
|
||||
docs = "tp name1 name2; teleports player name2 to name2",
|
||||
},
|
||||
|
||||
tpr =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
local name = param[2] or "";
|
||||
local player1 = _G.minetest.get_player_by_name(name);
|
||||
if player1 then tpr = {speaker, name} else return end
|
||||
_G.minetest.chat_send_player(name,minetest.colorize("yellow","#TELEPORT REQUEST: say tpy to teleport " .. speaker .. " to you."))
|
||||
|
||||
end,
|
||||
docs = "tpr name; request teleport to target player",
|
||||
},
|
||||
|
||||
tpy =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
if speaker == tpr[2] then
|
||||
local player1 = _G.minetest.get_player_by_name(tpr[1] or "");
|
||||
local player2 = _G.minetest.get_player_by_name(tpr[2] or "");
|
||||
if player1 and player2 then else return end
|
||||
player1:setpos(player2:getpos())
|
||||
_G.minetest.chat_send_player(tpr[2],minetest.colorize("yellow","#teleporting " .. tpr[1] .. " to you."))
|
||||
tpr = {}
|
||||
end
|
||||
|
||||
end,
|
||||
docs = "tpy; allow player who sent teleport request to teleport to you.",
|
||||
},
|
||||
|
||||
calc =
|
||||
{
|
||||
level = 0,
|
||||
run = function(param)
|
||||
|
||||
local formula = param[2] or "";
|
||||
if not string.find(formula,"%a") then
|
||||
result = 0;
|
||||
code.run("result = "..formula);
|
||||
result = tonumber(result)
|
||||
if result then say(result) else say("error in formula") end
|
||||
else
|
||||
say("dont use any letters in formula")
|
||||
end
|
||||
end,
|
||||
docs = "calculate expression",
|
||||
},
|
||||
|
||||
day =
|
||||
{
|
||||
level = 1,
|
||||
run = function() minetest.set_timeofday(0.25) end,
|
||||
docs = "set time to day"
|
||||
},
|
||||
|
||||
sayhi =
|
||||
{
|
||||
level = 0,
|
||||
run = function()
|
||||
local players = _G.minetest.get_connected_players();local msg = "";
|
||||
for _,player in pairs(players) do
|
||||
local name = player:get_player_name();
|
||||
local color = string.format("#%x",math.random(2^24)-1)
|
||||
if name~=speaker then msg = msg..colorize(color , " hi " .. name) .."," end
|
||||
end
|
||||
_G.minetest.chat_send_all("<"..speaker..">" .. string.sub(msg,1,-2))
|
||||
end,
|
||||
docs = "say hi to all the other players"
|
||||
},
|
||||
|
||||
msg = {
|
||||
level = 2,
|
||||
run = function(param)
|
||||
local text = string.sub(msg, string.len(param[1] or "")+string.len(param[2] or "") + 3)
|
||||
local form = "size [8,2] textarea[0.,0;8.75,3.75;book;MESSAGE from " .. speaker .. ";" .. _G.minetest.formspec_escape(text or "") .. "]"
|
||||
_G.minetest.show_formspec(param[2], "robot_msg", form);
|
||||
end,
|
||||
docs = "msg name message, displays message to target player",
|
||||
|
||||
},
|
||||
|
||||
plist = {
|
||||
level = 0,
|
||||
run = function()
|
||||
local p = {};
|
||||
for k,v in pairs(minetest.get_connected_players()) do
|
||||
local name = v:get_player_name()
|
||||
local pdata = _G.skyblock.players[name]
|
||||
p[#p+1] = name..", level " .. pdata.level .. "+" .. pdata.completed .. "/" .. pdata.total
|
||||
end
|
||||
local text = table.concat(p,"\n")
|
||||
local form = "size [8,5] textarea[0.,0;8.75,6.75;book;PLAYERS;" .. _G.minetest.formspec_escape(text or "") .. "]"
|
||||
_G.minetest.show_formspec(speaker, "robot_msg", form);
|
||||
end,
|
||||
docs = "plist, displays player list and their levels",
|
||||
|
||||
},
|
||||
|
||||
run = {
|
||||
level = 3,
|
||||
run = function(param)
|
||||
local expr = string.sub(msg,5);
|
||||
--say("running " .. expr)
|
||||
code.run(expr);
|
||||
end,
|
||||
docs = "run lua code",
|
||||
},
|
||||
}
|
||||
|
||||
self.label(colorize("red","\nCMD ROBOT"))
|
||||
end
|
||||
speaker, msg = self.listen_msg();
|
||||
|
||||
if msg then
|
||||
local words = {};
|
||||
for word in string.gmatch(msg,"%S+") do words[#words+1]=word end -- extract words
|
||||
|
||||
local level = users[speaker] or 0;
|
||||
local cmd = words[1];
|
||||
|
||||
cmdlevel = cmdlvl[cmd] or 0;
|
||||
if level < cmdlevel then
|
||||
say("You need to be level " .. cmdlevel .. " to use " .. words[1])
|
||||
else
|
||||
if cmds[cmd] then
|
||||
cmds[cmd].run(words)
|
||||
elseif words[1]=="heal" and words[2] then
|
||||
local player = _G.minetest.get_player_by_name(words[2]);
|
||||
if player then player:set_hp(20) end
|
||||
elseif words[1]=="rename" then
|
||||
local player = _G.minetest.get_player_by_name(words[2]);
|
||||
if player then if ((users[words[2]] or 0)<=level) and (level>=3 or words[2]~=speaker) then player:set_nametag_attributes({text = words[3] or words[2]}) end end
|
||||
elseif words[1]=="robot"then
|
||||
local player = _G.minetest.get_player_by_name(words[2])
|
||||
if player then
|
||||
player:set_properties({visual = "cube"});
|
||||
player:set_properties({textures={"arrow.png^[transformR90","basic_machine_side.png","basic_machine_side.png","basic_machine_side.png","face.png","basic_machine_side.png"}})
|
||||
player:set_properties({collisionbox={-0.5,-0.5,-0.5,0.5,0.5,0.5}})
|
||||
end
|
||||
elseif words[1] == "mese" then
|
||||
local player = _G.minetest.get_player_by_name(words[2])
|
||||
if player then
|
||||
player:set_properties({visual = "mesh",textures = {"zmobs_mese_monster.png"},mesh = "zmobs_mese_monster.x",visual_size = {x=1,y=1}})
|
||||
end
|
||||
|
||||
elseif words[1] == "givediamond" then
|
||||
local player = _G.minetest.get_player_by_name(words[2])
|
||||
local pos = player:getpos();
|
||||
_G.minetest.add_item(pos,_G.ItemStack("default:diamond"))
|
||||
elseif words[1] == "acalc" then
|
||||
local formula = words[2] or "";
|
||||
if not string.find(formula,"_G") then
|
||||
result = 0;
|
||||
code.run("result = "..formula);
|
||||
result = tonumber(result)
|
||||
if result then say(result) else say("error in formula") end
|
||||
end
|
||||
elseif words[1] == "shutdown" then
|
||||
_G.minetest.request_shutdown("maintenance, come back",true)
|
||||
elseif words[1] == "web" then
|
||||
local text = string.sub(msg,5);
|
||||
local f = _G.io.open("H:\\sfk\\files\\index.html", "w")
|
||||
f:write(text);f:close()
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,70 @@
|
|||
-- COPY PASTE ROBOT by rnd: c1 c2 r = markers, c = copy p = paste
|
||||
|
||||
if not paste then
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
paste = {};
|
||||
round = function(x)
|
||||
if x>0 then
|
||||
return math.floor(x+0.5)
|
||||
else
|
||||
return -math.floor(-x+0.5)
|
||||
end
|
||||
end
|
||||
data = {};
|
||||
self.listen(1)
|
||||
self.label("COPY-PASTE MASTER v1.2 gold edition. commands: c1 c2 r c p")
|
||||
end
|
||||
|
||||
|
||||
speaker, msg = self.listen_msg()
|
||||
|
||||
if speaker == "rnd" then
|
||||
local player = _G.minetest.get_player_by_name(speaker);
|
||||
local p = player:getpos(); p.x = round(p.x); p.y=round(p.y); p.z = round(p.z);
|
||||
if msg == "c1" then
|
||||
paste.src1 = {x=p.x,y=p.y,z=p.z};say("marker 1 set at " .. p.x .. " " .. p.y .. " " .. p.z)
|
||||
elseif msg == "c2" then
|
||||
paste.src2 = {x=p.x,y=p.y,z=p.z};say("marker 2 set at " .. p.x .. " " .. p.y .. " " .. p.z)
|
||||
elseif msg == "r" then
|
||||
paste.ref = {x=p.x,y=p.y,z=p.z};say("reference set at " .. p.x .. " " .. p.y .. " " .. p.z)
|
||||
elseif msg == "c" then -- copy
|
||||
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
|
||||
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
|
||||
local count = 0; data = {};
|
||||
for i = x1,x2 do
|
||||
for j = y1,y2 do
|
||||
for k = z1,z2 do
|
||||
local node = _G.minetest.get_node({x=i,y=j,z=k});
|
||||
if node.name ~= "air" then
|
||||
if not data[i] then data[i]= {} end
|
||||
if not data[i][j] then data[i][j]= {} end
|
||||
data[i][j][k] = {node, _G.minetest.get_meta({x=i,y=j,z=k}):to_table()}
|
||||
count = count +1;
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
say(count .. " nodes copied ");
|
||||
elseif msg == "p" then -- paste
|
||||
local x1 = math.min(paste.src1.x,paste.src2.x);local y1 = math.min(paste.src1.y,paste.src2.y);local z1 = math.min(paste.src1.z,paste.src2.z);
|
||||
local x2 = math.max(paste.src1.x,paste.src2.x);local y2 = math.max(paste.src1.y,paste.src2.y);local z2 = math.max(paste.src1.z,paste.src2.z);
|
||||
local count = 0; p.x = p.x-paste.ref.x; p.y = p.y-paste.ref.y; p.z = p.z-paste.ref.z;
|
||||
for i = x1,x2 do
|
||||
for j = y1,y2 do
|
||||
for k = z1,z2 do
|
||||
local pdata;
|
||||
if data[i] and data[i][j] and data[i][j][k] then
|
||||
pdata = data[i][j][k]
|
||||
end
|
||||
if pdata then
|
||||
count = count + 1
|
||||
_G.minetest.set_node({x=i+p.x,y=j+p.y,z=k+p.z}, pdata[1]);
|
||||
_G.minetest.get_meta({x=i+p.x,y=j+p.y,z=k+p.z}):from_table(pdata[2])
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
say(count .. " nodes pasted ");
|
||||
end
|
||||
end
|
|
@ -0,0 +1,15 @@
|
|||
if not init then
|
||||
init = true
|
||||
angle = 90
|
||||
walk = {["default:dirt"] = 1}
|
||||
stop = {["wool:white"] = 1}
|
||||
end
|
||||
|
||||
node = read_node.forward_down()
|
||||
if walk[node] then
|
||||
move.forward()
|
||||
elseif stop[node] then
|
||||
self.reset(); angle = 90
|
||||
else
|
||||
turn.angle(angle);move.forward(); turn.angle(angle); angle = - angle
|
||||
end
|
|
@ -0,0 +1,137 @@
|
|||
-- simple ctf robot, rnd
|
||||
--instructions: build game arena and place blue/red buttons as flags. edit flag positions below
|
||||
--you must register 'keyboard' events by placing robot with same 'id' as robot running this code at 'event register' positions - default (32*i,64*j+1, 32*k)
|
||||
|
||||
if not ctf then
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
ctf = {
|
||||
[1] = {state = 1, flagnode = "basic_robot:button8080FF", pos = {x=-18,y=501,z=17}, name = "blue", owner = "", score = 0}, -- team[1]
|
||||
[2] = {state = 1, flagnode = "basic_robot:buttonFF8080", pos = {x=-79,y=501,z=46}, name = "red", owner = "", score = 0}, -- team[2]
|
||||
}
|
||||
|
||||
teams = {} -- example : {["rnd"] = {1,0, player, health points at start}}; -- team, ownership of flag
|
||||
maxscore = 3;
|
||||
t = 0
|
||||
teamid = 1; -- team selector when joining
|
||||
|
||||
gamestate = 0;
|
||||
self.listen(1)
|
||||
self.spam(1)
|
||||
|
||||
get_id = function(pos)
|
||||
local range = 1000;
|
||||
return pos.x + range*pos.y+range^2*pos.z
|
||||
end
|
||||
|
||||
flag_id = {}; for i = 1,#ctf do flag_id[get_id(ctf[i].pos)] = i end
|
||||
|
||||
render_flags = function()
|
||||
for i = 1,#ctf do minetest.set_node(ctf[i].pos, {name = ctf[i].flagnode}) end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if gamestate == 0 then -- welcome
|
||||
say(colorize("red","#CAPTURE THE FLAG GAME. say '\\join' to join game. to start game one of joined players says '\\start'"))
|
||||
gamestate = 1
|
||||
elseif gamestate == 1 then
|
||||
speaker,msg = self.listen_msg()
|
||||
if msg == "join" then
|
||||
local pl = minetest.get_player_by_name(speaker);
|
||||
teams[speaker] = {teamid, 0, pl,20};pl:set_hp(20)
|
||||
local msg1 = ""; local msg2 = ""
|
||||
for k,v in pairs(teams) do
|
||||
if v[1] == 1 then msg1 = msg1 .. k .. " " elseif v[1] == 2 then msg2 = msg2 .. k .. " " end
|
||||
end
|
||||
|
||||
say(colorize("yellow","#CTF : " .. speaker .. " joined team " .. ctf[teamid].name .. ". TEAM " .. ctf[1].name .. ": " .. msg1 .. ", TEAM " .. ctf[2].name .. ": " .. msg2))
|
||||
teamid = 3-teamid; -- 1,2
|
||||
elseif msg == "start" then -- game start
|
||||
if teams[speaker] then
|
||||
gamestate = 2
|
||||
keyboard.get() -- clear keyboard buffer
|
||||
say(colorize("red","#CTF GAME STARTED. GET ENEMY FLAG AND BRING IT BACK TO YOUR FLAG. DONT LET YOUR HEALTH GO BELOW 5 HEARTS OR YOU ARE OUT."))
|
||||
for k,_ in pairs(teams) do -- teleport players
|
||||
local data = teams[k];data[3]:setpos( ctf[data[1]].pos )
|
||||
end
|
||||
render_flags()
|
||||
end
|
||||
end
|
||||
|
||||
elseif gamestate == 2 then
|
||||
-- check player health
|
||||
for k,v in pairs(teams) do
|
||||
local hp = teams[k][3]:get_hp();
|
||||
if not hp or hp<10 then -- teams[k][4]
|
||||
|
||||
local cflag = teams[k][2];
|
||||
if cflag>0 then -- drop flag
|
||||
ctf[cflag].state = 1
|
||||
ctf[cflag].owner = ""
|
||||
minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode})
|
||||
say(colorize("red", "#CTF " .. k .. " dropped " .. ctf[cflag].name .. " flag!"))
|
||||
end
|
||||
if not hp then -- player left
|
||||
say(colorize("yellow", "#CTF " .. k .. " left the game!"))
|
||||
teams[k] = nil
|
||||
else -- reset player
|
||||
say(colorize("yellow", "#CTF " .. k .. " resetted!"))
|
||||
v[2] = 0 -- player has no flag
|
||||
v[3]:set_hp(20)
|
||||
v[3]:setpos( ctf[v[1]].pos )
|
||||
end
|
||||
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
event = keyboard.get()
|
||||
if event and teams[event.puncher] then
|
||||
--say(serialize(event))
|
||||
local punch_id = get_id({x=event.x,y=event.y,z=event.z});
|
||||
local flag = flag_id[punch_id];
|
||||
if flag then
|
||||
local state = ctf[flag].state
|
||||
local puncher = event.puncher;
|
||||
if state == 1 then -- flag is here, ready to be taken or capture of enemy flag
|
||||
if teams[puncher][1] ~= flag then -- take
|
||||
say(colorize("red","#CTF " .. puncher .. " has taken " .. ctf[flag].name .. " flag !"))
|
||||
ctf[flag].state = 2;
|
||||
ctf[flag].owner = puncher;
|
||||
teams[puncher][2] = flag;
|
||||
minetest.set_node(ctf[flag].pos, {name = "basic_robot:buttonFFFF80"})
|
||||
else -- capture?
|
||||
if teams[puncher][2] > 0 then
|
||||
local cflag = teams[puncher][2] -- puncher has this flag
|
||||
local data = ctf[cflag];
|
||||
|
||||
local team = teams[puncher][1];
|
||||
ctf[team].score = ctf[team].score + 1
|
||||
ctf[team].owner = ""
|
||||
ctf[cflag].state = 1; -- reset captured flag state
|
||||
minetest.set_node(ctf[cflag].pos, {name = ctf[cflag].flagnode})
|
||||
teams[puncher][2] = 0
|
||||
say(colorize("orange","#CTF " .. puncher .. " has captured " .. data.name .. " flag! Team " .. ctf[team].name .. " has score " .. ctf[team].score ))
|
||||
if ctf[team].score == maxscore then
|
||||
say(colorize("yellow","#CTF: TEAM " .. ctf[team].name .. " WINS! "))
|
||||
gamestate = 3;t=5; -- intermission, duration 5
|
||||
|
||||
--reset
|
||||
teams = {}
|
||||
for i=1,#ctf do ctf[i].state = 1 ctf[i].score = 0 ctf[i].owner = "" end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
--say(serialize(event))
|
||||
end
|
||||
elseif gamestate == 3 then -- intermission
|
||||
if t>0 then t=t-1 else gamestate = 0 end
|
||||
end
|
|
@ -0,0 +1,91 @@
|
|||
if not s then
|
||||
-- init
|
||||
bots = {[4] = {}, [5] = {}}; -- [type] = {{1,1,10}, {3,2,10}}; -- {x,y,hp}
|
||||
arena = {}; --[x][z] = {type, idx}
|
||||
for i = -10,10 do arena[i] = {} for j=-10,10 do arena[i][j] = {0,0} end end
|
||||
centerpos = self.spawnpos(); centerpos.y = centerpos.y+2
|
||||
TYPE = 4; -- 4,5 defines which bots are on the move/attack
|
||||
DIR = 1
|
||||
s=0
|
||||
t=0
|
||||
-- load user progs
|
||||
_,script1 = book.read(1);_,script2 = book.read(2);
|
||||
prog1, _ = _G.loadstring( script1 ); prog2, _ = _G.loadstring( script2 );
|
||||
|
||||
spawn_bot = function (x,z,type)
|
||||
if arena[x] and arena[x][z] and arena[x][z][1] == 0 then
|
||||
keyboard.set({x=centerpos.x+x,y=centerpos.y,z=centerpos.z+z},type)
|
||||
table.insert(bots[type],{x,z,10})
|
||||
arena[x][z] = {type,#bots[type]}
|
||||
else
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
move_bot = function (i,dx,dz)
|
||||
local bot = bots[TYPE][i];if not bot then return false end
|
||||
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
|
||||
local x1=bot[1]+dx; local z1=bot[2]+dz;
|
||||
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
|
||||
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then else return false end
|
||||
|
||||
keyboard.set({x=centerpos.x+bot[1],y=centerpos.y,z=centerpos.z+bot[2]},0);
|
||||
keyboard.set({x=centerpos.x+x1,y=centerpos.y,z=centerpos.z+z1},TYPE);
|
||||
arena[bot[1]][bot[2]] = {0,0}
|
||||
arena[x1][z1] = {TYPE,i}
|
||||
|
||||
bot[1]=x1;bot[2]=z1;
|
||||
end
|
||||
|
||||
attack_bot = function(i,dx,dz)
|
||||
local bot = bots[TYPE][i];if not bot then return false end
|
||||
if math.abs(dx)>1 or math.abs(dz)>1 then return false end
|
||||
local x1=bot[1]+dx; local z1=bot[2]+dz;
|
||||
if math.abs(x1)>10 or math.abs(z1)>10 then return false end
|
||||
if arena[x1] and arena[x1][z1] and arena[x1][z1][1] == 0 then return false end
|
||||
local type = arena[x1][z1][1]; local idx = arena[x1][z1][2];
|
||||
local tbot = bots[type][idx];
|
||||
if not tbot then return false end
|
||||
tbot[3]=tbot[3]-5;
|
||||
if tbot[3]<=0 then
|
||||
keyboard.set({x=centerpos.x+tbot[1],y=centerpos.y,z=centerpos.z+tbot[2]},0);
|
||||
table.remove(bots[type],idx);
|
||||
arena[x1][z1] = {0,0}
|
||||
end
|
||||
end
|
||||
|
||||
read_arena = function(x,z)
|
||||
local data = arena[x][z];
|
||||
if not data then return end
|
||||
return {data[1],data[2]};
|
||||
end
|
||||
|
||||
read_bots = function (type, idx)
|
||||
local data = bots[type][idx];
|
||||
if not data then return end
|
||||
return {data[1],data[2],data[3]}
|
||||
end
|
||||
end
|
||||
|
||||
if t%10 == 0 then
|
||||
spawn_bot(0,-10,4)
|
||||
spawn_bot(0,10,5)
|
||||
end
|
||||
t=t+1
|
||||
self.label(#bots[4] .. " " .. #bots[5])
|
||||
|
||||
-- PROGRAM RULES:
|
||||
-- not allowed to modify api code: TYPE, bots,t,s, spawn_bot, move_bot, attack_bot, read_arena, read_bots
|
||||
-- only allowed to move bot or attack, but not to dig/place
|
||||
|
||||
TYPE = 4+(t%2);
|
||||
DIR = - DIR
|
||||
|
||||
if TYPE == 5 then
|
||||
_G.setfenv(prog1, _G.basic_robot.data[self.name()].sandbox )
|
||||
_,err = pcall(prog1)
|
||||
else
|
||||
_G.setfenv(prog2, _G.basic_robot.data[self.name()].sandbox )
|
||||
_,err = pcall(prog2)
|
||||
end
|
||||
if err then say(err) self.remove() end
|
|
@ -0,0 +1,150 @@
|
|||
if not data then
|
||||
m=10;n=10;
|
||||
players = {};
|
||||
paused = true
|
||||
|
||||
turn = 2;
|
||||
t = 0;
|
||||
SIGNUP = 0; GAME = 1; INTERMISSION = 2
|
||||
state = SIGNUP
|
||||
|
||||
t0 = _G.minetest.get_gametime();spawnpos = self.spawnpos() -- place mines
|
||||
data = {};
|
||||
|
||||
init_game = function()
|
||||
data = {}; minescount = 32
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
|
||||
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
|
||||
|
||||
minescount = 0;
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
end end
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},4) -- safe start spot
|
||||
end
|
||||
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
chk_mines = function()
|
||||
local count = minescount;
|
||||
for i=1,m do for j=1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
|
||||
count=count-1
|
||||
end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
|
||||
greeting = function()
|
||||
_G.minetest.chat_send_all(colorize("red","#BATTLE MINESWEEPER : two player battle in minesweeper. say join to play.\nRules: 1. each player has 5 second turn to make a move 2. if you dont make move you lose\n3. if you make move in other player turn you lose. 4. if you hit bomb or mark bomb falsely you lose"))
|
||||
end
|
||||
|
||||
player_lost = function ()
|
||||
for i=1,#players do
|
||||
local player = _G.minetest.get_player_by_name(players[i]);
|
||||
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+10,z=spawnpos.z}) end
|
||||
end
|
||||
state = INTERMISSION; t = 0
|
||||
end
|
||||
|
||||
function change_turn()
|
||||
if turn == 1 then
|
||||
_G.minetest.sound_play("default_break_glass",{pos=spawnpos, max_hear_distance = 100})
|
||||
else
|
||||
_G.minetest.sound_play("note_a",{pos=spawnpos, max_hear_distance = 100})
|
||||
end
|
||||
|
||||
if paused == false then
|
||||
say(players[turn] .. " lost : didn't make a move");
|
||||
player_lost()
|
||||
else
|
||||
if turn == 1 then turn = 2 else turn = 1 end
|
||||
self.label("turn " .. turn .. " " .. players[turn])
|
||||
t=0
|
||||
paused = false
|
||||
end
|
||||
end
|
||||
|
||||
init_game()
|
||||
greeting()
|
||||
self.listen(1)
|
||||
end
|
||||
|
||||
if state == SIGNUP then
|
||||
speaker,msg = self.listen_msg()
|
||||
if speaker then
|
||||
if msg == "join" then
|
||||
players[#players+1] = speaker;
|
||||
local plist = ""; for i=1,#players do plist = plist .. players[i] .. ", " end
|
||||
_G.minetest.chat_send_all("BATTLE MINESWEEPER, current players : " .. plist)
|
||||
|
||||
if #players >= 2 then
|
||||
state = GAME
|
||||
change_turn();
|
||||
keyboard.get(); t=0;
|
||||
for i = 1, #players do
|
||||
local player = _G.minetest.get_player_by_name(players[i]);
|
||||
if player then player:setpos({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z}) end
|
||||
end
|
||||
_G.minetest.chat_send_all(colorize("red","BATTLE MINESWEEPER " .. m .. "x" ..n .. " with " .. minescount .. " mines.\n" .. players[turn] .. " its your move!"))
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
elseif state == GAME then
|
||||
|
||||
t = t + 1;
|
||||
if t>5 then -- change of turn
|
||||
change_turn()
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event and event.type == 2 and not paused then
|
||||
if event.puncher == players[turn] then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
else
|
||||
local ppos = player.getpos(event.puncher)
|
||||
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
|
||||
if data[x] and data[x][z] == 1 then
|
||||
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},2)
|
||||
else
|
||||
keyboard.set({x=event.x,y=event.y,z=event.z},3)
|
||||
end
|
||||
else
|
||||
say(event.puncher .. " lost : marked a bomb where it was none! ");
|
||||
player_lost()
|
||||
end
|
||||
else
|
||||
if data[x] and data[x][z]==1 then
|
||||
_G.minetest.sound_play("tnt_boom",{pos=spawnpos, max_hear_distance = 100})
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3)
|
||||
say(event.puncher .. " lost : punched a bomb! ");
|
||||
player_lost()
|
||||
else
|
||||
local count = get_mine_count(x,z);
|
||||
if count == 0 then keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4)
|
||||
else keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},7+count) end
|
||||
end
|
||||
end
|
||||
end
|
||||
paused = true
|
||||
else
|
||||
say(event.puncher .. " lost : played out of his/her turn"); player_lost()
|
||||
end
|
||||
end
|
||||
|
||||
elseif state == INTERMISSION then
|
||||
t=t+1; if t> 15 then state = SIGNUP;players = {}; paused = true; init_game(); greeting() end
|
||||
end
|
|
@ -0,0 +1,208 @@
|
|||
--black box by rnd, 03/18/2017
|
||||
--https://en.wikipedia.org/wiki/Black_Box_(game)
|
||||
|
||||
if not data then
|
||||
m=16;n=16;
|
||||
atoms = 32
|
||||
attempts = 1;turn = 0;
|
||||
spawnpos = self.spawnpos(); spawnpos.x = spawnpos.x-m/2; spawnpos.y = spawnpos.y+2; spawnpos.z = spawnpos.z-n/2
|
||||
|
||||
local players = find_player(5,spawnpos);
|
||||
if not player then self.remove() else pname = players[1] end
|
||||
|
||||
self.spam(1);t0 = _G.minetest.get_gametime();
|
||||
data = {};
|
||||
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
|
||||
|
||||
for i=1,atoms do -- put in atoms randomly
|
||||
data[math.random(m)][math.random(n)] = 1
|
||||
end
|
||||
|
||||
atoms = 0
|
||||
for i = 1,m do for j = 1,n do if data[i][j]==1 then atoms = atoms + 1 end end end
|
||||
|
||||
render_board = function(mode) -- mode 0 : render without solution, 1: render solution
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if mode == 0 or data[i][j] == 0 then
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
else
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},3)
|
||||
end
|
||||
end end
|
||||
end
|
||||
|
||||
get_dirl = function(dir)
|
||||
local dirl; -- direction left
|
||||
if dir[1] > 0.5 then dirl = {0,-1}
|
||||
elseif dir[1] < -0.5 then dirl = {0,1}
|
||||
elseif dir[2] > 0.5 then dirl = {-1,0}
|
||||
elseif dir[2] < -0.5 then dirl = {1,0}
|
||||
end
|
||||
return dirl
|
||||
end
|
||||
|
||||
read_pos = function(x,z)
|
||||
if x<1 or x>m or z<1 or z>n then return nil end
|
||||
return data[x][z]
|
||||
end
|
||||
|
||||
newdir = function(x,z,dir) -- where will ray go next
|
||||
local retdir = {dir[1],dir[2]};
|
||||
local xf = x+dir[1]; local zf = z+dir[2] -- forward
|
||||
local dirl = get_dirl(dir)
|
||||
|
||||
local nodef = read_pos(xf,zf)
|
||||
local nodel = read_pos(xf + dirl[1],zf + dirl[2])
|
||||
local noder = read_pos(xf - dirl[1],zf - dirl[2])
|
||||
if nodef == 1 then
|
||||
retdir = {0,0} -- ray hit something
|
||||
elseif nodel == 1 and noder ~= 1 then
|
||||
retdir = {-dirl[1],-dirl[2]}
|
||||
elseif nodel ~= 1 and noder == 1 then
|
||||
retdir = {dirl[1],dirl[2]}
|
||||
elseif nodel == 1 and noder == 1 then
|
||||
retdir = {-dir[1],-dir[2]}
|
||||
end
|
||||
return retdir
|
||||
end
|
||||
|
||||
shootray = function(x,z,dir)
|
||||
--say("ray starts " .. x .. " " .. z .. " dir " .. dir[1] .. " " .. dir[2])
|
||||
local xp = x; local zp = z;
|
||||
local dirp = {dir[1],dir[2]};
|
||||
local maxstep = m*n;
|
||||
|
||||
for i = 1,maxstep do
|
||||
dirp = newdir(xp,zp,dirp);
|
||||
if dirp[1]==0 and dirp[2]==0 then return -i end -- hit
|
||||
xp=xp+dirp[1];zp=zp+dirp[2];
|
||||
if xp<1 or xp>m or zp<1 or zp>n then return i,{xp,zp} end -- out
|
||||
end
|
||||
return 0 -- hit
|
||||
end
|
||||
|
||||
count = 0; -- how many letters were used up
|
||||
border_start_ray = function(x,z)
|
||||
local rdir
|
||||
if x==0 then rdir = {1,0}
|
||||
elseif x == m+1 then rdir = {-1,0}
|
||||
elseif z == 0 then rdir = {0,1}
|
||||
elseif z == n+1 then rdir = {0,-1}
|
||||
end
|
||||
if rdir then
|
||||
local result,out = shootray(x,z,rdir);
|
||||
if result >= 0 then
|
||||
|
||||
if out then
|
||||
if out[1]==x and out[2]==z then -- got back where it originated, reflection
|
||||
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},1);
|
||||
else
|
||||
if result<=1 then
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},6); -- immediate bounce off
|
||||
else
|
||||
local nodename = "basic_robot:button_"..(65+count);
|
||||
_G.minetest.set_node(
|
||||
{x=spawnpos.x+out[1],y=spawnpos.y+1,z=spawnpos.z+out[2]},
|
||||
{name = nodename, param2 = 1})
|
||||
_G.minetest.set_node(
|
||||
{x=spawnpos.x+x,y=spawnpos.y+1,z=spawnpos.z+z},
|
||||
{name = nodename, param2 = 1})
|
||||
count = count + 1;
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4);
|
||||
keyboard.set({x=spawnpos.x+out[1],y=spawnpos.y,z=spawnpos.z+out[2]},4);
|
||||
end
|
||||
end
|
||||
end
|
||||
elseif result<0 then
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},3); -- hit
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- initial border loop and marking
|
||||
|
||||
--render blue border
|
||||
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+0},5) keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+n+1},5) end
|
||||
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y,z=spawnpos.z+j},5) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y,z=spawnpos.z+j},5) end
|
||||
|
||||
for i = 1,m do keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+0},0) keyboard.set({x=spawnpos.x+i,y=spawnpos.y+1,z=spawnpos.z+n+1},0) end
|
||||
for j = 1,n do keyboard.set({x=spawnpos.x+0,y=spawnpos.y+1,z=spawnpos.z+j},0) keyboard.set({x=spawnpos.x+m+1,y=spawnpos.y+1,z=spawnpos.z+j},0) end
|
||||
|
||||
|
||||
z=0 -- bottom
|
||||
for x = 1,m do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
x=m+1 -- right
|
||||
for z = 1,n do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
z=n+1 -- top
|
||||
for x = m,1,-1 do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
x=0 -- left
|
||||
for z = n,1,-1 do
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button8080FF" then
|
||||
border_start_ray(x,z)
|
||||
end
|
||||
end
|
||||
|
||||
check_solution = function()
|
||||
for i = 1,m do
|
||||
for j = 1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j}) == "basic_robot:buttonFF8080" then -- red
|
||||
if data[i][j]~=1 then return false end
|
||||
else
|
||||
if data[i][j]~=0 then return false end
|
||||
end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
--render board
|
||||
render_board(0)
|
||||
keyboard.set({x=spawnpos.x,y=spawnpos.y,z=spawnpos.z-1},4)
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z-1},5)
|
||||
self.label("BLACKBOX with " .. atoms .. " atoms")
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
if event.type == 4 then
|
||||
if check_solution() then
|
||||
say("#BLACKBOX : CONGRATULATIONS! " .. event.puncher .. " found all atoms after " .. attempts .. " tries."); self.remove()
|
||||
else
|
||||
say("#BLACKBOX : " .. event.puncher .. " failed to detect atoms after " .. attempts .. " attempts.")
|
||||
attempts = attempts+1
|
||||
end
|
||||
elseif event.type == 5 then
|
||||
say("#BLACKBOX : DISPLAYING SOLUTION",pname)
|
||||
render_board(1)
|
||||
self.remove()
|
||||
end
|
||||
else -- interior punch
|
||||
nodetype = 2;
|
||||
if keyboard.read({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z}) == "basic_robot:button808080" then
|
||||
nodetype = 3
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},nodetype);
|
||||
end
|
||||
|
||||
end
|
||||
::END::
|
|
@ -0,0 +1,38 @@
|
|||
-- rnd 2017
|
||||
if not s then
|
||||
s=0
|
||||
player0 ="";
|
||||
reward = "default:gold_ingot 6"
|
||||
price = "default:gold_ingot";
|
||||
self.spam(1)
|
||||
end
|
||||
if s==0 then
|
||||
local player = find_player(5);
|
||||
if player then
|
||||
player=player[1]
|
||||
if player~=player0 then
|
||||
self.label("Hello " .. player .. ". Please insert one gold ingot in chest to play.\nYou need to roll 6 on dice to win 6 gold.")
|
||||
player0 = player
|
||||
end
|
||||
else
|
||||
self.label(colorize("red","Come and win 6 gold!"))
|
||||
end
|
||||
if check_inventory.forward(price) then
|
||||
take.forward("default:gold_ingot");
|
||||
self.label("Thank you for your gold. rolling the dice!")
|
||||
s=1
|
||||
end
|
||||
elseif s==1 then
|
||||
roll = math.random(6);
|
||||
if roll == 6 then
|
||||
self.label("#YOU WIN!")
|
||||
say("#WE HAVE A WINNER! get 6 gold in chest!")
|
||||
insert.forward(reward)
|
||||
s=2
|
||||
else
|
||||
self.label(":( you rolled " .. roll.. ". Put gold in to try again.")
|
||||
s=0
|
||||
end
|
||||
elseif s==2 then
|
||||
if not check_inventory.forward(reward) then s=0 self.label("Please insert one gold to continue playing") end
|
||||
end
|
|
@ -0,0 +1,60 @@
|
|||
-- CONNECT, coded in 20 minutes by rnd
|
||||
if not data then
|
||||
m=8;n=8;turn = 0; num = 4;
|
||||
self.spam(1);t0 = _G.minetest.get_gametime();
|
||||
spawnpos = self.spawnpos() -- place mines
|
||||
state = 0; -- 0 signup 1 game
|
||||
players = {};
|
||||
data = {};
|
||||
for i = 1,m do data[i]={}; for j = 1,n do data[i][j]=0 end end
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},2)
|
||||
end
|
||||
end end
|
||||
|
||||
get_count_in_dir = function(dir,x,y)
|
||||
local r = num; -- num=4? in a row
|
||||
local snode = data[x][y];local count = 1;
|
||||
for j = 1,2 do
|
||||
for i = 1,r-1 do
|
||||
local x1 = x + dir[1]*i;local y1 = y + dir[2]*i;
|
||||
if not data[x1] or not data[x1][y1] then break end; if data[x1][y1]~= snode then break end
|
||||
count = count +1
|
||||
end
|
||||
dir[1]=-dir[1];dir[2]=-dir[2];
|
||||
end
|
||||
return count
|
||||
end
|
||||
|
||||
get_count = function(x,y)
|
||||
local c1 = get_count_in_dir({0,1},x,y); local c2 = get_count_in_dir({1,0},x,y)
|
||||
local c3 = get_count_in_dir({1,1},x,y); local c4 = get_count_in_dir({1,-1},x,y)
|
||||
if c2>c1 then c1 = c2 end; if c3>c1 then c1 = c3 end; if c4>c1 then c1 = c4 end
|
||||
return c1
|
||||
end
|
||||
|
||||
self.label("CONNECT 4 : GREEN starts play. 2 players punch to join game.")
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
elseif event.type == 2 then --if event.type == 2 then
|
||||
if state == 0 then
|
||||
if #players<2 then players[#players+1] = event.puncher
|
||||
else state = 1 end
|
||||
if #players==2 then state = 1 end
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},4+turn);
|
||||
data[x][z] = 4+turn;
|
||||
if get_count(x,z) == num then say("CONGRATULATIONS! " .. event.puncher .. " has "..num .. " in a row"); self.remove(); goto END end
|
||||
turn = 1-turn
|
||||
if state == 1 then
|
||||
local msg = ""; if turn == 0 then msg = "GREEN " else msg = "BLUE" end
|
||||
self.label(msg .. " : " .. players[turn+1])
|
||||
end
|
||||
end
|
||||
end
|
||||
::END::
|
|
@ -0,0 +1,97 @@
|
|||
-- 'hacking' game from Fallout, by rnd
|
||||
if not init then
|
||||
init = true
|
||||
|
||||
generate_random_string = function(n,m)
|
||||
local ret = {};
|
||||
for i = 1,n do ret[i]=string.char(math.random(m)+96) end --24
|
||||
return table.concat(ret)
|
||||
end
|
||||
|
||||
get_similiarity = function(text1,text2)
|
||||
local n = string.len(text1);
|
||||
if string.len(text2)~=n then return 0 end
|
||||
local ret = 0;
|
||||
for i = 1,n do
|
||||
if string.sub(text1,i,i) == string.sub(text2,i,i) then ret = ret + 1 end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
|
||||
local n = #passlist;
|
||||
|
||||
local frm = "label[0,0;" .. intro_msg .. "] " .. "label[0,8.5;" .. msg .. "] "
|
||||
|
||||
for i = 1,10 do
|
||||
frm = frm .. "button[0,".. (i)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
|
||||
end
|
||||
|
||||
for i = 11,n do
|
||||
frm = frm .. "button[2,".. (i-11+1)*0.75 ..";2,1;" .. i .. ";".. passlist[i] .. "] "
|
||||
end
|
||||
|
||||
local form = "size[4," .. 9 .. "]" .. frm;
|
||||
return form
|
||||
end
|
||||
|
||||
_G.math.randomseed(os.time())
|
||||
intro_msg = minetest.colorize("lawngreen","FALLOUT PASSWORD HACKING GAME\nmatch is both position and character.")
|
||||
msg = "" --TEST\nTEST\nTEST";
|
||||
passlist = {}; passdict = {}
|
||||
|
||||
n = 20; -- how many options
|
||||
count = 0;
|
||||
while count< n do
|
||||
local pass = generate_random_string(4,5); -- password length, charset size
|
||||
if not passdict[pass] then passlist[#passlist+1] = pass; passdict[pass] = true; count = count + 1 end
|
||||
end
|
||||
correct = math.random(n)
|
||||
guesses = 0
|
||||
max_guesses = 4
|
||||
|
||||
rom.data = {};
|
||||
if not rom.data then rom.data = {} end
|
||||
self.spam(1)
|
||||
|
||||
local players = find_player(4);
|
||||
if not players then say("#fallout hacking game: no players") self.remove() end
|
||||
pname = players[1];
|
||||
minetest.chat_send_player(pname,"#fallout hacking game, player " .. pname)
|
||||
|
||||
--if rom.data[pname] then say("password is locked out!") self.remove() end
|
||||
|
||||
self.show_form(pname,get_form())
|
||||
self.read_form()
|
||||
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender and sender == pname then -- form event
|
||||
local pl = _G.minetest.get_player_by_name(pname);
|
||||
if pl then
|
||||
local selected = 0
|
||||
for k,_ in pairs(fields) do if k~="quit" then selected = tonumber(k) break end end
|
||||
|
||||
if selected>0 then
|
||||
guesses = guesses + 1
|
||||
if selected == correct then
|
||||
minetest.chat_send_player(pname,"password " .. passlist[correct] .. " is correct! " .. guesses .. " guesses.")
|
||||
self.show_form(pname, "size[2,1] label[0,0.5;" .. minetest.colorize("lawngreen", "ACCESS GRANTED") .. "]")
|
||||
self.remove()
|
||||
--correct: do something with player
|
||||
else
|
||||
if guesses == 3 then msg = msg .. "\n" end
|
||||
msg = msg .. " " .. minetest.colorize("yellow",guesses .. ". " .. passlist[selected]) .. " (" .. get_similiarity(passlist[correct], passlist[selected]) .. " match)"
|
||||
self.show_form(pname, get_form())
|
||||
end
|
||||
if guesses>=max_guesses then
|
||||
msg = minetest.colorize("red","A C C E S S D E N I E D!")
|
||||
self.show_form(pname, get_form())
|
||||
minetest.chat_send_player(pname,"too many false guesses. password locked out!") rom.data[pname] = 1; self.remove()
|
||||
end
|
||||
end
|
||||
if fields.quit then self.remove() end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,106 @@
|
|||
--HIDE AND SEEK game robot, by rnd
|
||||
if not gamemaster then
|
||||
timeout = 10;
|
||||
gamemaster = "rnd"
|
||||
player_list = {};
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK .. say #hide to join play")
|
||||
s=0;t=0; count = 0;
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
self.listen(1); self.label(colorize("yellow","HIDE&SEEK"))
|
||||
end
|
||||
|
||||
speaker,msg = self.listen_msg();
|
||||
|
||||
if s==0 then
|
||||
if msg =="#hide" then
|
||||
player_list[speaker]={};
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. speaker .. " joined the game")
|
||||
local player = _G.minetest.get_player_by_name(speaker);
|
||||
if player then
|
||||
player:setpos({x=0,y=5,z=0});player:set_properties({nametag_color = "0x0"})
|
||||
end
|
||||
|
||||
end
|
||||
if msg == "#start" and speaker == gamemaster then s = 0.5 _G.minetest.chat_send_all("# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!") end
|
||||
|
||||
elseif s==0.5 then
|
||||
t=t+1
|
||||
if t==timeout then
|
||||
t=0;s = 1; count = 0;
|
||||
for pname,_ in pairs(player_list) do
|
||||
local player = _G.minetest.get_player_by_name(pname);
|
||||
if player then
|
||||
player_list[pname].hp = player:get_hp();
|
||||
player_list[pname].pos = player:getpos()
|
||||
player_list[pname].t = 0;
|
||||
count = count+1
|
||||
end
|
||||
end
|
||||
if count == 1 then
|
||||
gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.")
|
||||
else
|
||||
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS. You are out if: 1.your health changes, 2. leave spawn. If stay in same area for too long or you will be exposed."))
|
||||
end
|
||||
|
||||
end
|
||||
elseif s==1 then
|
||||
players = _G.minetest.get_connected_players();
|
||||
count = 0;
|
||||
for _,player in pairs(players) do
|
||||
local name = player:get_player_name();
|
||||
local data = player_list[name];
|
||||
if data then
|
||||
count=count+1
|
||||
local pos = player:getpos();
|
||||
local dist = math.max(math.abs(pos.x),math.abs(pos.y),math.abs(pos.z));
|
||||
if dist>50 or (not _G.minetest.get_player_by_name(name)) then
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " )
|
||||
player:set_properties({nametag_color = "white"})
|
||||
player_list[name] = nil;
|
||||
end
|
||||
if data.hp ~= player:get_hp() then
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" )
|
||||
player:set_properties({nametag_color = "white"})
|
||||
player_list[name] = nil;
|
||||
end
|
||||
|
||||
--expose campers
|
||||
local p = data.pos;
|
||||
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z));
|
||||
--say( name .. " dist " .. dist .. " t " .. data.t)
|
||||
if dist<8 then
|
||||
data.t = data.t+1;
|
||||
if not data.camp then
|
||||
if data.t>15 and not data.camp then
|
||||
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed")
|
||||
data.camp = true
|
||||
end
|
||||
elseif data.t>=20 then
|
||||
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z);
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z)
|
||||
data.camp = false; data.t = 0
|
||||
end
|
||||
else
|
||||
data.t = 0; data.pos = player:getpos(); data.camp = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
self.label(count)
|
||||
|
||||
if count<=1 then
|
||||
if count==1 then
|
||||
for name,_ in pairs(player_list) do
|
||||
player0=_G.minetest.get_player_by_name(name)
|
||||
_G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******"))
|
||||
player0:set_properties({nametag_color = "white"})
|
||||
gamemaster = false;
|
||||
end
|
||||
else
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: no players left")
|
||||
gamemaster = false;
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,151 @@
|
|||
--HIDE AND SEEK game robot
|
||||
if not gamemaster then
|
||||
timeout = 30;
|
||||
gamemaster = "rnd"
|
||||
gamepos = {x=0,y=5,z=0}
|
||||
player_list = {};
|
||||
s=0;t=0; count = 0;
|
||||
prize = ""
|
||||
|
||||
get_players = function()
|
||||
local msg = "";
|
||||
for name,_ in pairs(player_list) do
|
||||
msg = msg .. " " .. name
|
||||
end
|
||||
return msg
|
||||
end
|
||||
|
||||
init_game = function()
|
||||
|
||||
local msg = get_players();
|
||||
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK : hide from everyone else who is playing. Winner gets DIAMONDS\nsay join to join play. say #start to start game."..
|
||||
" players: " .. msg))
|
||||
s=0;t=0;
|
||||
end
|
||||
|
||||
init_game()
|
||||
_G.minetest.forceload_block(self.pos(),true)
|
||||
self.listen(1); self.label(colorize("yellow","HIDE&SEEK"))
|
||||
end
|
||||
|
||||
speaker,msg = self.listen_msg();
|
||||
|
||||
if s==0 then
|
||||
|
||||
t = t +1
|
||||
if t%30 == 0 then
|
||||
init_game();
|
||||
end
|
||||
|
||||
if msg =="join" then
|
||||
player_list[speaker]={};
|
||||
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK: " .. speaker .. " joined the game"));
|
||||
_G.minetest.chat_send_all("players: " .. get_players())
|
||||
|
||||
local player = _G.minetest.get_player_by_name(speaker);
|
||||
count = count + 1
|
||||
if player then
|
||||
player:setpos(gamepos);player:set_properties({nametag_color = "0x0"})
|
||||
player:set_hp(20)
|
||||
local inv = player:get_inventory();inv:set_list("main",{})
|
||||
end
|
||||
|
||||
end
|
||||
if msg == "#start" and count>1 then s = 0.5 _G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS in " .. timeout .. " SECONDS!")) end
|
||||
|
||||
elseif s==0.5 then
|
||||
t=t+1
|
||||
if t==timeout then
|
||||
t=0;s = 1; count = 0;
|
||||
for pname,_ in pairs(player_list) do
|
||||
local player = _G.minetest.get_player_by_name(pname);
|
||||
if player then
|
||||
player_list[pname].hp = player:get_hp();
|
||||
player_list[pname].pos = player:getpos()
|
||||
player_list[pname].t = 0;
|
||||
count = count+1
|
||||
end
|
||||
end
|
||||
if count == 1 then
|
||||
gamemaster = false; _G.minetest.chat_send_all("# HIDE AND SEEK only 1 player, aborting.")
|
||||
else
|
||||
prize = "default:diamond " .. (count-1);
|
||||
_G.minetest.chat_send_all(colorize("red","# HIDE AND SEEK STARTS NOW WITH " .. count .. " PLAYERS."..
|
||||
"You are out if: 1.your health changes, 2. leave game area. If stay in same area for too long or you will be exposed."))
|
||||
_G.minetest.chat_send_all(colorize("red","# WINNER WILL GET " .. prize))
|
||||
|
||||
end
|
||||
|
||||
end
|
||||
elseif s==1 then
|
||||
players = _G.minetest.get_connected_players();
|
||||
count = 0;
|
||||
for _,player in pairs(players) do
|
||||
local name = player:get_player_name();
|
||||
local data = player_list[name];
|
||||
if data then
|
||||
count=count+1
|
||||
local pos = player:getpos();
|
||||
local dist = math.max(math.abs(pos.x-gamepos.x),math.abs(pos.y-gamepos.y),math.abs(pos.z-gamepos.z));
|
||||
if dist>100 or (not _G.minetest.get_player_by_name(name)) then
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! went too far away " )
|
||||
player:set_properties({nametag_color = "white"})
|
||||
player_list[name] = nil;
|
||||
_G.minetest.chat_send_all("remaining players: " .. get_players())
|
||||
end
|
||||
if data.hp ~= player:get_hp() then
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: ".. name .. " is OUT! his health changed!" )
|
||||
player:set_properties({nametag_color = "white"})
|
||||
player_list[name] = nil;
|
||||
_G.minetest.chat_send_all("remaining players: " .. get_players())
|
||||
player:setpos({x=0,y=5,z=0})
|
||||
end
|
||||
|
||||
--expose campers
|
||||
local p = data.pos;
|
||||
dist = math.max(math.abs(pos.x-p.x),math.abs(pos.y-p.y),math.abs(pos.z-p.z));
|
||||
--say( name .. " dist " .. dist .. " t " .. data.t)
|
||||
if dist<8 then
|
||||
data.t = data.t+1;
|
||||
if not data.camp then
|
||||
if data.t>25 and not data.camp then
|
||||
_G.minetest.chat_send_player(name, "# HIDE AND SEEK: move in 5s or be exposed")
|
||||
data.camp = true
|
||||
end
|
||||
elseif data.t>=30 then
|
||||
pos.x=math.ceil(pos.x);pos.y=math.ceil(pos.y);pos.z=math.ceil(pos.z);
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: " .. name .. " is camping at " .. pos.x .. " " .. pos.z)
|
||||
data.camp = false; data.t = 0
|
||||
end
|
||||
else
|
||||
data.t = 0; data.pos = player:getpos(); data.camp = false
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
self.label(count)
|
||||
|
||||
if count<=1 then
|
||||
if count==1 then
|
||||
for name,_ in pairs(player_list) do
|
||||
local player0=_G.minetest.get_player_by_name(name)
|
||||
if player0 then
|
||||
_G.minetest.chat_send_all(colorize("red","****** HIDE AND SEEK: ".. name .. " wins ******"))
|
||||
local inv = player0:get_inventory();
|
||||
inv:add_item("main",_G.ItemStack(prize))
|
||||
player0:set_properties({nametag_color = "white"})
|
||||
player0:setpos({x=0,y=5,z=0})
|
||||
end
|
||||
s=2
|
||||
end
|
||||
else
|
||||
_G.minetest.chat_send_all("# HIDE AND SEEK: no players left")
|
||||
s=2
|
||||
end
|
||||
end
|
||||
|
||||
elseif s==2 then
|
||||
player_list = {}
|
||||
init_game()
|
||||
end
|
|
@ -0,0 +1,138 @@
|
|||
-- maze generation by rnd
|
||||
|
||||
-- http://en.wikipedia.org/wiki/Maze_generation_algorithm#Depth-first_search, recursive backtracker
|
||||
-- representation of node coordinate (row,coloumn)=(i,j) -> (i-1)*n+j, i=1..n, j=1...m
|
||||
-- representation of walls: below node k --> k, left of node k --> k+m.n
|
||||
|
||||
-- good overview of maze generation algorithms using javascript/html5
|
||||
-- http://www.jamisbuck.org/presentations/rubyconf2011/index.html#recursive-backtracker
|
||||
|
||||
-- helper functions
|
||||
--stack in lua
|
||||
local stack={};
|
||||
function stack.push(s,e) s[#s+1]=e end
|
||||
function stack.pop(s) local r = s[#s];s[#s]=nil;return r end
|
||||
--function table2string(s) local r = ""; for i,v in pairs(s) do r = r.. " ["..i.."]=".. v ; end return r end
|
||||
|
||||
function maze_deep_first_search(m,n,start,seed) -- returns a table of strings representing line renders
|
||||
|
||||
local steps,maxsteps; steps= 0; maxsteps = 999999;
|
||||
local maze = {}
|
||||
maze.m = m; maze.n = n;
|
||||
maze.unvisited = {};maze.stack = {}; maze.walls = {};
|
||||
maze.free = maze.m*maze.n;
|
||||
local i,j,k
|
||||
local nb,wall -- unvisited neighbbors, walls
|
||||
|
||||
--init structures
|
||||
for i=1,maze.m do
|
||||
for j =1,maze.n do
|
||||
k=(i-1)*maze.n+j;maze.unvisited[k]=true -- initially all cells unvisited
|
||||
maze.walls[k]=true;maze.walls[k+maze.n*maze.m]=true; -- walls are there
|
||||
end
|
||||
end
|
||||
|
||||
_G.math.randomseed(seed)
|
||||
maze.current = start
|
||||
maze.unvisited [ maze.current ] = false;
|
||||
maze.free = maze.free-1; maze.stack[1+#maze.stack] = maze.current
|
||||
|
||||
while maze.free>0 and steps<maxsteps do -- main loop
|
||||
steps=steps+1
|
||||
-- check current node neighbors
|
||||
k=maze.current
|
||||
j = k % maze.n;i=math.ceil(k/maze.n); -- get coords back from index
|
||||
if j==0 then j = maze.n end
|
||||
--print("coords current node "..k .. " = " .. i .. " " ..j)
|
||||
|
||||
nb={};wall={}-- check unvisited neighbors & wall removals
|
||||
|
||||
if i>1 then -- down
|
||||
k=(i-2)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k+maze.n;nb[#nb+1]=k end
|
||||
end
|
||||
if i<maze.m then -- up
|
||||
k=(i)*maze.n+j; if maze.unvisited[k] then wall[#wall+1]=k;nb[#nb+1]=k end
|
||||
end
|
||||
if j<maze.n then --right
|
||||
k=(i-1)*maze.n+j+1; if maze.unvisited[k] then wall[#wall+1]=k+maze.n*maze.m; nb[#nb+1]=k end
|
||||
end
|
||||
if j>1 then --left
|
||||
k=(i-1)*maze.n+j-1; if maze.unvisited[k] then wall[#wall+1]=k+1+maze.n*maze.m;nb[#nb+1]=k end
|
||||
end
|
||||
|
||||
--print(" unvisited neighbors " .. table2string(nb))
|
||||
if (#nb)>0 then -- if unvisited neighbors, choose random one as next current node
|
||||
stack.push(maze.stack,maze.current) -- remember previous current node
|
||||
k=math.random(#nb); -- pick random unvisited neighbor
|
||||
maze.walls[wall[k]]=false; -- remove wall
|
||||
--print(" removed wall ".. wall[k])
|
||||
k=nb[k];
|
||||
maze.current = k; -- new current cell
|
||||
maze.unvisited[k]=false; maze.free = maze.free-1 -- one less unvisited
|
||||
--print("new explore " .. k);
|
||||
|
||||
elseif (#maze.stack)~=0 then -- no unvisited neighbors, backtrack using stack
|
||||
|
||||
maze.current = stack.pop(maze.stack)
|
||||
--print("backtrack to "..maze.current)
|
||||
|
||||
else -- even stack is empty, just pick random unvisited cell
|
||||
k = math.random(maze.free); j=1;
|
||||
for i =1,maze.m*maze.n do
|
||||
if maze.unvisited[i] then
|
||||
if j==k then k=i; break end -- pick node
|
||||
j=j+1
|
||||
end
|
||||
end
|
||||
--print(" stack empty, random pick " ..k)
|
||||
maze.current=k;maze.unvisited[k]=false; maze.free = maze.free -1;
|
||||
end
|
||||
end -- of do
|
||||
|
||||
-- render maze with chars, row by row
|
||||
maze.ret = {};
|
||||
local hor;local vert;
|
||||
local wall = "1"
|
||||
|
||||
for i=1,maze.m do
|
||||
hor="";vert="";
|
||||
k= (i-1)*maze.n;
|
||||
-- horizontal
|
||||
for j = 1, maze.n do
|
||||
k=k+1;
|
||||
-- if maze.walls[k+maze.n*maze.m] then vert=vert.."X." else vert=vert.. "0." end
|
||||
-- if maze.walls[k] then hor=hor.."XX" else hor=hor.."X0" end
|
||||
if maze.walls[k+maze.n*maze.m] then vert=vert..wall.."0" else vert=vert.. "00" end
|
||||
if maze.walls[k] then hor=hor..wall..wall else hor=hor..wall.."0" end
|
||||
end
|
||||
maze.ret[1+#maze.ret]=hor..wall;maze.ret[1+#maze.ret]=vert..wall;
|
||||
end
|
||||
maze.ret[1+#maze.ret] = string.rep(wall,2*maze.n+1)
|
||||
return maze.ret
|
||||
end
|
||||
|
||||
-- RUN PROGRAM
|
||||
local maze=maze_deep_first_search(10,30,1,2015)
|
||||
--for _,v in pairs(maze) do print(v) end
|
||||
|
||||
|
||||
|
||||
make_maze = function(m,n,start,seed)
|
||||
local pos = self.spawnpos();pos.y=pos.y+1
|
||||
local p
|
||||
local maze=maze_deep_first_search(m,n,start,seed) -- m,n,start,seed
|
||||
local i,j,k;local p = {x=pos.x,y=pos.y,z=pos.z};
|
||||
for i,v in pairs(maze) do
|
||||
p.x = pos.x+i
|
||||
for k = 1,string.len(v) do
|
||||
p.z=pos.z+k
|
||||
if string.sub(v,k,k)=="1" then
|
||||
minetest.set_node(p,{name="default:brick"})
|
||||
else minetest.set_node(p,{name="air"})
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
make_maze(10,10,1,1)
|
||||
self.remove()
|
|
@ -0,0 +1,89 @@
|
|||
|
||||
if not init then
|
||||
|
||||
msg =
|
||||
"'Mensch argere Dich nicht' is a German board game (but not a German-style board game), developed by Josef Friedrich Schmidt in 1907/1908.\n"..
|
||||
" The players throw a die in turn and can advance any of their pieces in the game by the thrown number of dots on the dice.\n" ..
|
||||
"Throwing a six means bringing a piece into the game (by placing one from the 'out' area onto the 'start' field) and throwing the dice again. If\n" ..
|
||||
"a piece is on the 'start' field and there are still pieces in the 'out' area, it must be moved as soon as possible. If a piece cannot be\n".. "brought into the game then any other piece in the game must be moved by the thrown number, if that is possible. Pay attention that throwing\n".." dice continuously without moving is forbidden and by each dice throw you have to make a move.\n" ..
|
||||
"Pieces can jump over other pieces, and throw out pieces from other players (into that player's 'out' area) if they land on them. A player\n".. "cannot throw out his own pieces though, he can advance further than the last field in the 'home' row. A player can be thrown out if he is on\n"..
|
||||
"his 'start' field.\n" ..
|
||||
"Variation which is played by most players: A player who has no pieces in the game has 3 tries to throw a six"
|
||||
self.label(msg)
|
||||
|
||||
init = true;
|
||||
state = 1; -- game on
|
||||
step = 0;
|
||||
punchstate = 1; -- first punch
|
||||
punchpos = {}
|
||||
pos = self.spawnpos()
|
||||
dice = 0
|
||||
spawns = {{2,2,"basic_robot:buttonFF8080"},{2,11,"basic_robot:button8080FF"},{11,11,"basic_robot:button80FF80"},{11,2,"basic_robot:buttonFFFF80"}}
|
||||
|
||||
for i = 1,12 do for j = 1,12 do
|
||||
minetest.swap_node({x=pos.x+i,y=pos.y+1,z=pos.z+j},{name = "air"})
|
||||
end end
|
||||
|
||||
for k = 1,#spawns do
|
||||
for i = 0,1 do for j = 0,1 do
|
||||
minetest.swap_node({x=pos.x+i+spawns[k][1],y=pos.y+1,z=pos.z+j+spawns[k][2]},{name = spawns[k][3]})
|
||||
end end
|
||||
end
|
||||
|
||||
keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7)
|
||||
|
||||
end
|
||||
|
||||
if state == 0 then
|
||||
elseif state == 1 then
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
x = event.x-pos.x; y = event.y-pos.y; z = event.z-pos.z
|
||||
--say("type " .. event.type .. " pos " .. x .. " " .. y .. " " .. z)
|
||||
if x == 7 and y == 1 and z == 7 then
|
||||
_G.math.randomseed(os.time())
|
||||
dice = math.random(6);
|
||||
keyboard.set({x=pos.x+7,y=pos.y+1,z=pos.z+7},7+dice)
|
||||
step = step + 1;
|
||||
msg = colorize("red","<Mensch argere dich nicht>") .. " STEP " .. step .. ": " ..event.puncher .. " threw dice = " .. dice;
|
||||
minetest.chat_send_all(msg)
|
||||
self.label(msg)
|
||||
punchstate = 1
|
||||
elseif punchstate == 1 then
|
||||
if y == 1 and event.type ~= 2 and event.type<7 then
|
||||
punchpos = 2; minetest.chat_send_player(event.puncher,colorize("red","<Mensch argere dich nicht>") .. " punch place on board where to move ")
|
||||
punchpos = {x=event.x,y=event.y,z=event.z}
|
||||
punchstate = 2
|
||||
end
|
||||
elseif punchstate == 2 then
|
||||
if y == 0 and event.type ~= 2 then
|
||||
if x<2 or x>12 or z<2 or z>12 then
|
||||
else
|
||||
local nodename = minetest.get_node(punchpos).name;
|
||||
minetest.swap_node({x=event.x, y = event.y+1, z=event.z},{name = nodename})
|
||||
minetest.swap_node(punchpos,{name = "air"})
|
||||
punchstate = 1; dice = 0
|
||||
minetest.add_particle(
|
||||
{
|
||||
pos = punchpos,
|
||||
expirationtime = 15,
|
||||
velocity = {x=0, y=0,z=0},
|
||||
size = 18,
|
||||
texture = "default_apple.png",
|
||||
acceleration = {x=0,y=0,z=0},
|
||||
collisiondetection = true,
|
||||
collision_removal = true,
|
||||
}
|
||||
)
|
||||
msg = colorize("red","<Mensch argere dich nicht>") .. " " .. event.puncher .. " moved.";
|
||||
minetest.chat_send_all(msg)
|
||||
self.label(msg)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
end
|
|
@ -0,0 +1,83 @@
|
|||
-- minesweeper
|
||||
if not data then
|
||||
m=24;n=22; minescount = m*n/5;
|
||||
reward = 30;
|
||||
|
||||
if not find_player(4) then error("minesweeper: no players near") end
|
||||
|
||||
self.spam(1)
|
||||
t0 = _G.minetest.get_gametime();
|
||||
data = {}; spawnpos = self.spawnpos() -- place mines
|
||||
for i = 1, minescount do local i = math.random(m); local j = math.random(n); if not data[i] then data[i] = {} end; data[i][j] = 1; end
|
||||
if not data[1] then data[1] = {} end if not data[2] then data[2] = {} end -- create 2x2 safe area
|
||||
data[1][1] = 0;data[1][2] = 0;data[2][1] = 0;data[2][2] = 0;
|
||||
|
||||
minescount = 0;
|
||||
for i = 1,m do for j = 1,n do -- render game
|
||||
if data[i] and data[i][j] == 1 then minescount = minescount + 1 end
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})~="basic_robot:button808080" then
|
||||
puzzle.set_node({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},{name = "basic_robot:button808080"})
|
||||
end
|
||||
end end
|
||||
puzzle.set_node({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z+1},{name = "basic_robot:button80FF80"})
|
||||
|
||||
get_mine_count = function(i,j)
|
||||
if i<0 or i>m+1 or j<0 or j>n+1 then return 0 end; count = 0
|
||||
for k = -1,1 do for l = -1,1 do
|
||||
if data[i+k] and data[i+k][j+l] == 1 then count = count +1 end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
chk_mines = function()
|
||||
local count = minescount;
|
||||
for i=1,m do for j=1,n do
|
||||
if keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j})=="basic_robot:buttonFF8080" and data[i] and data[i][j]==1 then
|
||||
count=count-1
|
||||
end
|
||||
end end
|
||||
return count
|
||||
end
|
||||
say("minesweeper " .. m .. "x" ..n .. " with " .. minescount .. " mines ")
|
||||
self.label("find all hidden mines! mark mine by standing on top of block and punch,\notherwise it will uncover the block (and possibly explode).")
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event then
|
||||
local x = event.x - spawnpos.x;local y = event.y - spawnpos.y;local z = event.z - spawnpos.z;
|
||||
if x<1 or x>m or z<1 or z>n then
|
||||
if x == 0 and z == 1 then
|
||||
local count = chk_mines();
|
||||
if count == 0 then
|
||||
t0 = _G.minetest.get_gametime() - t0;
|
||||
say("congratulations! " .. event.puncher .. " discovered all mines in " .. t0 .. " s")
|
||||
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward
|
||||
else
|
||||
reward = reward*(1-(count/minescount))^(1.5); reward = math.floor(reward);
|
||||
say("FAIL! " .. count .. " mines remaining. You get " .. reward .. " gold for found mines")
|
||||
_G.minetest.add_item({x=spawnpos.x,y=spawnpos.y+1,z=spawnpos.z},_G.ItemStack("default:gold_ingot "..reward)) -- diamond reward
|
||||
end
|
||||
self.remove()
|
||||
end
|
||||
else --if event.type == 2 then
|
||||
local ppos = player.getpos(event.puncher)
|
||||
if ppos and math.abs(ppos.x-event.x)<0.5 and math.abs(ppos.z-event.z)<0.5 then -- just mark mine
|
||||
if keyboard.read({x=event.x,y=event.y,z=event.z})~="basic_robot:button808080" then
|
||||
puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:button808080"})
|
||||
else
|
||||
puzzle.set_node({x=event.x,y=event.y,z=event.z},{name = "basic_robot:buttonFF8080"})
|
||||
end
|
||||
else
|
||||
if data[x] and data[x][z]==1 then
|
||||
say("boom! "..event.puncher .. " is dead ");puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:buttonFF8080"});
|
||||
local player_ = puzzle.get_player(event.puncher);
|
||||
player_:setpos({x=spawnpos.x-1,y=spawnpos.y+1,z=spawnpos.z-1});
|
||||
self.remove()
|
||||
else
|
||||
local count = get_mine_count(x,z);
|
||||
if count == 0 then puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button80FF80"})
|
||||
else puzzle.set_node({x=spawnpos.x+x,y=spawnpos.y,z=spawnpos.z+z},{name = "basic_robot:button"..count}) end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,264 @@
|
|||
-- nonogram game, created in 1hr 40 min by rnd
|
||||
|
||||
-- INIT
|
||||
if not grid then
|
||||
n=6
|
||||
solved = false -- do we render solution or blank?
|
||||
-- _G.math.randomseed(3)
|
||||
|
||||
self.spam(1)
|
||||
function get_score_from_string(score)
|
||||
--say(score)
|
||||
local scores = {};
|
||||
local j=1; --j k l
|
||||
for i=0,5 do -- 0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999
|
||||
j = string.find(score," ", j+1);
|
||||
local k = string.find(score," ", j+1);
|
||||
local l = string.find(score," ", k+1);
|
||||
if i==5 then l = string.len(score)+1 end
|
||||
scores[i] = {string.sub(score,j+1,k-1),tonumber(string.sub(score,k+1,l-1))};
|
||||
j=l
|
||||
end
|
||||
return scores
|
||||
end
|
||||
|
||||
if not rom.score then _,rom.score = book.read(1) end
|
||||
if not rom.score then rom.score = "0 - 999 1 - 999 2 - 999 3 - 999 4 - 999 5 - 999" end
|
||||
highscore = get_score_from_string(rom.score)
|
||||
--self.label(string.gsub(_G.dump(highscore), "\n",""))
|
||||
|
||||
function get_score_string(scores)
|
||||
local out = ""
|
||||
for i = 0,5 do
|
||||
out = out .. i .. " " ..
|
||||
scores[i][1] .. " " ..
|
||||
scores[i][2] .. " "
|
||||
end
|
||||
return out
|
||||
end
|
||||
|
||||
t0 = _G.minetest.get_gametime()
|
||||
local intro ="numbers at beginning of each row (coloumn) tell how many\nred blocks are together in each row ( coloumn )." ..
|
||||
"\npunch gray blocks to toggle them and reveal hidden red blocks.\npunch green to check solution. If you give up punch blue.";
|
||||
self.label(intro)
|
||||
|
||||
grid = {}
|
||||
spawnpos = self.spawnpos();
|
||||
offsetx = 10 - math.ceil(n/2); offsetz = math.floor(n/2);
|
||||
spawnpos.x = spawnpos.x - offsetx; spawnpos.z = spawnpos.z - offsetz;
|
||||
spawnpos.y = spawnpos.y+3
|
||||
|
||||
for i=1,n do
|
||||
grid[i]={};
|
||||
for j=1,n do
|
||||
grid[i][j]=math.random(2)-1
|
||||
end
|
||||
end
|
||||
|
||||
getcounts = function(grid)
|
||||
local rowdata = {};
|
||||
for i=1,n do
|
||||
rowdata[i]={}; local data = rowdata[i];
|
||||
local s=0;local c=0;
|
||||
for j = 1, n do
|
||||
if s == 0 and grid[i][j]==1 then s=1;c=0 end
|
||||
if s == 1 then
|
||||
if grid[i][j]==1 then
|
||||
c=c+1
|
||||
if j == n then data[#data+1]=c end
|
||||
else
|
||||
data[#data+1]=c; s=0
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
local coldata = {};
|
||||
for j=1,n do
|
||||
coldata[j]={}; local data = coldata[j];
|
||||
local s=0;local c=0;
|
||||
for i = 1, n do
|
||||
if s == 0 and grid[i][j]==1 then s=1;c=0 end
|
||||
if s == 1 then
|
||||
if grid[i][j]==1 then
|
||||
c=c+1
|
||||
if i == n then data[#data+1]=c end
|
||||
else
|
||||
data[#data+1]=c; s=0
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
return rowdata,coldata
|
||||
end
|
||||
|
||||
read_field = function()
|
||||
local grid = {};
|
||||
for i = 1, n do
|
||||
grid[i]={};
|
||||
for j = 1,n do
|
||||
local typ = keyboard.read({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+i});
|
||||
if typ == "basic_robot:button808080" then grid[i][j] = 0 else grid[i][j] = 1 end
|
||||
end
|
||||
end
|
||||
return grid
|
||||
end
|
||||
|
||||
rowdata,coldata = getcounts(grid)
|
||||
|
||||
check_solution = function()
|
||||
local rdata,cdata;
|
||||
rdata,cdata = getcounts(read_field())
|
||||
for i = 1,#rdata do
|
||||
if #rdata[i]~=#rowdata[i] then return false end
|
||||
for j = 1, #rdata[i] do
|
||||
if rdata[i][j]~=rowdata[i][j] then return false end
|
||||
end
|
||||
end
|
||||
|
||||
for i = 1,#cdata do
|
||||
if #cdata[i]~=#coldata[i] then return false end
|
||||
for j = 1, #rdata[i] do
|
||||
if cdata[i][j]~=coldata[i][j] then return false end
|
||||
end
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
get_difficulty = function()
|
||||
local easy = 0;
|
||||
for k = 1, n do
|
||||
local sum=0
|
||||
for i = 1,#rowdata[k]-1 do
|
||||
sum = sum + rowdata[k][i]+1;
|
||||
end
|
||||
if #rowdata[k]>0 then sum = sum + rowdata[k][#rowdata[k]] else sum = n end
|
||||
if sum == n then easy = easy + 1 end
|
||||
end
|
||||
|
||||
for k = 1, n do
|
||||
local sum=0
|
||||
for i = 1,#coldata[k]-1 do
|
||||
sum = sum + coldata[k][i]+1;
|
||||
end
|
||||
if #coldata[k]>0 then sum = sum + coldata[k][#coldata[k]] else sum = n end
|
||||
if sum == n then easy = easy + 1 end
|
||||
end
|
||||
easy = 5-easy;
|
||||
if easy < 0 then easy = 0 end
|
||||
return easy
|
||||
end
|
||||
|
||||
-- render game
|
||||
for i=1,n do
|
||||
for j =1,n do
|
||||
keyboard.set({x=spawnpos.x-n+j,y=spawnpos.y,z=spawnpos.z+i},0) -- clear
|
||||
keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+2*n-i+1},0) -- clear
|
||||
local typ;
|
||||
if grid[j][i]==0 then typ = 2 else typ = 3 end
|
||||
if not solved then typ = 2 end
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ) --board
|
||||
end
|
||||
end
|
||||
|
||||
--render counts rows
|
||||
for i=1,n do
|
||||
length = #rowdata[i]
|
||||
for k = 1,length do
|
||||
keyboard.set({x=spawnpos.x-length+k,y=spawnpos.y,z=spawnpos.z+i},rowdata[i][k]+7)
|
||||
end
|
||||
end
|
||||
--render counts coloumns
|
||||
for j=1,n do
|
||||
length = #coldata[j]
|
||||
for k = 1,length do
|
||||
keyboard.set({x=spawnpos.x+j,y=spawnpos.y,z=spawnpos.z+k+n},coldata[j][k]+7)
|
||||
end
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},4) -- game check button
|
||||
keyboard.set({x=spawnpos.x+2,y=spawnpos.y,z=spawnpos.z},5) -- game check button
|
||||
|
||||
local players = find_player(4,spawnpos)
|
||||
if not players then error("minesweeper: no players near") end
|
||||
local pname = players[1];
|
||||
|
||||
|
||||
--self.label()
|
||||
|
||||
--self.label(string.gsub(_G.dump(read_field()),"\n","") )
|
||||
difficulty = get_difficulty()
|
||||
reward = 0; limit = 0;
|
||||
|
||||
if difficulty == 5 then limit = 120 reward = 10
|
||||
elseif difficulty == 4 then limit = 115 reward = 9 -- 60s
|
||||
elseif difficulty == 3 then limit = 100 reward = 8
|
||||
elseif difficulty == 2 then limit = 80 reward = 7
|
||||
elseif difficulty <= 1 then limit = 70 reward = 6
|
||||
end
|
||||
minetest.chat_send_player(pname, "nonogram difficulty " .. difficulty .. ". you will get " .. reward .. " gold if you solve it in faster than " .. limit .."s" ..
|
||||
". Current record " .. highscore[difficulty][2] .. " by " .. highscore[difficulty][1])
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get()
|
||||
if event then
|
||||
if event.y == spawnpos.y and event.z == spawnpos.z then
|
||||
if event.x == spawnpos.x+1 then -- check solution
|
||||
if check_solution() then
|
||||
t = _G.minetest.get_gametime(); t = t- t0;
|
||||
local msg = "";
|
||||
keyboard.set({x=spawnpos.x+1,y=spawnpos.y,z=spawnpos.z},2)
|
||||
msg = n .. "x" .. n .. " nonogram (difficuly " .. difficulty .. ") solved by " .. event.puncher .. " in " .. t .. " seconds. "
|
||||
|
||||
if t < limit then
|
||||
msg = msg .. " He gets " .. reward .. " gold for quick solve.";
|
||||
else
|
||||
reward = reward*2*(1-2*(t-limit)/limit)/2; if reward<0 then reward = 0 end
|
||||
reward = math.floor(reward);
|
||||
msg = msg .. " Your time was more than " .. limit .. ", you get " .. reward .. " gold ";
|
||||
end
|
||||
|
||||
-- highscore
|
||||
if t<highscore[difficulty][2] then
|
||||
say("nonogram: new record " .. t .. " s ! old record " .. highscore[difficulty][2] .. "s by " .. highscore[difficulty][1])
|
||||
highscore[difficulty] = {event.puncher, t}
|
||||
rom.score = get_score_string(highscore)
|
||||
book.write(1,"scores", rom.score)
|
||||
end
|
||||
|
||||
if reward>0 then
|
||||
local player = _G.minetest.get_player_by_name(event.puncher);
|
||||
if player then
|
||||
local inv = player:get_inventory();
|
||||
inv:add_item("main",_G.ItemStack("default:gold_ingot " .. reward))
|
||||
end
|
||||
end
|
||||
minetest.chat_send_player(event.puncher,msg)
|
||||
|
||||
self.remove()
|
||||
|
||||
else self.label("FAIL") end
|
||||
elseif event.x == spawnpos.x+2 then -- solve
|
||||
minetest.chat_send_player(event.puncher,"you gave up on game, displaying solution")
|
||||
for i=1,n do
|
||||
for j =1,n do
|
||||
local typ;
|
||||
if grid[j][i]==0 then typ = 2 else typ = 3 end
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},typ)
|
||||
end
|
||||
end
|
||||
self.remove()
|
||||
end
|
||||
else
|
||||
local i = event.x-spawnpos.x;local j = event.z-spawnpos.z;
|
||||
if i>0 and i<=n and j>0 and j<=n then
|
||||
local typ = keyboard.read({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j});
|
||||
local newtyp;
|
||||
if typ == "basic_robot:button808080" then newtyp = 3
|
||||
else newtyp = 2
|
||||
end
|
||||
keyboard.set({x=spawnpos.x+i,y=spawnpos.y,z=spawnpos.z+j},newtyp);
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,101 @@
|
|||
-- sliding unscramble game by rnd, made in 20 minutes
|
||||
if not init then
|
||||
reward = "default:gold_ingot"
|
||||
size = 3;
|
||||
|
||||
init = true
|
||||
spos = self.spawnpos(); spos.y = spos.y + 1
|
||||
board = {};
|
||||
local players = find_player(4);
|
||||
if not players then say("#sliding puzzle game: no players") self.remove() end
|
||||
name = players[1];
|
||||
|
||||
minetest.chat_send_player(name, "#SLIDING GAME: try to sort numbers in increasing order, starting from top left")
|
||||
|
||||
create_board = function(n)
|
||||
local k = 0;
|
||||
local ret = scramble(n*n, os.time())
|
||||
for i = 1,n do
|
||||
board[i]={};
|
||||
for j = 1,n do
|
||||
k=k+1
|
||||
board[i][j]=7+ret[k] -- 7 numbers, 82 letters
|
||||
end
|
||||
end
|
||||
board[math.random(n)][math.random(n)] = 0
|
||||
end
|
||||
|
||||
render_board = function()
|
||||
local n = #board;
|
||||
for i = 1,n do
|
||||
for j = 1,n do
|
||||
keyboard.set({x=spos.x+i, y = spos.y, z=spos.z+j}, board[i][j])
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
check_score = function() -- check how many places are increasing in order, starting top left
|
||||
local n = #board;
|
||||
local cmax = 0;
|
||||
local score = 0;
|
||||
for j = n,1,-1 do
|
||||
for i = 1,n do
|
||||
local b = board[i][j];
|
||||
if b==0 or b<cmax then return score else score = score +1 cmax = b end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
find_hole = function(i,j)
|
||||
if board[i][j] == 0 then return i,j end
|
||||
if i>1 and board[i-1][j] == 0 then return i-1,j end
|
||||
if i<#board and board[i+1][j] == 0 then return i+1,j end
|
||||
if j>1 and board[i][j-1] == 0 then return i,j-1 end
|
||||
if j<#board and board[i][j+1] == 0 then return i,j+1 end
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
scramble = function(n,seed)
|
||||
_G.math.randomseed(seed);
|
||||
local ret = {}; for i = 1,n do ret[i]=i end
|
||||
for j = n,2,-1 do
|
||||
local k = math.random(j);
|
||||
if k~=j then
|
||||
local tmp = ret[k]; ret[k] = ret[j]; ret[j] = tmp
|
||||
end
|
||||
end
|
||||
return ret
|
||||
end
|
||||
|
||||
create_board(size)
|
||||
render_board()
|
||||
|
||||
end
|
||||
|
||||
event = keyboard.get();
|
||||
if event and event.y == spos.y then
|
||||
local x = event.x-spos.x;
|
||||
local z = event.z-spos.z;
|
||||
if x<1 or x>size or z<1 or z>size then
|
||||
else
|
||||
local i,j = find_hole(x,z);
|
||||
if i then
|
||||
local tmp = board[x][z];
|
||||
keyboard.set({x=spos.x+x, y = spos.y, z=spos.z+z}, board[i][j])
|
||||
board[x][z] = board[i][j]
|
||||
board[i][j] = tmp;
|
||||
keyboard.set({x=spos.x+i, y = spos.y, z=spos.z+j}, tmp)
|
||||
end
|
||||
local score = check_score()
|
||||
self.label("score : " .. score)
|
||||
if score >= size*size-2 then
|
||||
minetest.chat_send_player(name, "CONGRATULATIONS! YOU SOLVED PUZZLE. REWARD WAS DROPPED ON TOP OF ROBOT.")
|
||||
pos = self.pos(); pos.y = pos.y+2;
|
||||
minetest.add_item(pos, _G.ItemStack(reward))
|
||||
self.remove()
|
||||
end
|
||||
end
|
||||
|
||||
end
|
|
@ -0,0 +1,189 @@
|
|||
-- SOKOBAN GAME, by rnd, robots port
|
||||
|
||||
|
||||
if not sokoban then
|
||||
sokoban = {};
|
||||
local players = find_player(8);
|
||||
if not players then error("sokoban: no player near") end
|
||||
name = players[1];
|
||||
|
||||
-- self.show_form(name,
|
||||
-- "size[2,1.25]"..
|
||||
-- "label[0,0;SELECT LEVEL 1-90]"..
|
||||
-- "field[0.25,1;1,1;LVL;LEVEL;1]"..
|
||||
-- "button_exit[1.25,0.75;1,1;OK;OK]"
|
||||
-- )
|
||||
state = 1 -- will wait for form receive otherwise game play
|
||||
self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks")
|
||||
|
||||
player_ = puzzle.get_player(name); -- get player entity - player must be present in area
|
||||
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}) -- reset player
|
||||
|
||||
|
||||
self.spam(1)
|
||||
sokoban.push_time = 0
|
||||
sokoban.blocks = 0;sokoban.level = 0; sokoban.moves=0;
|
||||
imax = 0; jmax = 0
|
||||
|
||||
sokoban.load=0;sokoban.playername =""; sokoban.pos = {};
|
||||
SOKOBAN_WALL = "moreblocks:cactus_brick"
|
||||
SOKOBAN_FLOOR = "default:silver_sandstone"
|
||||
SOKOBAN_GOAL = "default:aspen_tree"
|
||||
SOKOBAN_BOX = "basic_robot:buttonFFFFFF"
|
||||
|
||||
load_level = function(lvl)
|
||||
|
||||
local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1;
|
||||
sokoban.pos = pos;
|
||||
sokoban.playername = name
|
||||
|
||||
if lvl == nil then return end
|
||||
if lvl <0 or lvl >89 then return end
|
||||
|
||||
local file = _G.io.open(minetest.get_modpath("basic_robot").."\\scripts\\sokoban.txt","r")
|
||||
if not file then return end
|
||||
local str = ""; local s; local p = {x=pos.x,y=pos.y,z=pos.z}; local i,j;i=0;
|
||||
local lvl_found = false
|
||||
while str~= nil do
|
||||
str = file:read("*line");
|
||||
if str~=nil and str =="; "..lvl then lvl_found=true break end
|
||||
end
|
||||
if not lvl_found then file:close();return end
|
||||
|
||||
sokoban.blocks = 0;sokoban.level = lvl+1; sokoban.moves=0;
|
||||
imax=0; jmax = 0;
|
||||
while str~= nil do
|
||||
str = file:read("*line");
|
||||
if str~=nil then
|
||||
if string.sub(str,1,1)==";" then
|
||||
imax=i;
|
||||
file:close();
|
||||
player_:set_physics_override({jump=0})
|
||||
player_:set_eye_offset({x=0,y=20,z=0},{x=0,y=0,z=0});
|
||||
return
|
||||
end
|
||||
i=i+1;
|
||||
if string.len(str)>jmax then jmax = string.len(str) end -- determine max dimensions
|
||||
for j = 1,string.len(str) do
|
||||
p.x=pos.x+i;p.y=pos.y; p.z=pos.z+j; s=string.sub(str,j,j);
|
||||
p.y=p.y-1;
|
||||
if puzzle.get_node(p).name~=SOKOBAN_FLOOR then puzzle.set_node(p,{name=SOKOBAN_FLOOR}); end -- clear floor
|
||||
p.y=p.y+1;
|
||||
if s==" " and puzzle.get_node(p).name~="air" then puzzle.set_node(p,{name="air"}) end
|
||||
if s=="#" and puzzle.get_node(p).name~=SOKOBAN_WALL then puzzle.set_node(p,{name=SOKOBAN_WALL}) end
|
||||
if s=="$" then puzzle.set_node(p,{name=SOKOBAN_BOX});sokoban.blocks=sokoban.blocks+1 end
|
||||
if s=="." then p.y=p.y-1;puzzle.set_node(p,{name=SOKOBAN_GOAL}); p.y=p.y+1;puzzle.set_node(p,{name="air"}) end
|
||||
--starting position
|
||||
if s=="@" then
|
||||
player_:setpos({x=p.x,y=p.y-0.5,z=p.z}); -- move player to start position
|
||||
--p.y=p.y-1;puzzle.set_node(p,{name="default:glass"});
|
||||
puzzle.set_node(p,{name="air"})
|
||||
p.y=p.y+1;puzzle.set_node(p,{name="air"})
|
||||
--p.y=p.y+2;puzzle.set_node(p,{name="default:ladder"})
|
||||
end
|
||||
if s~="@" then p.y = pos.y+2;puzzle.set_node(p,{name="air"}); -- ceiling default:obsidian_glass
|
||||
else --p.y=pos.y+2;puzzle.set_node(p,{name="default:ladder"})
|
||||
end -- roof above to block jumps
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
file:close();
|
||||
end
|
||||
|
||||
clear_game = function()
|
||||
local pos = self.spawnpos(); pos.x=pos.x+1;pos.y=pos.y+1;
|
||||
for i = 1, 20 do
|
||||
for j = 1,20 do
|
||||
local node = minetest.get_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}).name
|
||||
if node ~= "default:silver_sandstone" then minetest.set_node({x=pos.x+i,y=pos.y-1,z=pos.z+j}, {name = "default:silver_sandstone"}) end
|
||||
node = minetest.get_node({x=pos.x+i,y=pos.y,z=pos.z+j}).name
|
||||
if node ~= "air" then minetest.set_node({x=pos.x+i,y=pos.y,z=pos.z+j}, {name = "air"}) end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
|
||||
if state == 1 then
|
||||
clear_game()
|
||||
load_level(20)
|
||||
state = 0
|
||||
self.label("stand close to white box and punch it one time to push it. you can only push 1 box\nand cant pull. goal is to get all white boxes pushed on aspen blocks")
|
||||
else
|
||||
|
||||
local ppos = player_:getpos()
|
||||
if math.abs(ppos.y-sokoban.pos.y)~= 0.5 then minetest.chat_send_player(name,colorize("red", "SOKOBAN: " .. name .. " QUITS ! "));
|
||||
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0});player_:set_physics_override({jump=1}); self.remove() end
|
||||
|
||||
event = keyboard.get();
|
||||
|
||||
if event then
|
||||
|
||||
local pname = event.puncher
|
||||
if pname ~= name then goto quit end
|
||||
local pos = {x=event.x, y = event.y, z = event.z};
|
||||
local p=player.getpos(pname);local q={x=pos.x,y=pos.y,z=pos.z}
|
||||
p.x=p.x-q.x;p.y=p.y-q.y;p.z=p.z-q.z
|
||||
if math.abs(p.y+0.5)>0 then goto quit end
|
||||
if math.abs(p.x)>math.abs(p.z) then -- determine push direction
|
||||
if p.z<-0.5 or p.z>0.5 or math.abs(p.x)>1.5 then goto quit end
|
||||
if p.x+q.x>q.x then q.x= q.x-1
|
||||
else q.x = q.x+1
|
||||
end
|
||||
else
|
||||
if p.x<-0.5 or p.x>0.5 or math.abs(p.z)>1.5 then goto quit end
|
||||
if p.z+q.z>q.z then q.z= q.z-1
|
||||
else q.z = q.z+1
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
if minetest.get_node(q).name=="air" then -- push crate
|
||||
sokoban.moves = sokoban.moves+1
|
||||
local old_infotext = minetest.get_meta(pos):get_string("infotext");
|
||||
minetest.set_node(pos,{name="air"})
|
||||
minetest.set_node(q,{name=SOKOBAN_BOX})
|
||||
minetest.sound_play("default_dig_dig_immediate", {pos=q,gain=1.0,max_hear_distance = 24,}) -- sound of pushing
|
||||
local meta = minetest.get_meta(q);
|
||||
q.y=q.y-1;
|
||||
if minetest.get_node(q).name==SOKOBAN_GOAL then
|
||||
if old_infotext~="GOAL REACHED" then
|
||||
sokoban.blocks = sokoban.blocks -1;
|
||||
end
|
||||
meta:set_string("infotext", "GOAL REACHED")
|
||||
else
|
||||
if old_infotext=="GOAL REACHED" then
|
||||
sokoban.blocks = sokoban.blocks +1
|
||||
end
|
||||
--meta:set_string("infotext", "push crate on top of goal block")
|
||||
end
|
||||
end
|
||||
|
||||
if sokoban.blocks~=0 then -- how many blocks left
|
||||
--say("move " .. sokoban.moves .. " : " ..sokoban.blocks .. " crates left ");
|
||||
else
|
||||
say("games: ".. name .. " just solved sokoban level ".. sokoban.level .. " in " .. sokoban.moves .. " moves.");
|
||||
player_:set_physics_override({jump=1})
|
||||
player_:set_eye_offset({x=0,y=0,z=0},{x=0,y=0,z=0})
|
||||
|
||||
local player = _G.minetest.get_player_by_name(event.puncher);
|
||||
if player then
|
||||
local inv = player:get_inventory();
|
||||
inv:add_item("main",_G.ItemStack("skyblock:sokoban 4 "))
|
||||
end
|
||||
|
||||
local i,j;
|
||||
for i = 1,imax do
|
||||
for j=1,jmax do
|
||||
minetest.set_node({x= sokoban.pos.x+i,y=sokoban.pos.y,z=sokoban.pos.z+j}, {name = "air"}); -- clear level
|
||||
end
|
||||
end
|
||||
|
||||
sokoban.playername = ""; sokoban.level = 1
|
||||
end
|
||||
::quit::
|
||||
end
|
||||
end
|
|
@ -0,0 +1,101 @@
|
|||
--[[
|
||||
SWITCHING GAME by rnd, 2018
|
||||
|
||||
lights:
|
||||
0110
|
||||
|
||||
switches, each one toggles certain lights like: s1 1001 (toggles light with 1)
|
||||
|
||||
PROBLEM:
|
||||
hit switches in correct order to turn on all lights
|
||||
|
||||
GENERATE RANDOM CHALLENGE:
|
||||
start with all lights on and apply random sequence of switches
|
||||
|
||||
TODO: instead of simply 0/1 switches have ones that advance +1 mod p (p can be say 3 or more)
|
||||
|
||||
|
||||
REMARKS: application of 2 different switches is commutative ( obvious, since just x->x+1 mod p)
|
||||
--]]
|
||||
if not init then
|
||||
|
||||
init = true
|
||||
numlights = 2;
|
||||
numswitches = 2;
|
||||
states = 10;
|
||||
|
||||
lights = {}; -- states of lights, initialy 1,1,...,1
|
||||
for i = 1, numlights do lights[i] = 0 end
|
||||
switches = {}
|
||||
|
||||
--switches = {{1,0,0,1},{1,1,1,1}};
|
||||
make_random_switches = function(lights, switches,count)
|
||||
for i = 1, count do
|
||||
switches[i] = {};
|
||||
local switch = switches[i];
|
||||
for j = 1, #lights do switch[j] = math.random(states)-1 end
|
||||
end
|
||||
end
|
||||
make_random_switches(lights,switches, numswitches)
|
||||
|
||||
|
||||
pos = self.spawnpos(); pos.x = pos.x + 1;-- pos.z = pos.z + 1
|
||||
|
||||
apply_switch = function(switches,lights,idx)
|
||||
local switch = switches[idx];
|
||||
for i = 1, #switch do
|
||||
local state = lights[i] + switch[i];
|
||||
if state >= states then state = state - states end
|
||||
lights[i] = state
|
||||
end
|
||||
end
|
||||
|
||||
randomize = function(switches, lights, steps) -- randomize lights
|
||||
for i = 1, steps do
|
||||
local idx = math.random(#switches);
|
||||
apply_switch(switches,lights,idx);
|
||||
end
|
||||
end
|
||||
|
||||
render_lights = function() for i = 1, #lights do keyboard.set({x=pos.x+i-1,y=pos.y+1, z=pos.z}, 7+lights[i]) end end
|
||||
render_switches = function(mode)
|
||||
if mode then
|
||||
for i = 1, #switches do keyboard.set({x=pos.x+i-1,y=pos.y, z=pos.z}, 1+i) end
|
||||
else
|
||||
for i = 1, #switches do keyboard.set({x=pos.x+i-1,y=pos.y, z=pos.z}, 0) end
|
||||
end
|
||||
end
|
||||
|
||||
check_lights = function()
|
||||
for i = 1, #lights do if lights[i] ~= 0 then return false end end
|
||||
return true
|
||||
end
|
||||
step = 0
|
||||
|
||||
randomize(switches,lights, math.min((#switches)^states,10000))
|
||||
if check_lights() then randomize(switches,lights, #switches + states) end
|
||||
|
||||
render_lights(); render_switches(true)
|
||||
|
||||
|
||||
self.label("GOAL OF GAME: punch buttons with numbers in correct order to turn all blocks to 0")
|
||||
|
||||
--self.label(serialize(switches))
|
||||
end
|
||||
|
||||
|
||||
event = keyboard.get()
|
||||
if event then
|
||||
local idx = event.x-pos.x+1;
|
||||
if event.y==pos.y and idx>=1 and idx <= #switches then
|
||||
apply_switch(switches, lights, idx)
|
||||
render_lights()
|
||||
step = step + 1
|
||||
if check_lights() then
|
||||
self.label("DONE IN " .. step .. " STEPS !")
|
||||
render_switches(false)
|
||||
else
|
||||
self.label("STEP " .. step)
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,85 @@
|
|||
-- rnd 2017
|
||||
-- instructions: put 7 buttons around bot(top one left empty)
|
||||
-- clockwise: empty, green, yellow,blue, red, blue,yellow,green.
|
||||
-- those buttons serve as controls
|
||||
|
||||
if not s then
|
||||
name = self.name();
|
||||
direction = 1;
|
||||
s=0;
|
||||
self.label("TANK ROBOT. control with colored buttons")
|
||||
user=find_player(4); if user then user = user[1] end
|
||||
|
||||
speed = 7 + math.random(7);turn.angle(math.random(360));
|
||||
pitch = 0
|
||||
|
||||
gravity = 1+math.random(2);
|
||||
if user then
|
||||
say("TANK ROBOT, ready. ".. user .. " in control")
|
||||
else
|
||||
say("no player found nearby. deactivating"); self.remove()
|
||||
s=-1
|
||||
end
|
||||
pos = self.spawnpos();
|
||||
end
|
||||
|
||||
ppos = player.getpos(user); ppos.x=ppos.x-pos.x;ppos.y=ppos.y-pos.y;ppos.z=ppos.z-pos.z;
|
||||
if ppos.x^2+ppos.y^2+ppos.z^2>10 then
|
||||
local obj = _G.minetest.get_player_by_name(user);
|
||||
if obj then say("deserter " .. user .. " killed for abandoning the tank!") obj:set_hp(0) end
|
||||
self.remove()
|
||||
else
|
||||
local obj = _G.minetest.get_player_by_name(user);
|
||||
if obj then
|
||||
if obj:get_hp() == 0 then
|
||||
say("TANK DESTROYED!")
|
||||
self.remove()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if s == 0 then
|
||||
event = keyboard.get();
|
||||
if event and event.puncher==user then
|
||||
--self.label(event.x-pos.x .. " " .. event.y-pos.y .. " " .. event.z-pos.z .. " T " .. event.type)
|
||||
event.x = event.x-pos.x;event.y = event.y-pos.y;event.z = event.z-pos.z;
|
||||
if event.x == 0 and event.y == 0 and event.z == 1 then
|
||||
self.fire(speed, pitch,gravity)
|
||||
s=1;self.label("BOOM")
|
||||
_G.minetest.sound_play("tnt_explode",{pos = self.pos(), max_hear_distance = 256, gain = 1})
|
||||
elseif event.x == direction*1 and event.y == 0 and event.z == direction*1 then
|
||||
turn.angle(2)
|
||||
elseif event.x == -1*direction and event.y == 0 and event.z == 1*direction then
|
||||
turn.angle(-2)
|
||||
elseif event.x == 1*direction and event.y == 0 and event.z == 0 then
|
||||
turn.angle(40)
|
||||
elseif event.x == -1*direction and event.y == 0 and event.z == 0 then
|
||||
turn.angle(-40)
|
||||
elseif event.x == 1*direction and event.y == 0 and event.z == -1*direction then
|
||||
pitch = pitch + 5; if pitch> 85 then pitch = 85 end
|
||||
self.label("pitch " .. pitch)
|
||||
elseif event.x == -1*direction and event.y == 0 and event.z == -1*direction then
|
||||
pitch = pitch - 5; if pitch<-10 then pitch = -10 end
|
||||
self.label("pitch " .. pitch)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if s == 1 then
|
||||
local pos = self.fire_pos();
|
||||
if pos then
|
||||
self.label("HIT")
|
||||
msg = "";
|
||||
_G.minetest.sound_play("tnt_explode",{pos = pos, max_hear_distance = 256, gain = 1})
|
||||
|
||||
local objs=_G.minetest.get_objects_inside_radius(pos, 4);
|
||||
for _,obj in pairs(objs) do
|
||||
if obj:is_player() then
|
||||
obj:set_hp(0)
|
||||
msg = msg .. obj:get_player_name() .. " is dead, "
|
||||
end
|
||||
end
|
||||
s = 0
|
||||
if msg~="" then say(msg) end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,112 @@
|
|||
-- paint canvas by rnd, 2018
|
||||
if not init then
|
||||
colors = {
|
||||
"black","blue","brown","cyan","dark_green","dark_grey","green","grey",
|
||||
"magenta","orange","pink","red","violet","white","yellow"
|
||||
}
|
||||
invcolors = {}; for i = 1,#colors do invcolors[colors[i]] = i end
|
||||
|
||||
color = 1;
|
||||
size = 16;
|
||||
|
||||
init = true
|
||||
|
||||
local ent = _G.basic_robot.data[self.name()].obj:get_luaentity();
|
||||
ent.timestep = 0.5
|
||||
|
||||
players = find_player(5); if not players then self.remove() end
|
||||
player = _G.minetest.get_player_by_name(players[1])
|
||||
self.label("-> " .. players[1])
|
||||
|
||||
spos = self.spawnpos(); spos.y=spos.y+1;
|
||||
|
||||
canvasn = "wool:white"
|
||||
reset_canvas = function()
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = canvasn})
|
||||
end
|
||||
end
|
||||
end
|
||||
reset_canvas()
|
||||
|
||||
save_image = function()
|
||||
local ret = {};
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
local nname = string.sub(minetest.get_node({x=spos.x +i , y = spos.y + j, z = spos.z }).name,6)
|
||||
local pcolor = invcolors[nname] or 1;
|
||||
ret[#ret+1]= string.char(96+pcolor)
|
||||
end
|
||||
end
|
||||
return table.concat(ret,"")
|
||||
end
|
||||
|
||||
load_image = function(image)
|
||||
if not image then return end
|
||||
local ret = {}; local k = 0;
|
||||
for i = 1, size do
|
||||
for j = 1, size do
|
||||
k=k+1;
|
||||
local pcolor = colors[string.byte(image,k)-96] or "black";
|
||||
minetest.set_node({x=spos.x +i , y = spos.y + j, z = spos.z },{name = "wool:"..pcolor})
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
--draw buttons
|
||||
for i = 1,#colors do
|
||||
minetest.set_node({x=spos.x +i , y = spos.y , z = spos.z },{name = "wool:"..colors[i]})
|
||||
end
|
||||
|
||||
minetest.set_node({x=spos.x +1 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_83"})
|
||||
minetest.set_node({x=spos.x +2 , y = spos.y-1 , z = spos.z },{name = "basic_robot:button_76"})
|
||||
|
||||
|
||||
vn = {x=0,y=0,z=1};
|
||||
T0 = {x=spos.x+0.5,y=spos.y+0.5,z=spos.z-0.5*vn.z};
|
||||
|
||||
get_intersect = function(vn, T0, p, v)
|
||||
local a = (T0.x-p.x)*vn.x + (T0.y-p.y)*vn.y + (T0.z-p.z)*vn.z;
|
||||
local b = vn.x*v.x + vn.y*v.y + vn.z*v.z
|
||||
if b<=0 then return nil end
|
||||
if a<=0 then return nil end
|
||||
local t = a / b
|
||||
return {x = p.x+v.x*t, y= p.y+v.y*t, z = p.z+v.z*t}
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
if player:get_player_control().LMB then -- player interacts with 'virtual canvas gui'
|
||||
local v = player:get_look_dir();
|
||||
local p = player:get_pos(); p.y = p.y + 1.5
|
||||
local c = get_intersect(vn,T0,p,v);
|
||||
if c then
|
||||
|
||||
local x = c.x - T0.x; local y = c.y - T0.y
|
||||
if x>0 and x<size and y>-2 and y<size then
|
||||
if y>0 then -- above: painting
|
||||
c.z = c.z+0.5
|
||||
minetest.set_node(c, {name = "wool:" .. colors[color]})
|
||||
elseif y>-1 then -- color selection
|
||||
x = 1+math.floor(x)
|
||||
if colors[x] then
|
||||
color = x;
|
||||
self.label(colors[x])
|
||||
end
|
||||
else -- save,load button row
|
||||
x = 1+math.floor(x)
|
||||
if x==1 then
|
||||
self.label("SAVED.")
|
||||
book.write(1,"ROBOT_IMAGE",save_image())
|
||||
elseif x==2 then
|
||||
local _,image = book.read(1)
|
||||
load_image(image);
|
||||
self.label("LOADED.")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
|
@ -0,0 +1,58 @@
|
|||
-- painting import from minetest 'painting mod' to robot canvas by rnd
|
||||
-- stand near image and run "get_texture()" command in remote control
|
||||
|
||||
if not init then
|
||||
self.label("PAINTING IMPORTER")
|
||||
pname = "rnd"
|
||||
player = minetest.get_player_by_name(pname)
|
||||
|
||||
get_texture = function()
|
||||
|
||||
|
||||
local pos = player:get_pos(); local radius = 2
|
||||
local objs = minetest.get_objects_inside_radius(pos, radius)
|
||||
local obj = {};
|
||||
|
||||
local ret = {};
|
||||
for i=1,#objs do
|
||||
if not objs[i]:is_player() then obj = objs[i] break end
|
||||
end
|
||||
|
||||
if obj then
|
||||
local tex = obj:get_properties().textures
|
||||
local out = tex[1] or ""
|
||||
if string.sub(out,1,9) == "[combine:" then
|
||||
local pcolors = {"black","blue","brown","cyan","darkgreen","darkgrey","green","grey",
|
||||
"magenta","orange","pink","red","violet","white","yellow"}
|
||||
local ipcolors = {}; for i = 1,#pcolors do ipcolors[pcolors[i]] = i end
|
||||
|
||||
local ret = {};
|
||||
local i =0; local j = 1; local k = 0; local size = 16;
|
||||
--ret[1] = {}
|
||||
for word in out:gmatch("=(%a+)%.png") do
|
||||
ret[#ret+1] = string.char(96 + (ipcolors[word] or 1))
|
||||
end
|
||||
|
||||
local rret = {};
|
||||
for i = 1, size do rret[i] = {} for j = 1,size do rret[i][j] = 0 end end
|
||||
|
||||
k = 0 -- rotate 90 right
|
||||
for j = 1,size do
|
||||
for i = size,1,-1 do
|
||||
k = k + 1
|
||||
rret[size-i+1][size-j+1] = ret[k]
|
||||
end
|
||||
end
|
||||
|
||||
ret = {}; for i = 1, size do for j = 1, size do ret[#ret+1]= rret[i][j] end end -- write back
|
||||
|
||||
out = table.concat(ret,"")
|
||||
book.write(1,"IMPORTED_PAINTING", out)
|
||||
minetest.chat_send_player(pname, "PAINTING FOUND, saved in robot library in book 1.")
|
||||
end
|
||||
else return "empty"
|
||||
end
|
||||
end
|
||||
|
||||
init = true
|
||||
end
|
|
@ -0,0 +1,108 @@
|
|||
-- ROBOT craft guide by rnd, 2017
|
||||
if not list then
|
||||
|
||||
tname = "rnd";
|
||||
list = {};
|
||||
tmplist = _G.minetest.registered_items;
|
||||
for k,v in pairs(tmplist) do
|
||||
local texture = v.inventory_image or "";
|
||||
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
|
||||
if (not v.groups.not_in_craft_guide or v.groups.not_in_craft_guide == 0) and type(texture)=="string" and texture~="" then
|
||||
list[#list+1] = {_G.minetest.formspec_escape(k),_G.minetest.formspec_escape(v.description),_G.minetest.formspec_escape(texture)}; -- v.inventory_image, k, v.description
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
idx = 1; n = 35; row = 6; size = 1.25;
|
||||
filter = "" item = "" recipeid = 1
|
||||
filterlist = {}; for i = 1,#list do filterlist[i] = i end
|
||||
|
||||
get_texture = function(ritem)
|
||||
local v = _G.minetest.registered_items[ritem]; if not v then return "" end
|
||||
local texture = v.inventory_image or "";
|
||||
if texture=="" and v.tiles then texture = v.tiles[1] or "" end
|
||||
if type(texture)~="string" then return "" end
|
||||
return texture
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
local form = "size[7.5,8.5]";
|
||||
local x,y,i; local idxt = idx+n; if idxt > #filterlist then idxt = #filterlist end
|
||||
for i = idx, idxt do
|
||||
local id = filterlist[i];
|
||||
if list[id] and list[id][3] then
|
||||
x = ((i-idx) % row)
|
||||
y = (i-idx-x)/row;
|
||||
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. list[id][3] ..";".."item"..";".. list[id][1] .."]"
|
||||
end
|
||||
end
|
||||
form = form .. "textarea[0.25,0;2,0.75;filter;filter;"..filter .. "]" .. "button[2.,0;1,0.5;search;search]"..
|
||||
"button[5.5,0;1,0.5;prev;PREV]" .. "button[6.5,0;1,0.5;next;NEXT]" .. "label[4,0;".. idx .. "-"..idxt .. "/" .. #filterlist.."]";
|
||||
return form
|
||||
end
|
||||
|
||||
get_recipe = function()
|
||||
local form = "size[7.5,8.5]";
|
||||
local recipes = _G.minetest.get_all_craft_recipes(item); if not recipes then return end;
|
||||
local recipe = recipes[recipeid]; if not recipe then return end
|
||||
local items = recipe.items
|
||||
local x,y,i;
|
||||
for i = 0, 8 do
|
||||
local ritem = items[i+1] or ""; local sritem = "";
|
||||
local j = string.find(ritem,":"); if j then sritem = string.sub(ritem,j+1) end; --ritem = _G.minetest.formspec_escape(ritem);
|
||||
x = (i % 3)
|
||||
y = (i-x)/3;
|
||||
form = form .. "image_button[".. x*size ..",".. y*size+0.75 .. ";"..size.."," .. size .. ";" .. get_texture(ritem) ..";".."item"..";".. sritem .."]"
|
||||
end
|
||||
form = form .. "textarea[0.25,0;2,0.75;recipeid;recipeid ".. #recipes .. ";"..recipeid .. "]" .. "button[2.,0;1,0.5;go;go]"..
|
||||
"label[3,0;" .. item .. "]" .. "button[6.5,0;1,0.5;back;BACK]" ;
|
||||
return form
|
||||
end
|
||||
|
||||
s=0
|
||||
end
|
||||
|
||||
if s==0 then
|
||||
local p = find_player(4); s = 1
|
||||
if p then
|
||||
self.show_form(p[1],get_form())
|
||||
else
|
||||
self.remove()
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if sender then
|
||||
|
||||
if fields.search then
|
||||
filter = fields.filter or ""
|
||||
filterlist = {};
|
||||
for i = 1,#list do
|
||||
if string.find(list[i][1],filter) then filterlist[#filterlist+1] = i end
|
||||
end
|
||||
idx=1;self.show_form(sender,get_form())
|
||||
|
||||
elseif fields.prev then
|
||||
idx = idx - n; if idx<1 then idx =#filterlist-n end
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.next then
|
||||
idx = idx+n; if idx > #filterlist then idx = 1 end
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.back then
|
||||
self.show_form(sender,get_form())
|
||||
elseif fields.recipeid then
|
||||
recipeid = tonumber(fields.recipeid) or 1;
|
||||
self.show_form(sender,get_recipe())
|
||||
elseif fields.item then
|
||||
item = fields.item;
|
||||
local recipes = _G.minetest.get_all_craft_recipes(item);
|
||||
local count = 0; if recipes then count = #recipes end
|
||||
if count>0 then
|
||||
recipeid = 1
|
||||
self.show_form(sender,get_recipe() or "")
|
||||
end
|
||||
elseif fields.quit then
|
||||
self.remove()
|
||||
end
|
||||
end
|
|
@ -0,0 +1,57 @@
|
|||
-- gui demo by rnd
|
||||
-- 2 player cooperative editing of image with 2 colors
|
||||
|
||||
if not init then
|
||||
_G.basic_robot.data[self.name()].obj:get_luaentity().timestep = 0.25
|
||||
init = true
|
||||
name = "rnd"
|
||||
otherrobotname = "rnd2" -- on other robot do name of this robot
|
||||
drawcolor = 3; -- other robot has reversed colors
|
||||
otherdrawcolor = 4
|
||||
|
||||
color = {"black","white","blue","green"}
|
||||
data = {};
|
||||
n = 20;
|
||||
|
||||
for i = 1,n do
|
||||
data[i]={};
|
||||
--local y = math.floor(f(i));
|
||||
for j = 1,n do
|
||||
data[i][j] = 1--(n-j>y) and 2 or 1
|
||||
end
|
||||
end
|
||||
|
||||
get_form = function()
|
||||
local form = "size[10,10] "; ret = {};
|
||||
|
||||
for i = 1,n do
|
||||
for j = 1,n do
|
||||
ret[#ret+1] = "image_button["..((i-1)*0.5)..","..((j-1)*0.5)..";0.7,0.63;wool_"..color[data[i][j]]..".png;"..((i-1)*n+j-1) .. ";] "
|
||||
end
|
||||
end
|
||||
return form .. table.concat(ret,"")
|
||||
end
|
||||
|
||||
self.show_form(name,get_form())
|
||||
self.read_form()
|
||||
end
|
||||
|
||||
sender,mail = self.read_mail()
|
||||
if mail then
|
||||
local x = mail[1]; local y = mail[2];
|
||||
if data[x][y]==1 then data[x][y] = otherdrawcolor else data[x][y] = 1 end
|
||||
self.show_form(name,get_form())
|
||||
end
|
||||
|
||||
sender,fields = self.read_form()
|
||||
if fields then
|
||||
if fields.quit then self.remove() end
|
||||
local sel = 0;
|
||||
for k,v in pairs(fields) do
|
||||
if k ~="quit" then sel = tonumber(k); break end
|
||||
end
|
||||
local x = 1+math.floor(sel/n); local y = 1+sel % n;
|
||||
if data[x][y]==1 then data[x][y] = drawcolor else data[x][y] = 1 end
|
||||
self.send_mail(otherrobotname,{x,y})
|
||||
self.show_form(name,get_form())
|
||||
end
|