change into modpack

clear_recipe added for moreblocks ( warning: crash on 0.4.16 )
master
rnd 2018-10-12 16:36:24 +02:00
parent 76807f0ed1
commit 8b37b7add9
1205 changed files with 433783 additions and 2 deletions

Binary file not shown.

1
alchemy/depends.txt Normal file
View File

@ -0,0 +1 @@
default

372
alchemy/init.lua Normal file
View File

@ -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,
})

54
alchemy/items.lua Normal file
View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 335 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 174 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 169 B

1
antigrief Submodule

@ -0,0 +1 @@
Subproject commit e6e7defc9f5d9daa636f4ecfd24e9dd2ad8ba5fb

24
basic_machines/README Normal file
View File

@ -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/>.
----------------------------------------------------------------------

View File

@ -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" }
-- },
-- })

670
basic_machines/ball.lua Normal file
View File

@ -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"}
-- }
-- })

View File

@ -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"},
}
})

View File

@ -0,0 +1,5 @@
default
protector?
areas?
boneworld?
moreores?

388
basic_machines/enviro.lua Normal file
View File

@ -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"}
-- }
-- })

364
basic_machines/grinder.lua Normal file
View File

@ -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"}
}
})

91
basic_machines/init.lua Normal file
View File

@ -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.")

154
basic_machines/mark.lua Normal file
View File

@ -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,
})

View File

@ -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);

View File

@ -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");

Binary file not shown.

2533
basic_machines/mover.lua Normal file

File diff suppressed because it is too large Load Diff

24
basic_machines/plans.txt Normal file
View File

@ -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)

View File

@ -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
)

255
basic_machines/recycler.lua Normal file
View File

@ -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"},
-- }
-- })

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 393 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 398 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 242 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 274 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 240 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 348 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 292 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 119 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 219 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 239 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 258 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 253 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 238 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 302 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 325 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 315 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 218 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 214 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 123 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 134 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 316 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 133 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 234 B

View File

@ -0,0 +1 @@
default

681
basic_protect/init.lua Normal file
View File

@ -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
-- }
-- )

1
basic_protect/swap.dat Normal file

File diff suppressed because one or more lines are too long

Binary file not shown.

After

Width:  |  Height:  |  Size: 96 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 743 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.2 KiB

29
basic_robot/README.txt Normal file
View File

@ -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,

1497
basic_robot/commands.lua Normal file

File diff suppressed because it is too large Load Diff

1
basic_robot/depends.txt Normal file
View File

@ -0,0 +1 @@
default

1895
basic_robot/init.lua Normal file

File diff suppressed because it is too large Load Diff

View File

@ -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})

350
basic_robot/robogui.lua Normal file
View File

@ -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,
};

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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::

View File

@ -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

View File

@ -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::

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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()

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

Some files were not shown because too many files have changed in this diff Show More