├── README ├── autocrafter.lua ├── ball.lua ├── constructor.lua ├── depends.txt ├── enviro.lua ├── grinder.lua ├── init.lua ├── mark.lua ├── mesecon_adapter.lua ├── mesecon_doors.lua ├── mesecon_lights.lua ├── mod.conf ├── mover.lua ├── plans.txt ├── protect.lua ├── recycler.lua ├── sounds ├── chest_inventory_move.ogg ├── electric_zap.ogg ├── grinder.ogg ├── recycler.ogg ├── tng_transporter1.ogg └── transporter.ogg ├── technic_power.lua └── textures ├── basic_machine_battery.png ├── basic_machine_battery_0.png ├── basic_machine_battery_1.png ├── basic_machine_battery_2.png ├── basic_machine_clock_generator.png ├── basic_machine_generator.png ├── basic_machine_mover_side.png ├── basic_machine_outlet.png ├── basic_machine_side.png ├── basic_machines_ball.png ├── basic_machines_dust.png ├── basic_machines_stars.png ├── charcoal.png ├── compass_top.png ├── constructor.png ├── detector.png ├── distributor.png ├── enviro.png ├── grinder.png ├── keypad.png ├── light.png ├── light_off.png ├── machines_pos1.png ├── machines_pos11.png ├── machines_pos2.png ├── ore_extractor.png ├── pipeworks_autocrafter.png ├── power_block.png ├── power_cell.png ├── power_rod.png └── recycler.png /README: -------------------------------------------------------------------------------- 1 | BASIC_MACHINES: lightweight automation mod for minetest 2 | minetest 0.4.14+ 3 | (c) 2015-2016 rnd 4 | textures by rnd, new textures by SaKeL (2016) and Jozet (2017) 5 | 6 | 7 | MANUAL: 8 | 1.WIKI PAGES: https://github.com/ac-minetest/basic_machines/wiki 9 | 2.ingame help: right click mover/detector/keypad.. and click help button 10 | 11 | --------------------------------------------------------------------- 12 | This program is free software: you can redistribute it and/or modify 13 | it under the terms of the GNU General Public License as published by 14 | the Free Software Foundation, either version 3 of the License, or 15 | (at your option) any later version. 16 | 17 | This program is distributed in the hope that it will be useful, 18 | but WITHOUT ANY WARRANTY; without even the implied warranty of 19 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 20 | GNU General Public License for more details. 21 | 22 | You should have received a copy of the GNU General Public License 23 | along with this program. If not, see . 24 | ---------------------------------------------------------------------- -------------------------------------------------------------------------------- /autocrafter.lua: -------------------------------------------------------------------------------- 1 | -- modified and adapted from pipeworks mod by VanessaE 2 | -- by rnd 3 | -- disabled timers and on/off button, now autocrafter is only activated by signal 4 | 5 | local autocrafterCache = {} -- caches some recipe data to avoid to call the slow function minetest.get_craft_result() every second 6 | 7 | local craft_time = 1 8 | 9 | local function count_index(invlist) 10 | local index = {} 11 | for _, stack in pairs(invlist) do 12 | if not stack:is_empty() then 13 | local stack_name = stack:get_name() 14 | index[stack_name] = (index[stack_name] or 0) + stack:get_count() 15 | end 16 | end 17 | return index 18 | end 19 | 20 | local function get_item_info(stack) 21 | local name = stack:get_name() 22 | local def = minetest.registered_items[name] 23 | local description = def and def.description or "Unknown item" 24 | return description, name 25 | end 26 | 27 | local function get_craft(pos, inventory, hash) 28 | local hash = hash or minetest.hash_node_position(pos) 29 | local craft = autocrafterCache[hash] 30 | if not craft then 31 | local recipe = inventory:get_list("recipe") 32 | local output, decremented_input = minetest.get_craft_result({method = "normal", width = 3, items = recipe}) 33 | craft = {recipe = recipe, consumption=count_index(recipe), output = output, decremented_input = decremented_input} 34 | autocrafterCache[hash] = craft 35 | end 36 | return craft 37 | end 38 | 39 | local function autocraft(inventory, craft) 40 | if not craft then return false end 41 | local output_item = craft.output.item 42 | 43 | -- check if we have enough room in dst 44 | if not inventory:room_for_item("dst", output_item) then return false end 45 | local consumption = craft.consumption 46 | local inv_index = count_index(inventory:get_list("src")) 47 | -- check if we have enough material available 48 | for itemname, number in pairs(consumption) do 49 | if (not inv_index[itemname]) or inv_index[itemname] < number then return false end 50 | end 51 | -- consume material 52 | for itemname, number in pairs(consumption) do 53 | for i = 1, number do -- We have to do that since remove_item does not work if count > stack_max 54 | inventory:remove_item("src", ItemStack(itemname)) 55 | end 56 | end 57 | 58 | -- craft the result into the dst inventory and add any "replacements" as well 59 | inventory:add_item("dst", output_item) 60 | for i = 1, 9 do 61 | inventory:add_item("dst", craft.decremented_input.items[i]) 62 | end 63 | return true 64 | end 65 | 66 | -- returns false to stop the timer, true to continue running 67 | -- is started only from start_autocrafter(pos) after sanity checks and cached recipe 68 | local function run_autocrafter(pos, elapsed) 69 | local meta = minetest.get_meta(pos) 70 | local inventory = meta:get_inventory() 71 | local craft = get_craft(pos, inventory) 72 | local output_item = craft.output.item 73 | -- only use crafts that have an actual result 74 | if output_item:is_empty() then 75 | meta:set_string("infotext", "unconfigured Autocrafter: unknown recipe") 76 | return false 77 | end 78 | 79 | for step = 1, math.floor(elapsed/craft_time) do 80 | local continue = autocraft(inventory, craft) 81 | if not continue then return false end 82 | end 83 | return true 84 | end 85 | 86 | local function start_crafter(pos) -- rnd we dont need timer anymore 87 | -- local meta = minetest.get_meta(pos) 88 | -- if meta:get_int("enabled") == 1 then 89 | -- local timer = minetest.get_node_timer(pos) 90 | -- if not timer:is_started() then 91 | -- timer:start(craft_time) 92 | -- end 93 | -- end 94 | end 95 | 96 | local function after_inventory_change(pos) 97 | start_crafter(pos) 98 | end 99 | 100 | -- note, that this function assumes allready being updated to virtual items 101 | -- and doesn't handle recipes with stacksizes > 1 102 | local function after_recipe_change(pos, inventory) 103 | local meta = minetest.get_meta(pos) 104 | -- if we emptied the grid, there's no point in keeping it running or cached 105 | if inventory:is_empty("recipe") then 106 | --minetest.get_node_timer(pos):stop() 107 | autocrafterCache[minetest.hash_node_position(pos)] = nil 108 | meta:set_string("infotext", "unconfigured Autocrafter") 109 | return 110 | end 111 | local recipe_changed = false 112 | local recipe = inventory:get_list("recipe") 113 | 114 | local hash = minetest.hash_node_position(pos) 115 | local craft = autocrafterCache[hash] 116 | 117 | if craft then 118 | -- check if it changed 119 | local cached_recipe = craft.recipe 120 | for i = 1, 9 do 121 | if recipe[i]:get_name() ~= cached_recipe[i]:get_name() then 122 | autocrafterCache[hash] = nil -- invalidate recipe 123 | craft = nil 124 | break 125 | end 126 | end 127 | end 128 | 129 | craft = craft or get_craft(pos, inventory, hash) 130 | local output_item = craft.output.item 131 | local description, name = get_item_info(output_item) 132 | meta:set_string("infotext", string.format("'%s' Autocrafter (%s)", description, name)) 133 | inventory:set_stack("output", 1, output_item) 134 | 135 | after_inventory_change(pos) 136 | end 137 | 138 | -- clean out unknown items and groups, which would be handled like unknown items in the crafting grid 139 | -- if minetest supports query by group one day, this might replace them 140 | -- with a canonical version instead 141 | local function normalize(item_list) 142 | for i = 1, #item_list do 143 | local name = item_list[i] 144 | if not minetest.registered_items[name] then 145 | item_list[i] = "" 146 | end 147 | end 148 | return item_list 149 | end 150 | 151 | local function on_output_change(pos, inventory, stack) 152 | if not stack then 153 | inventory:set_list("output", {}) 154 | inventory:set_list("recipe", {}) 155 | else 156 | local input = minetest.get_craft_recipe(stack:get_name()) 157 | if not input.items or input.type ~= "normal" then return end 158 | local items, width = normalize(input.items), input.width 159 | local item_idx, width_idx = 1, 1 160 | for i = 1, 9 do 161 | if width_idx <= width then 162 | inventory:set_stack("recipe", i, items[item_idx]) 163 | item_idx = item_idx + 1 164 | else 165 | inventory:set_stack("recipe", i, ItemStack("")) 166 | end 167 | width_idx = (width_idx < 3) and (width_idx + 1) or 1 168 | end 169 | -- we'll set the output slot in after_recipe_change to the actual result of the new recipe 170 | end 171 | after_recipe_change(pos, inventory) 172 | end 173 | 174 | -- returns false if we shouldn't bother attempting to start the timer again after this 175 | local function update_meta(meta, enabled) 176 | --local state = enabled and "on" or "off" 177 | --meta:set_int("enabled", enabled and 1 or 0) 178 | meta:set_string("formspec", 179 | "size[8,11]".. 180 | "list[context;recipe;0,0;3,3;]".. 181 | "image[3,1;1,1;gui_hb_bg.png^[colorize:#141318:255]".. 182 | "list[context;output;3,1;1,1;]".. 183 | --"image_button[3,2;1,1;pipeworks_button_" .. state .. ".png;" .. state .. ";;;false;pipeworks_button_interm.png]" .. -- rnd disable button 184 | "list[context;src;0,3.5;8,3;]".. 185 | "list[context;dst;4,0;4,3;]".. 186 | default.gui_bg.. 187 | default.gui_bg_img.. 188 | default.gui_slots.. 189 | default.get_hotbar_bg(0,7).. 190 | "list[current_player;main;0,7;8,4;]".. 191 | "listring[context;dst]".. 192 | "listring[current_player;main]".. 193 | "listring[context;src]".. 194 | "listring[current_player;main]".. 195 | "listring[context;recipe]".. 196 | "listring[current_player;main]" 197 | ) 198 | 199 | -- toggling the button doesn't quite call for running a recipe change check 200 | -- so instead we run a minimal version for infotext setting only 201 | -- this might be more written code, but actually executes less 202 | local output = meta:get_inventory():get_stack("output", 1) 203 | if output:is_empty() then -- doesn't matter if paused or not 204 | 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).") 205 | return false 206 | end 207 | 208 | local description, name = get_item_info(output) 209 | local infotext = enabled and string.format("'%s' Autocrafter (%s)", description, name) 210 | or string.format("paused '%s' Autocrafter", description) 211 | 212 | meta:set_string("infotext", infotext) 213 | return enabled 214 | end 215 | 216 | -- 1st version of the autocrafter had actual items in the crafting grid 217 | -- the 2nd replaced these with virtual items, dropped the content on update and set "virtual_items" to string "1" 218 | -- the third added an output inventory, changed the formspec and added a button for enabling/disabling 219 | -- so we work out way backwards on this history and update each single case to the newest version 220 | local function upgrade_autocrafter(pos, meta) 221 | local meta = meta or minetest.get_meta(pos) 222 | local inv = meta:get_inventory() 223 | 224 | if inv:get_size("output") == 0 then -- we are version 2 or 1 225 | inv:set_size("output", 1) 226 | -- migrate the old autocrafters into an "enabled" state 227 | update_meta(meta, true) 228 | 229 | if meta:get_string("virtual_items") == "1" then -- we are version 2 230 | -- we allready dropped stuff, so lets remove the metadatasetting (we are not being called again for this node) 231 | meta:set_string("virtual_items", "") 232 | else -- we are version 1 233 | local recipe = inv:get_list("recipe") 234 | if not recipe then return end 235 | for idx, stack in ipairs(recipe) do 236 | if not stack:is_empty() then 237 | minetest.item_drop(stack, "", pos) 238 | stack:set_count(1) 239 | stack:set_wear(0) 240 | inv:set_stack("recipe", idx, stack) 241 | end 242 | end 243 | end 244 | 245 | -- update the recipe, cache, and start the crafter 246 | autocrafterCache[minetest.hash_node_position(pos)] = nil 247 | after_recipe_change(pos, inv) 248 | end 249 | end 250 | 251 | minetest.register_node("basic_machines:autocrafter", { 252 | description = "Autocrafter", 253 | drawtype = "normal", 254 | tiles = {"pipeworks_autocrafter.png"}, 255 | groups = {cracky=3}, 256 | on_construct = function(pos) 257 | local meta = minetest.get_meta(pos) 258 | local inv = meta:get_inventory() 259 | inv:set_size("src", 3*8) 260 | inv:set_size("recipe", 3*3) 261 | inv:set_size("dst", 4*3) 262 | inv:set_size("output", 1) 263 | update_meta(meta, false) 264 | end, 265 | on_receive_fields = function(pos, formname, fields, sender) 266 | --if not pipeworks.may_configure(pos, sender) then return end 267 | local meta = minetest.get_meta(pos) 268 | if fields.on then 269 | update_meta(meta, false) 270 | --minetest.get_node_timer(pos):stop() 271 | elseif fields.off then 272 | if update_meta(meta, true) then 273 | start_crafter(pos) 274 | end 275 | end 276 | end, 277 | can_dig = function(pos, player) 278 | upgrade_autocrafter(pos) 279 | local meta = minetest.get_meta(pos) 280 | local inv = meta:get_inventory() 281 | return (inv:is_empty("src") and inv:is_empty("dst")) 282 | end, 283 | after_place_node = function(pos, placer) -- rnd : set owner 284 | local meta = minetest.get_meta(pos); 285 | meta:set_string("owner", placer:get_player_name()); 286 | end, 287 | --after_place_node = pipeworks.scan_for_tube_objects, 288 | --after_dig_node = function(pos) 289 | --pipeworks.scan_for_tube_objects(pos) 290 | --end, 291 | on_destruct = function(pos) 292 | autocrafterCache[minetest.hash_node_position(pos)] = nil 293 | end, 294 | allow_metadata_inventory_put = function(pos, listname, index, stack, player) 295 | --if not pipeworks.may_configure(pos, player) then return 0 end 296 | local meta = minetest.get_meta(pos);if meta:get_string("owner")~=player:get_player_name() then return 0 end -- rnd 297 | 298 | upgrade_autocrafter(pos) 299 | local inv = minetest.get_meta(pos):get_inventory() 300 | if listname == "recipe" then 301 | stack:set_count(1) 302 | inv:set_stack(listname, index, stack) 303 | after_recipe_change(pos, inv) 304 | return 0 305 | elseif listname == "output" then 306 | on_output_change(pos, inv, stack) 307 | return 0 308 | end 309 | after_inventory_change(pos) 310 | return stack:get_count() 311 | end, 312 | allow_metadata_inventory_take = function(pos, listname, index, stack, player) 313 | --if not pipeworks.may_configure(pos, player) then 314 | -- minetest.log("action", string.format("%s attempted to take from autocrafter at %s", player:get_player_name(), minetest.pos_to_string(pos))) 315 | -- return 0 316 | -- end 317 | local meta = minetest.get_meta(pos);if meta:get_string("owner")~=player:get_player_name() then return 0 end -- rnd 318 | 319 | upgrade_autocrafter(pos) 320 | local inv = minetest.get_meta(pos):get_inventory() 321 | if listname == "recipe" then 322 | inv:set_stack(listname, index, ItemStack("")) 323 | after_recipe_change(pos, inv) 324 | return 0 325 | elseif listname == "output" then 326 | on_output_change(pos, inv, nil) 327 | return 0 328 | end 329 | after_inventory_change(pos) 330 | return stack:get_count() 331 | end, 332 | allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) 333 | return 0; -- no internal inventory moves! 334 | end, 335 | 336 | effector = { -- rnd: run machine when activated by signal 337 | action_on = function (pos, node,ttl) 338 | if type(ttl)~="number" then ttl = 1 end 339 | if ttl<0 then return end -- machines_TTL prevents infinite recursion 340 | run_autocrafter(pos, craft_time); 341 | end, 342 | 343 | can_dig = function(pos) 344 | local meta = minetest.get_meta(pos); 345 | local inv = meta:get_inventory(); 346 | 347 | if not (inv:is_empty("src")) or not (inv:is_empty("dst")) then return false end -- all inv must be empty to be dug 348 | 349 | return true 350 | 351 | end 352 | 353 | } 354 | 355 | --on_timer = run_autocrafter -- rnd 356 | }) 357 | 358 | -- minetest.register_craft( { 359 | -- output = "basic_machines:autocrafter", 360 | -- recipe = { 361 | -- { "default:steel_ingot", "default:mese_crystal", "default:steel_ingot" }, 362 | -- { "default:diamondblock", "default:steel_ingot", "default:diamondblock" }, 363 | -- { "default:steel_ingot", "default:mese_crystal", "default:steel_ingot" } 364 | -- }, 365 | -- }) -------------------------------------------------------------------------------- /ball.lua: -------------------------------------------------------------------------------- 1 | -- BALL: energy ball that flies around, can bounce and activate stuff 2 | -- rnd 2016: 3 | 4 | -- 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 5 | 6 | -- SETTINGS 7 | 8 | basic_machines.ball = {}; 9 | basic_machines.ball.maxdamage = 10; -- player health 20 10 | 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 11 | ["default:wood"]=1, 12 | ["xpanes:bar_2"]=1, 13 | ["xpanes:bar_10"]=1, 14 | ["darkage:iron_bars"]=1, 15 | ["default:glass"] = 2, 16 | }; 17 | 18 | -- END OF SETTINGS 19 | 20 | local ballcount = {}; 21 | local function round(x) 22 | if x < 0 then 23 | return -math.floor(-x+0.5); 24 | else 25 | return math.floor(x+0.5); 26 | end 27 | end 28 | 29 | 30 | local ball_spawner_update_form = function (pos) 31 | 32 | local meta = minetest.get_meta(pos); 33 | local x0,y0,z0; 34 | x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); -- direction of velocity 35 | 36 | local energy,bounce,g,puncheable, gravity,hp,hurt,solid; 37 | local speed = meta:get_float("speed"); -- if positive sets initial ball speed 38 | energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing 39 | bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed 40 | gravity = meta:get_float("gravity"); -- gravity 41 | hp = meta:get_float("hp"); 42 | hurt = meta:get_float("hurt"); 43 | puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone 44 | solid = meta:get_int("solid"); -- if 1 then entity is solid - cant be walked on 45 | 46 | local texture = meta:get_string("texture") or "basic_machines_ball.png"; 47 | local visual = meta:get_string("visual") or "sprite"; 48 | local scale = meta:get_int("scale"); 49 | 50 | local form = 51 | "size[4.25,4.75]" .. -- width, height 52 | "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.."]".. 53 | "field[3.25,0.5;1,1;speed;speed;"..speed.."]".. 54 | --speed, jump, gravity,sneak 55 | "field[0.25,1.5;1,1;energy;energy;"..energy.."]".. 56 | "field[1.25,1.5;1,1;bounce;bounce;".. bounce.."]".. 57 | "field[2.25,1.5;1,1;gravity;gravity;"..gravity.."]".. 58 | "field[3.25,1.5;1,1;puncheable;puncheable;"..puncheable.."]".. 59 | "field[3.25,2.5;1,1;solid;solid;"..solid.."]".. 60 | "field[0.25,2.5;1,1;hp;hp;"..hp.."]".."field[1.25,2.5;1,1;hurt;hurt;"..hurt.."]".. 61 | "field[0.25,3.5;4,1;texture;texture;"..minetest.formspec_escape(texture).."]".. 62 | "field[0.25,4.5;1,1;scale;scale;"..scale.."]".."field[1.25,4.5;1,1;visual;visual;"..visual.."]".. 63 | "button_exit[3.25,4.25;1,1;OK;OK]"; 64 | 65 | 66 | 67 | if meta:get_int("admin")==1 then 68 | local lifetime = meta:get_int("lifetime"); 69 | if lifetime <= 0 then lifetime = 20 end 70 | form = form .. "field[2.25,2.5;1,1;lifetime;lifetime;"..lifetime.."]" 71 | end 72 | 73 | meta:set_string("formspec",form); 74 | 75 | end 76 | 77 | 78 | 79 | minetest.register_entity("basic_machines:ball",{ 80 | timer = 0, 81 | lifetime = 20, -- how long it exists before disappearing 82 | energy = 0, -- if negative it will deactivate stuff, positive will activate, 0 wont do anything 83 | puncheable = 1, -- can be punched by players in protection 84 | bounce = 0, -- 0: absorbs in block, 1 = proper bounce=lag buggy, -- to do: 2 = line of sight bounce 85 | gravity = 0, 86 | speed = 5, -- velocity when punched 87 | hurt = 0, -- how much damage it does to target entity, if 0 damage disabled 88 | owner = "", 89 | state = false, 90 | origin = {x=0,y=0,z=0}, 91 | lastpos = {x=0,y=0,z=0}, -- last not-colliding position 92 | hp_max = 100, 93 | elasticity = 0.9, -- speed gets multiplied by this after bounce 94 | visual="sprite", 95 | visual_size={x=.6,y=.6}, 96 | collisionbox = {-0.5,-0.5,-0.5, 0.5,0.5,0.5}, 97 | physical=false, 98 | 99 | --textures={"basic_machines_ball"}, 100 | 101 | on_activate = function(self, staticdata) 102 | self.object:set_properties({textures={"basic_machines_ball.png"}}) 103 | self.object:set_properties({visual_size = {x=1, y=1}}); 104 | self.timer = 0;self.owner = ""; 105 | self.origin = self.object:getpos(); 106 | self.lifetime = 20; 107 | end, 108 | 109 | get_staticdata = function(self) -- this gets called before object put in world and before it hides 110 | if not self.state then return nil end 111 | self.object:remove(); 112 | return nil 113 | end, 114 | 115 | 116 | on_punch = function (self, puncher, time_from_last_punch, tool_capabilities, dir) 117 | if self.puncheable == 0 then return end 118 | if self.puncheable == 1 then -- only those in protection 119 | local name = puncher:get_player_name(); 120 | local pos = self.object:getpos(); 121 | if minetest.is_protected(pos,name) then return end 122 | end 123 | --minetest.chat_send_all(minetest.pos_to_string(dir)) 124 | if time_from_last_punch<0.5 then return end 125 | local v = self.speed or 1; 126 | 127 | local velocity = dir; 128 | velocity.x = velocity.x*v;velocity.y = velocity.y*v;velocity.z = velocity.z*v; 129 | self.object:setvelocity(velocity) 130 | end, 131 | 132 | on_step = function(self, dtime) 133 | self.timer=self.timer+dtime 134 | if self.timer>self.lifetime then 135 | local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count; 136 | self.object:remove() 137 | return 138 | end 139 | 140 | if not self.state then self.state = true end 141 | local pos=self.object:getpos() 142 | 143 | local origin = self.origin; 144 | 145 | local r = 30;-- maximal distance when balls disappear 146 | local dist = math.max(math.abs(pos.x-origin.x), math.abs(pos.y-origin.y), math.abs(pos.z-origin.z)); 147 | if dist>r then -- remove if it goes too far 148 | local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count; 149 | self.object:remove() 150 | return 151 | end 152 | 153 | local nodename = minetest.get_node(pos).name; 154 | local walkable = false; 155 | if nodename ~= "air" then 156 | walkable = minetest.registered_nodes[nodename].walkable; 157 | if nodename == "basic_machines:ball_spawner" and dist>0.5 then walkable = true end -- ball can activate spawner, just not originating one 158 | end 159 | if not walkable then 160 | self.lastpos = pos 161 | if self.hurt~=0 then -- check for coliding nearby objects 162 | local objects = minetest.get_objects_inside_radius(pos,2); 163 | if #objects>1 then 164 | for _, obj in pairs(objects) do 165 | local p = obj:getpos(); 166 | local d = math.sqrt((p.x-pos.x)^2+(p.y-pos.y)^2+(p.z-pos.z)^2); 167 | if d>0 then 168 | 169 | --if minetest.is_protected(p,self.owner) then return end 170 | 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 171 | 172 | if obj:is_player() then --player 173 | if obj:get_player_name()==self.owner then break end -- dont hurt owner 174 | 175 | local hp = obj:get_hp() 176 | local newhp = hp-self.hurt; 177 | if newhp<=0 and boneworld and boneworld.killxp then 178 | local killxp = boneworld.killxp[self.owner]; 179 | if killxp then 180 | boneworld.killxp[self.owner] = killxp + 0.01; 181 | end 182 | end 183 | obj:set_hp(newhp) 184 | else -- non player 185 | local lua_entity = obj:get_luaentity(); 186 | if lua_entity and lua_entity.itemstring then 187 | local entname = lua_entity.itemstring; 188 | if entname == "robot" then 189 | self.object:remove() 190 | return; 191 | end 192 | end 193 | local hp = obj:get_hp() 194 | local newhp = hp-self.hurt; 195 | minetest.chat_send_player(self.owner,"#ball: target hp " .. newhp) 196 | if newhp<=0 then obj:remove() else obj:set_hp(newhp) end 197 | end 198 | 199 | 200 | 201 | 202 | local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count; 203 | self.object:remove(); 204 | return 205 | end 206 | end 207 | end 208 | end 209 | end 210 | 211 | 212 | if walkable then -- we hit a node 213 | --minetest.chat_send_all(" hit node at " .. minetest.pos_to_string(pos)) 214 | 215 | 216 | local node = minetest.get_node(pos); 217 | local table = minetest.registered_nodes[node.name]; 218 | if table and table.effector then -- activate target 219 | 220 | local energy = self.energy; 221 | if energy~=0 then 222 | if minetest.is_protected(pos,self.owner) then return end 223 | end 224 | local effector = table.effector; 225 | 226 | local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count; 227 | self.object:remove(); 228 | 229 | if energy>0 then 230 | if not effector.action_on then return end 231 | effector.action_on(pos,node,16); 232 | elseif energy<0 then 233 | if not effector.action_off then return end 234 | effector.action_off(pos,node,16); 235 | end 236 | 237 | 238 | else -- bounce ( copyright rnd, 2016 ) 239 | local bounce = self.bounce; 240 | if self.bounce == 0 then 241 | local count = ballcount[self.owner] or 1; count=count-1; ballcount[self.owner] = count; 242 | self.object:remove() 243 | return end 244 | 245 | local n = {x=0,y=0,z=0}; -- this will be bounce normal 246 | local v = self.object:getvelocity(); 247 | local opos = {x=round(pos.x),y=round(pos.y), z=round(pos.z)}; -- obstacle 248 | local bpos ={ x=(pos.x-opos.x),y=(pos.y-opos.y),z=(pos.z-opos.z)}; -- boundary position on cube, approximate 249 | 250 | if bounce == 2 then -- uses special blocks for non buggy lag proof bouncing: by default it bounces in y direction 251 | local bounce_direction = basic_machines.ball.bounce_materials[node.name] or 0; 252 | 253 | if bounce_direction == 0 then 254 | if v.y>=0 then n.y = -1 else n.y = 1 end 255 | elseif bounce_direction == 1 then 256 | if v.x>=0 then n.x = -1 else n.x = 1 end 257 | n.y = 0; 258 | elseif bounce_direction == 2 then 259 | if v.z>=0 then n.z = -1 else n.z = 1 end 260 | n.y = 0; 261 | end 262 | 263 | else -- algorithm to determine bounce direction - problem: with lag its impossible to determine reliable which node was hit and which face .. 264 | 265 | if v.x<=0 then n.x = 1 else n.x = -1 end -- possible bounce directions 266 | if v.y<=0 then n.y = 1 else n.y = -1 end 267 | if v.z<=0 then n.z = 1 else n.z = -1 end 268 | 269 | local dpos = {}; 270 | 271 | dpos.x = 0.5*n.x; dpos.y = 0; dpos.z = 0; -- calculate distance to bounding surface midpoints 272 | 273 | local d1 = (bpos.x-dpos.x)^2 + (bpos.y)^2 + (bpos.z)^2; 274 | dpos.x = 0; dpos.y = 0.5*n.y; dpos.z = 0; 275 | local d2 = (bpos.x)^2 + (bpos.y-dpos.y)^2 + (bpos.z)^2; 276 | dpos.x = 0; dpos.y = 0; dpos.z = 0.5*n.z; 277 | local d3 = (bpos.x)^2 + (bpos.y)^2 + (bpos.z-dpos.z)^2; 278 | local d = math.min(d1,d2,d3); -- we obtain bounce direction from minimal distance 279 | 280 | if d1==d then --x 281 | n.y=0;n.z=0 282 | elseif d2==d then --y 283 | n.x=0;n.z=0 284 | elseif d3==d then --z 285 | n.x=0;n.y=0 286 | end 287 | 288 | 289 | nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal 290 | walkable = nodename ~= "air"; 291 | if walkable then -- problem, nonempty node - incorrect normal, fix it 292 | if n.x ~=0 then -- x direction is wrong, try something else 293 | n.x=0; 294 | if v.y>=0 then n.y = -1 else n.y = 1 end -- try y 295 | nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal 296 | walkable = nodename ~= "air"; 297 | if walkable then -- still problem, only remaining is z 298 | n.y=0; 299 | if v.z>=0 then n.z = -1 else n.z = 1 end 300 | nodename=minetest.get_node({x=opos.x+n.x,y=opos.y+n.y,z=opos.z+n.z}).name -- verify normal 301 | walkable = nodename ~= "air"; 302 | if walkable then -- messed up, just remove the ball 303 | self.object:remove() 304 | return 305 | end 306 | 307 | end 308 | 309 | end 310 | end 311 | 312 | end 313 | 314 | local elasticity = self.elasticity; 315 | 316 | -- bounce 317 | if n.x~=0 then 318 | v.x=-elasticity*v.x 319 | elseif n.y~=0 then 320 | v.y=-elasticity*v.y 321 | elseif n.z~=0 then 322 | v.z=-elasticity*v.z 323 | end 324 | 325 | local r = 0.2 326 | 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 327 | self.object:setpos(bpos) -- place object at last known outside point 328 | 329 | self.object:setvelocity(v); 330 | 331 | minetest.sound_play("default_dig_cracky", {pos=pos,gain=1.0,max_hear_distance = 8,}) 332 | 333 | end 334 | end 335 | return 336 | end, 337 | }) 338 | 339 | 340 | minetest.register_node("basic_machines:ball_spawner", { 341 | description = "Spawns energy ball one block above", 342 | tiles = {"basic_machines_ball.png"}, 343 | groups = {cracky=3}, 344 | drawtype = "allfaces", 345 | paramtype = "light", 346 | param1=1, 347 | walkable = false, 348 | sounds = default.node_sound_wood_defaults(), 349 | after_place_node = function(pos, placer) 350 | local meta = minetest.env:get_meta(pos) 351 | meta:set_string("owner", placer:get_player_name()); 352 | local privs = minetest.get_player_privs(placer:get_player_name()); if privs.privs then meta:set_int("admin",1) end 353 | 354 | if privs.machines then meta:set_int("machines",1) end 355 | 356 | meta:set_float("hurt",0); 357 | meta:set_string("texture", "basic_machines_ball.png"); 358 | meta:set_float("hp",100); 359 | meta:set_float("speed",5); -- if positive sets initial ball speed 360 | meta:set_float("energy",1); -- if positive activates, negative deactivates, 0 does nothing 361 | meta:set_int("bounce",0); -- if nonzero bounces when hit obstacle, 0 gets absorbed 362 | meta:set_float("gravity",0); -- gravity 363 | 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 364 | meta:set_int("scale",100); 365 | meta:set_string("visual","sprite"); -- sprite/cube OR particle 366 | ball_spawner_update_form(pos); 367 | 368 | end, 369 | 370 | effector = { 371 | action_on = function (pos, node,ttl) 372 | if type(ttl)~="number" then ttl = 1 end 373 | if ttl<0 then return end 374 | 375 | local meta = minetest.get_meta(pos); 376 | local t0 = meta:get_int("t"); 377 | local t1 = minetest.get_gametime(); 378 | local T = meta:get_int("T"); -- temperature 379 | 380 | if t0>t1-2 then -- activated before natural time 381 | T=T+1; 382 | else 383 | if T>0 then 384 | T=T-1 385 | if t1-t0>5 then T = 0 end 386 | end 387 | end 388 | meta:set_int("T",T); 389 | meta:set_int("t",t1); -- update last activation time 390 | 391 | if T > 2 then -- overheat 392 | minetest.sound_play("default_cool_lava",{pos = pos, max_hear_distance = 16, gain = 0.25}) 393 | meta:set_string("infotext","overheat: temperature ".. T) 394 | return 395 | end 396 | 397 | if meta:get_int("machines")~=1 then -- no machines priv, limit ball count 398 | local owner = meta:get_string("owner"); 399 | local count = ballcount[owner]; 400 | if not count or count<0 then count = 0 end 401 | 402 | if count>=2 then 403 | if t1-t0>10 then count = 0 404 | else return 405 | end 406 | end 407 | 408 | count = count + 1; 409 | ballcount[owner]=count; 410 | --minetest.chat_send_all("count " .. count); 411 | end 412 | 413 | pos.x = round(pos.x);pos.y = round(pos.y);pos.z = round(pos.z); 414 | local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball"); 415 | local luaent = obj:get_luaentity(); 416 | local meta = minetest.get_meta(pos); 417 | 418 | local speed,energy,bounce,gravity,puncheable,solid; 419 | speed = meta:get_float("speed"); 420 | energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing 421 | bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed 422 | gravity = meta:get_float("gravity"); -- gravity 423 | puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone 424 | solid = meta:get_int("solid"); 425 | 426 | if energy<0 then 427 | obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}}) 428 | end 429 | 430 | luaent.bounce = bounce; 431 | luaent.energy = energy; 432 | if gravity>0 then 433 | obj:setacceleration({x=0,y=-gravity,z=0}); 434 | end 435 | luaent.puncheable = puncheable; 436 | luaent.owner = meta:get_string("owner"); 437 | luaent.hurt = meta:get_float("hurt"); 438 | if solid==1 then 439 | luaent.physical = true 440 | end 441 | 442 | obj:set_hp( meta:get_float("hp") ); 443 | 444 | local x0,y0,z0; 445 | if speed>0 then luaent.speed = speed end 446 | 447 | x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); -- direction of velocity 448 | if speed~=0 and (x0~=0 or y0~=0 or z0~=0) then -- set velocity direction 449 | local velocity = {x=x0,y=y0,z=z0}; 450 | local v = math.sqrt(velocity.x^2+velocity.y^2+velocity.z^2); if v == 0 then v = 1 end 451 | v = v / speed; 452 | velocity.x=velocity.x/v;velocity.y=velocity.y/v;velocity.z=velocity.z/v; 453 | obj:setvelocity(velocity); 454 | end 455 | 456 | if meta:get_int("admin")==1 then 457 | luaent.lifetime = meta:get_float("lifetime"); 458 | end 459 | 460 | 461 | local visual = meta:get_string("visual") 462 | obj:set_properties({visual=visual}); 463 | local texture = meta:get_string("texture"); 464 | if visual=="sprite" then 465 | obj:set_properties({textures={texture}}) 466 | elseif visual == "cube" then 467 | obj:set_properties({textures={texture,texture,texture,texture,texture,texture}}) 468 | end 469 | local scale = meta:get_int("scale");if scale<=0 then scale = 1 else scale = scale/100 end 470 | obj:set_properties({visual_size = {x=scale, y=scale}}); 471 | 472 | 473 | 474 | end, 475 | 476 | action_off = function (pos, node,ttl) 477 | if type(ttl)~="number" then ttl = 1 end 478 | if ttl<0 then return end 479 | pos.x = round(pos.x);pos.y = round(pos.y);pos.z = round(pos.z); 480 | local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball"); 481 | local luaent = obj:get_luaentity(); 482 | luaent.energy = -1; 483 | obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}}) 484 | end 485 | }, 486 | 487 | on_receive_fields = function(pos, formname, fields, sender) 488 | 489 | local name = sender:get_player_name();if minetest.is_protected(pos,name) then return end 490 | 491 | if fields.OK then 492 | local privs = minetest.get_player_privs(sender:get_player_name()); 493 | local meta = minetest.get_meta(pos); 494 | local x0=0; local y0=0; local z0=0; 495 | --minetest.chat_send_all("form at " .. dump(pos) .. " fields " .. dump(fields)) 496 | if fields.x0 then x0 = tonumber(fields.x0) or 0 end 497 | if fields.y0 then y0 = tonumber(fields.y0) or 0 end 498 | if fields.z0 then z0 = tonumber(fields.z0) or 0 end 499 | if not privs.privs and (math.abs(x0)>10 or math.abs(y0)>10 or math.abs(z0) > 10) then return end 500 | 501 | meta:set_int("x0",x0);meta:set_int("y0",y0);meta:set_int("z0",z0); 502 | 503 | local speed,energy,bounce,gravity,puncheable,solid; 504 | energy = meta:get_float("energy"); -- if positive activates, negative deactivates, 0 does nothing 505 | bounce = meta:get_int("bounce"); -- if nonzero bounces when hit obstacle, 0 gets absorbed 506 | gravity = meta:get_float("gravity"); -- gravity 507 | puncheable = meta:get_int("puncheable"); -- if 1 can be punched by players in protection, if 2 can be punched by anyone 508 | solid = meta:get_int("solid"); 509 | 510 | 511 | if fields.speed then 512 | local speed = tonumber(fields.speed) or 0; 513 | if (speed > 10 or speed < 0) and not privs.privs then return end 514 | meta:set_float("speed", speed) 515 | end 516 | 517 | if fields.energy then 518 | local energy = tonumber(fields.energy) or 1; 519 | meta:set_float("energy", energy) 520 | end 521 | 522 | if fields.bounce then 523 | local bounce = tonumber(fields.bounce) or 1; 524 | meta:set_int("bounce",bounce) 525 | end 526 | 527 | if fields.gravity then 528 | local gravity = tonumber(fields.gravity) or 1; 529 | if (gravity<0 or gravity>30) and not privs.privs then return end 530 | meta:set_float("gravity", gravity) 531 | end 532 | if fields.puncheable then 533 | meta:set_int("puncheable", tonumber(fields.puncheable) or 0) 534 | end 535 | 536 | if fields.solid then 537 | meta:set_int("solid", tonumber(fields.solid) or 0) 538 | end 539 | 540 | if fields.lifetime then 541 | meta:set_int("lifetime", tonumber(fields.lifetime) or 0) 542 | end 543 | 544 | if fields.hurt then 545 | meta:set_float("hurt", tonumber(fields.hurt) or 0) 546 | end 547 | 548 | if fields.hp then 549 | meta:set_float("hp", math.abs(tonumber(fields.hp) or 0)) 550 | end 551 | 552 | if fields.texture then 553 | meta:set_string ("texture", fields.texture); 554 | end 555 | 556 | if fields.scale then 557 | local scale = math.abs(tonumber(fields.scale) or 100); 558 | if scale>1000 and not privs.privs then scale = 1000 end 559 | meta:set_int("scale", scale) 560 | end 561 | 562 | if fields.visual then 563 | local visual = fields.visual or ""; 564 | if visual~="sprite" and visual~="cube" then return end 565 | meta:set_string ("visual", fields.visual); 566 | end 567 | 568 | ball_spawner_update_form(pos); 569 | end 570 | end, 571 | 572 | after_dig_node = function(pos, oldnode, oldmetadata, digger) 573 | local name = digger:get_player_name(); 574 | local inv = digger:get_inventory(); 575 | inv:remove_item("main", ItemStack("basic_machines:ball_spawner")); 576 | local stack = ItemStack("basic_machines:ball_spell"); 577 | local meta = oldmetadata["fields"]; 578 | meta["formspec"]=nil; 579 | stack:set_metadata(minetest.serialize(meta)); 580 | inv:add_item("main",stack); 581 | end 582 | 583 | }) 584 | 585 | 586 | local spelltime = {}; 587 | 588 | -- ball as magic spell user can cast 589 | minetest.register_tool("basic_machines:ball_spell", { 590 | description = "ball spawner", 591 | inventory_image = "basic_machines_ball.png", 592 | tool_capabilities = { 593 | full_punch_interval = 2, 594 | max_drop_level=0, 595 | }, 596 | on_use = function(itemstack, user, pointed_thing) 597 | 598 | local pos = user:getpos();pos.y=pos.y+1; 599 | local meta = minetest.deserialize(itemstack:get_metadata()); 600 | if not meta then return end 601 | local owner = meta["owner"] or ""; 602 | 603 | --if minetest.is_protected(pos,owner) then return end 604 | 605 | local t0 = spelltime[owner] or 0; 606 | local t1 = minetest.get_gametime(); 607 | if t1-t0<2 then return end -- too soon 608 | spelltime[owner]=t1; 609 | 610 | 611 | local obj = minetest.add_entity({x=pos.x,y=pos.y,z=pos.z}, "basic_machines:ball"); 612 | local luaent = obj:get_luaentity(); 613 | 614 | 615 | local speed,energy,bounce,gravity,puncheable; 616 | speed = tonumber(meta["speed"]) or 0; 617 | energy = tonumber(meta["energy"]) or 0; -- if positive activates, negative deactivates, 0 does nothing 618 | bounce = tonumber(meta["bounce"]) or 0; -- if nonzero bounces when hit obstacle, 0 gets absorbed 619 | gravity = tonumber(meta["gravity"]) or 0; -- gravity 620 | puncheable = tonumber(meta["puncheable"]) or 0; -- if 1 can be punched by players in protection, if 2 can be punched by anyone 621 | 622 | if energy<0 then 623 | obj:set_properties({textures={"basic_machines_ball.png^[colorize:blue:120"}}) 624 | end 625 | 626 | luaent.bounce = bounce; 627 | luaent.energy = energy; 628 | if gravity>0 then 629 | obj:setacceleration({x=0,y=-gravity,z=0}); 630 | end 631 | luaent.puncheable = puncheable; 632 | luaent.owner = meta["owner"]; 633 | luaent.hurt = math.min(tonumber(meta["hurt"]),basic_machines.ball.maxdamage); 634 | 635 | obj:set_hp( tonumber(meta["hp"]) ); 636 | 637 | local x0,y0,z0; 638 | if speed>0 then luaent.speed = speed end 639 | 640 | 641 | 642 | local v = user:get_look_dir(); 643 | v.x=v.x*speed;v.y=v.y*speed;v.z=v.z*speed; 644 | obj:setvelocity(v); 645 | 646 | 647 | if tonumber(meta["admin"])==1 then 648 | luaent.lifetime = tonumber(meta["lifetime"]); 649 | end 650 | 651 | 652 | obj:set_properties({textures={meta["texture"]}}) 653 | 654 | 655 | end, 656 | 657 | 658 | }) 659 | 660 | 661 | 662 | -- minetest.register_craft({ 663 | -- output = "basic_machines:ball_spawner", 664 | -- recipe = { 665 | -- {"basic_machines:power_cell"}, 666 | -- {"basic_machines:keypad"} 667 | -- } 668 | -- }) -------------------------------------------------------------------------------- /constructor.lua: -------------------------------------------------------------------------------- 1 | -- rnd 2016: 2 | 3 | -- CONSTRUCTOR machine: used to make all other basic_machines 4 | 5 | basic_machines.craft_recipes = { 6 | ["keypad"] = {item = "basic_machines:keypad", description = "Turns on/off lights and activates machines or opens doors", craft = {"default:wood","default:stick"}, tex = "keypad"}, 7 | ["light"]={item = "basic_machines:light_on", description = "Light in darkness", craft = {"default:torch 4"}, tex = "light"}, 8 | ["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"}, 9 | 10 | ["detector"] = {item = "basic_machines:detector", description = "Detect and measure players, objects,blocks,light level", craft = {"default:mese_crystal 4","basic_machines:keypad"}, tex = "detector"}, 11 | 12 | ["distributor"]= {item = "basic_machines:distributor", description = "Organize your circuits better", craft = {"default:steel_ingot","default:mese_crystal", "basic_machines:keypad"}, tex = "distributor"}, 13 | 14 | ["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"}, 15 | 16 | ["recycler"]= {item = "basic_machines:recycler", description = "Recycle old tools", craft = {"default:mese_crystal 8","default:diamondblock"}, tex = "recycler"}, 17 | 18 | ["enviroment"] = {item = "basic_machines:enviro", description = "Change gravity and more", craft = {"basic_machines:generator 8","basic_machines:clockgen"}, tex = "enviro"}, 19 | 20 | ["ball_spawner"]={item = "basic_machines:ball_spawner", description = "Spawn moving energy balls", craft = {"basic_machines:power_cell","basic_machines:keypad"}, tex = "basic_machines_ball"}, 21 | 22 | ["battery"]={item = "basic_machines:battery_0", description = "Power for machines", craft = {"default:bronzeblock 2","default:mese","default:diamond"}, tex = "basic_machine_battery"}, 23 | 24 | ["generator"]={item = "basic_machines:generator", description = "Generate power crystals", craft = {"default:diamondblock 5","basic_machines:battery 5","default:goldblock 5"}, tex = "basic_machine_generator"}, 25 | 26 | ["autocrafter"] = {item = "basic_machines:autocrafter", description = "Automate crafting", craft = { "default:steel_ingot 5", "default:mese_crystal 2", "default:diamondblock 2"}, tex = "pipeworks_autocrafter"}, 27 | 28 | ["grinder"] = {item = "basic_machines:grinder", description = "Makes dusts and grinds materials", craft = {"default:diamond 13","default:mese 4"}, tex = "grinder"}, 29 | 30 | ["power_block"] = {item = "basic_machines:power_block 5", description = "Energy cell, contains 11 energy units", craft = {"basic_machines:power_rod"}, tex = "power_block"}, 31 | 32 | ["power_cell"] = {item = "basic_machines:power_cell 5", description = "Energy cell, contains 1 energy unit", craft = {"basic_machines:power_block"}, tex = "power_cell"}, 33 | 34 | ["coal_lump"] = {item = "default:coal_lump", description = "Coal lump, contains 1 energy unit", craft = {"basic_machines:power_cell 2"}, tex = "default_coal_lump"}, 35 | 36 | } 37 | 38 | 39 | basic_machines.craft_recipe_order = { -- order in which nodes appear 40 | "keypad","light","grinder","mover", "battery","generator","detector", "distributor", "clock_generator","recycler","autocrafter","ball_spawner", "enviroment", "power_block", "power_cell", "coal_lump", 41 | } 42 | 43 | 44 | local constructor_process = function(pos) 45 | 46 | local meta = minetest.get_meta(pos); 47 | local craft = basic_machines.craft_recipes[meta:get_string("craft")]; 48 | if not craft then return end 49 | local item = craft.item; 50 | local craftlist = craft.craft; 51 | 52 | local inv = meta:get_inventory(); 53 | for _,v in pairs(craftlist) do 54 | if not inv:contains_item("main", ItemStack(v)) then 55 | meta:set_string("infotext", "#CRAFTING: you need " .. v .. " to craft " .. craft.item) 56 | return 57 | end 58 | end 59 | 60 | for _,v in pairs(craftlist) do 61 | inv:remove_item("main", ItemStack(v)); 62 | end 63 | inv:add_item("main", ItemStack(item)); 64 | 65 | end 66 | 67 | local constructor_update_meta = function(pos) 68 | local meta = minetest.get_meta(pos); 69 | local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z 70 | local craft = meta:get_string("craft"); 71 | 72 | local description = basic_machines.craft_recipes[craft]; 73 | local tex; 74 | 75 | if description then 76 | tex = description.tex; 77 | local i = 0; 78 | local itex; 79 | 80 | local inv = meta:get_inventory(); -- set up craft list 81 | for _,v in pairs(description.craft) do 82 | i=i+1; 83 | inv:set_stack("recipe", i, ItemStack(v)) 84 | end 85 | 86 | for j = i+1,6 do 87 | inv:set_stack("recipe", j, ItemStack("")) 88 | end 89 | 90 | description = description.description 91 | 92 | else 93 | description = "" 94 | tex = "" 95 | end 96 | 97 | 98 | local textlist = " "; 99 | 100 | local selected = meta:get_int("selected") or 1; 101 | for _,v in ipairs(basic_machines.craft_recipe_order) do 102 | textlist = textlist .. v .. ", "; 103 | 104 | end 105 | 106 | local form = 107 | "size[8,10]".. 108 | "textlist[0,0;3,1.5;craft;" .. textlist .. ";" .. selected .."]".. 109 | "button[3.5,1;1.25,0.75;CRAFT;CRAFT]".. 110 | "image[3.65,0;1,1;".. tex .. ".png]".. 111 | "label[0,1.85;".. description .. "]".. 112 | "list[context;recipe;5,0;3,2;]".. 113 | "label[0,2.3;Put crafting materials here]".. 114 | "list[context;main;0,2.7;8,3;]".. 115 | --"list[context;dst;5,0;3,2;]".. 116 | "label[0,5.5;player inventory]".. 117 | "list[current_player;main;0,6;8,4;]".. 118 | "listring[context;main]".. 119 | "listring[current_player;main]"; 120 | meta:set_string("formspec", form); 121 | end 122 | 123 | 124 | minetest.register_node("basic_machines:constructor", { 125 | description = "Constructor: used to make machines", 126 | tiles = {"constructor.png"}, 127 | groups = {cracky=3}, 128 | sounds = default.node_sound_wood_defaults(), 129 | after_place_node = function(pos, placer) 130 | local meta = minetest.get_meta(pos); 131 | meta:set_string("infotext", "Constructor: To operate it insert materials, select item to make and click craft button.") 132 | meta:set_string("owner", placer:get_player_name()); 133 | meta:set_string("craft","keypad") 134 | meta:set_int("selected",1); 135 | local inv = meta:get_inventory();inv:set_size("main", 24);--inv:set_size("dst",6); 136 | inv:set_size("recipe",8); 137 | end, 138 | 139 | on_rightclick = function(pos, node, player, itemstack, pointed_thing) 140 | local meta = minetest.get_meta(pos); 141 | local privs = minetest.get_player_privs(player:get_player_name()); 142 | if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler 143 | constructor_update_meta(pos); 144 | end, 145 | 146 | allow_metadata_inventory_put = function(pos, listname, index, stack, player) 147 | if listname == "recipe" then return 0 end 148 | local meta = minetest.get_meta(pos); 149 | local privs = minetest.get_player_privs(player:get_player_name()); 150 | if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end 151 | return stack:get_count(); 152 | end, 153 | 154 | allow_metadata_inventory_take = function(pos, listname, index, stack, player) 155 | if listname == "recipe" then return 0 end 156 | local privs = minetest.get_player_privs(player:get_player_name()); 157 | if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return 0 end 158 | return stack:get_count(); 159 | end, 160 | 161 | on_metadata_inventory_put = function(pos, listname, index, stack, player) 162 | if listname == "recipe" then return 0 end 163 | local privs = minetest.get_player_privs(player:get_player_name()); 164 | if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return 0 end 165 | return stack:get_count(); 166 | end, 167 | 168 | allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) 169 | return 0; 170 | end, 171 | 172 | effector = { 173 | action_on = function (pos, node,ttl) 174 | if type(ttl)~="number" then ttl = 1 end 175 | if ttl<0 then return end -- machines_TTL prevents infinite recursion 176 | constructor_process(pos); 177 | end 178 | }, 179 | 180 | on_receive_fields = function(pos, formname, fields, sender) 181 | 182 | if minetest.is_protected(pos, sender:get_player_name()) then return end 183 | local meta = minetest.get_meta(pos); 184 | 185 | if fields.craft then 186 | if string.sub(fields.craft,1,3)=="CHG" then 187 | local sel = tonumber(string.sub(fields.craft,5)) or 1 188 | meta:set_int("selected",sel); 189 | 190 | local i = 0; 191 | for _,v in ipairs(basic_machines.craft_recipe_order) do 192 | i=i+1; 193 | if i == sel then meta:set_string("craft",v); break; end 194 | end 195 | else 196 | return 197 | end 198 | end 199 | 200 | if fields.CRAFT then 201 | constructor_process(pos); 202 | end 203 | 204 | constructor_update_meta(pos); 205 | end, 206 | 207 | can_dig = function(pos) 208 | local meta = minetest.get_meta(pos); 209 | local inv = meta:get_inventory(); 210 | 211 | if not (inv:is_empty("main")) then return false end -- main inv must be empty to be dug 212 | 213 | return true 214 | 215 | end 216 | 217 | 218 | }) 219 | 220 | 221 | minetest.register_craft({ 222 | output = "basic_machines:constructor", 223 | recipe = { 224 | {"default:steel_ingot","default:steel_ingot","default:steel_ingot"}, 225 | {"default:steel_ingot","default:copperblock","default:steel_ingot"}, 226 | {"default:steel_ingot","default:steel_ingot","default:steel_ingot"}, 227 | 228 | } 229 | }) -------------------------------------------------------------------------------- /depends.txt: -------------------------------------------------------------------------------- 1 | default 2 | protector? 3 | doors? 4 | areas? 5 | boneworld? 6 | moreores? 7 | mesecons? -------------------------------------------------------------------------------- /enviro.lua: -------------------------------------------------------------------------------- 1 | -- ENVIRO block: change physics and skybox for players 2 | -- note: nonadmin players are limited in changes ( cant change skybox and have limits on other allowed changes) 3 | 4 | -- rnd 2016: 5 | 6 | local enviro = {}; 7 | enviro.skyboxes = { 8 | ["default"]={type = "regular", textures = {}}, 9 | ["space"]={type="skybox", clouds = false,textures={"basic_machines_stars.png","basic_machines_stars.png","basic_machines_stars.png","basic_machines_stars.png","basic_machines_stars.png","basic_machines_stars.png",}}, -- need textures installed! 10 | ["caves"]={type = "cavebox", textures = {"black.png","black.png","black.png","black.png","black.png","black.png",}}, 11 | }; 12 | 13 | local space_start = 1100; 14 | local exclusion_height = 6666; -- above all players without privs die and get teleported to spawn 15 | 16 | local ENABLE_SPACE_EFFECTS = false -- enable damage outside protected areas 17 | 18 | local enviro_update_form = function (pos) 19 | 20 | local meta = minetest.get_meta(pos); 21 | 22 | local x0,y0,z0; 23 | x0=meta:get_int("x0");y0=meta:get_int("y0");z0=meta:get_int("z0"); 24 | 25 | local skybox = meta:get_string("skybox"); 26 | local skylist = ""; 27 | local sky_ind,j; 28 | j=1;sky_ind = 3; 29 | for i,_ in pairs(enviro.skyboxes) do 30 | if i == skybox then sky_ind = j end 31 | skylist = skylist .. i .. ","; 32 | j=j+1; 33 | end 34 | local r = meta:get_int("r"); 35 | local speed,jump, g, sneak; 36 | speed = meta:get_float("speed");jump = meta:get_float("jump"); 37 | g = meta:get_float("g"); sneak = meta:get_int("sneak"); 38 | local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z; 39 | 40 | local form = 41 | "size[8,8.5]".. -- width, height 42 | "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.."]".. 43 | "field[3.25,0.5;1,1;r;radius;"..r.."]".. 44 | --speed, jump, gravity,sneak 45 | "field[0.25,1.5;1,1;speed;speed;"..speed.."]".. 46 | "field[1.25,1.5;1,1;jump;jump;"..jump.."]".. 47 | "field[2.25,1.5;1,1;g;gravity;"..g.."]".. 48 | "field[3.25,1.5;1,1;sneak;sneak;"..sneak.."]".. 49 | "label[0.,3.0;Skybox selection]".. 50 | "dropdown[0.,3.35;3,1;skybox;"..skylist..";"..sky_ind.."]".. 51 | "button_exit[3.25,3.25;1,1;OK;OK]".. 52 | "list["..list_name..";fuel;3.25,2.25;1,1;]".. 53 | "list[current_player;main;0,4.5;8,4;]".. 54 | "listring[current_player;main]".. 55 | "listring["..list_name..";fuel]".. 56 | "listring[current_player;main]" 57 | meta:set_string("formspec",form) 58 | end 59 | 60 | -- enviroment changer 61 | minetest.register_node("basic_machines:enviro", { 62 | description = "Changes enviroment for players around target location", 63 | tiles = {"enviro.png"}, 64 | drawtype = "allfaces", 65 | paramtype = "light", 66 | param1=1, 67 | groups = {cracky=3}, 68 | sounds = default.node_sound_wood_defaults(), 69 | after_place_node = function(pos, placer) 70 | local meta = minetest.env:get_meta(pos) 71 | meta:set_string("infotext", "Right click to set it. Activate by signal.") 72 | meta:set_string("owner", placer:get_player_name()); meta:set_int("public",1); 73 | meta:set_int("x0",0);meta:set_int("y0",0);meta:set_int("z0",0); -- target 74 | meta:set_int("r",5); meta:set_string("skybox","default"); 75 | meta:set_float("speed",1); 76 | meta:set_float("jump",1); 77 | meta:set_float("g",1); 78 | meta:set_int("sneak",1); 79 | meta:set_int("admin",0); 80 | local name = placer:get_player_name(); 81 | meta:set_string("owner",name); 82 | local privs = minetest.get_player_privs(name); 83 | if privs.privs then meta:set_int("admin",1) end 84 | if privs.machines then meta:set_int("machines",1) end 85 | 86 | local inv = meta:get_inventory(); 87 | inv:set_size("fuel",1*1); 88 | 89 | enviro_update_form(pos); 90 | end, 91 | 92 | effector = { 93 | action_on = function (pos, node,ttl) 94 | local meta = minetest.get_meta(pos); 95 | local machines = meta:get_int("machines"); 96 | if not machines == 1 then meta:set_string("infotext","Error. You need machines privs.") return end 97 | 98 | local admin = meta:get_int("admin"); 99 | 100 | local inv = meta:get_inventory(); local stack = ItemStack("default:diamond 1"); 101 | 102 | if inv:contains_item("fuel", stack) then 103 | inv:remove_item("fuel", stack); 104 | else 105 | meta:set_string("infotext","Error. Insert diamond in fuel inventory") 106 | return 107 | end 108 | 109 | local x0,y0,z0,r,skybox,speed,jump,g,sneak; 110 | x0=meta:get_int("x0"); y0=meta:get_int("y0");z0=meta:get_int("z0"); -- target 111 | r= meta:get_int("r",5); skybox=meta:get_string("skybox"); 112 | speed=meta:get_float("speed");jump= meta:get_float("jump"); 113 | g=meta:get_float("g");sneak=meta:get_int("sneak"); if sneak~=0 then sneak = true else sneak = false end 114 | 115 | local players = minetest.get_connected_players(); 116 | for _,player in pairs(players) do 117 | local pos1 = player:get_pos(); 118 | local dist = math.sqrt((pos1.x-pos.x)^2 + (pos1.y-pos.y)^2 + (pos1.z-pos.z)^2 ); 119 | if dist<=r then 120 | 121 | player:set_physics_override({speed=speed,jump=jump,gravity=g,sneak=sneak}) 122 | 123 | if admin == 1 then -- only admin can change skybox 124 | local sky = enviro.skyboxes[skybox]; 125 | player:set_sky(sky); 126 | end 127 | end 128 | end 129 | 130 | -- attempt to set acceleration to balls, if any around 131 | local objects = minetest.get_objects_inside_radius(pos, r) 132 | 133 | for _,obj in pairs(objects) do 134 | if obj:get_luaentity() then 135 | local obj_name = obj:get_luaentity().name or "" 136 | if obj_name == "basic_machines:ball" then 137 | obj:setacceleration({x=0,y=-g,z=0}); 138 | end 139 | end 140 | 141 | end 142 | 143 | 144 | 145 | 146 | end 147 | }, 148 | 149 | 150 | on_receive_fields = function(pos, formname, fields, sender) 151 | 152 | local name = sender:get_player_name();if minetest.is_protected(pos,name) then return end 153 | 154 | if fields.OK then 155 | local privs = minetest.get_player_privs(sender:get_player_name()); 156 | local meta = minetest.get_meta(pos); 157 | local x0=0; local y0=0; local z0=0; 158 | --minetest.chat_send_all("form at " .. dump(pos) .. " fields " .. dump(fields)) 159 | if fields.x0 then x0 = tonumber(fields.x0) or 0 end 160 | if fields.y0 then y0 = tonumber(fields.y0) or 0 end 161 | if fields.z0 then z0 = tonumber(fields.z0) or 0 end 162 | if not privs.privs and (math.abs(x0)>10 or math.abs(y0)>10 or math.abs(z0) > 10) then return end 163 | 164 | meta:set_int("x0",x0);meta:set_int("y0",y0);meta:set_int("z0",z0); 165 | if privs.privs then -- only admin can set sky 166 | if fields.skybox then meta:set_string("skybox", fields.skybox) end 167 | end 168 | if fields.r then 169 | local r = tonumber(fields.r) or 0; 170 | if r > 10 and not privs.privs then return end 171 | meta:set_int("r", r) 172 | end 173 | if fields.g then 174 | local g = tonumber(fields.g) or 1; 175 | if (g<0.1 or g>40) and not privs.privs then return end 176 | meta:set_float("g", g) 177 | end 178 | if fields.speed then 179 | local speed = tonumber(fields.speed) or 1; 180 | if (speed>1 or speed < 0) and not privs.privs then return end 181 | meta:set_float("speed", speed) 182 | end 183 | if fields.jump then 184 | local jump = tonumber(fields.jump) or 1; 185 | if (jump<0 or jump>2) and not privs.privs then return end 186 | meta:set_float("jump", jump) 187 | end 188 | if fields.sneak then 189 | meta:set_int("sneak", tonumber(fields.sneak) or 0) 190 | end 191 | 192 | 193 | enviro_update_form(pos); 194 | end 195 | end, 196 | 197 | allow_metadata_inventory_take = function(pos, listname, index, stack, player) 198 | local meta = minetest.get_meta(pos); 199 | local privs = minetest.get_player_privs(player:get_player_name()); 200 | if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end 201 | return stack:get_count(); 202 | end, 203 | 204 | allow_metadata_inventory_put = function(pos, listname, index, stack, player) 205 | local meta = minetest.get_meta(pos); 206 | local privs = minetest.get_player_privs(player:get_player_name()); 207 | if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end 208 | return stack:get_count(); 209 | end, 210 | 211 | can_dig = function(pos, player) -- dont dig if fuel is inside, cause it will be destroyed 212 | local meta = minetest.get_meta(pos); 213 | local inv = meta:get_inventory(); 214 | return inv:is_empty("fuel") 215 | end, 216 | 217 | }) 218 | 219 | 220 | -- DEFAULT (SPAWN) PHYSICS VALUE/SKYBOX 221 | 222 | local reset_player_physics = function(player) 223 | if player then 224 | player:set_physics_override({speed=1,jump=1,gravity=1}) -- value set for extreme test space spawn 225 | local skybox = enviro.skyboxes["default"]; -- default skybox is "default" 226 | player:set_sky(skybox); 227 | end 228 | end 229 | 230 | -- globally available function 231 | enviro_adjust_physics = function(player) -- adjust players physics/skybox 1 second after various events 232 | minetest.after(1, function() 233 | if player then 234 | local pos = player:get_pos(); if not pos then return end 235 | if pos.y > space_start then -- is player in space or not? 236 | player:set_physics_override({speed=1,jump=0.5,gravity=0.1}) -- value set for extreme test space spawn 237 | local skybox = enviro.skyboxes["space"]; 238 | player:set_sky(skybox); 239 | else 240 | player:set_physics_override({speed=1,jump=1,gravity=1}) -- value set for extreme test space spawn 241 | local skybox = enviro.skyboxes["default"]; 242 | player:set_sky(skybox); 243 | end 244 | end 245 | end) 246 | end 247 | 248 | 249 | -- restore default values/skybox on respawn of player 250 | minetest.register_on_respawnplayer(reset_player_physics) 251 | 252 | -- when player joins, check where he is and adjust settings 253 | minetest.register_on_joinplayer(enviro_adjust_physics) 254 | 255 | 256 | -- SERVER GLOBAL SPACE CODE: uncomment to enable it 257 | 258 | local round = math.floor; 259 | local protector_position = function(pos) 260 | local r = 20; 261 | local ry = 2*r; 262 | 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}; 263 | end 264 | 265 | local stimer = 0 266 | local enviro_space = {}; 267 | local exclusion_zone = {}; 268 | local moderator = {}; 269 | 270 | minetest.register_globalstep(function(dtime) 271 | stimer = stimer + dtime; 272 | if stimer >= 5 then 273 | stimer = 0; 274 | local players = minetest.get_connected_players(); 275 | for _,player in pairs(players) do 276 | local name = player:get_player_name(); 277 | local pos = player:get_pos(); 278 | local inspace=0; 279 | if pos.y>space_start then 280 | inspace = 1 281 | if pos.y > exclusion_height then 282 | local exclude = exclusion_zone[name]; 283 | if exclude == nil then 284 | exclusion_zone[name] = not minetest.get_player_privs(name).include; 285 | exclude = exclusion_zone[name] 286 | end 287 | if exclude then 288 | minetest.chat_send_all("exclusion zone alert: " .. name .. " " .. pos.x .. " " .. pos.y .. " " .. pos.z ) 289 | minetest.log("exclusion zone alert: " .. name .. " " .. pos.x .. " " .. pos.y .. " " .. pos.z ) 290 | player:set_pos({x=0,y=-100,z=0}) 291 | end 292 | end 293 | end 294 | local inspace0=enviro_space[name]; 295 | if inspace~=inspace0 then -- only adjust player enviroment ONLY if change occured ( earth->space or space->earth !) 296 | enviro_space[name] = inspace; 297 | enviro_adjust_physics(player); 298 | end 299 | 300 | if ENABLE_SPACE_EFFECTS and inspace==1 then -- special space code 301 | 302 | 303 | if pos.y<1500 and pos.y>1120 then 304 | local hp = player:get_hp(); 305 | 306 | if hp>0 then 307 | minetest.chat_send_player(name,"WARNING: you entered DEADLY RADIATION ZONE."); 308 | local privs = minetest.get_player_privs(name) 309 | if not privs.kick then player:set_hp(hp-15) end 310 | end 311 | return 312 | else 313 | 314 | local ppos = protector_position(pos); 315 | local populated = (minetest.get_node(ppos).name=="basic_protect:protector"); 316 | if populated then 317 | if minetest.get_meta(ppos):get_int("space") == 1 then populated = false end 318 | end 319 | 320 | if not populated then -- do damage if player found not close to protectors 321 | local hp = player:get_hp(); 322 | local privs = minetest.get_player_privs(name); 323 | if hp>0 and not privs.kick then 324 | player:set_hp(hp-10); -- dead in 20/10 = 2 events 325 | minetest.chat_send_player(name,"WARNING: in space you must stay close to spawn or protected areas"); 326 | end 327 | end 328 | end 329 | 330 | end 331 | end 332 | end 333 | end) 334 | 335 | -- END OF SPACE CODE 336 | 337 | 338 | -- AIR EXPERIMENT 339 | -- minetest.register_node("basic_machines:air", { 340 | -- description = "enables breathing in space", 341 | -- drawtype = "liquid", 342 | -- tiles = {"default_water_source_animated.png"}, 343 | 344 | -- drawtype = "glasslike", 345 | -- paramtype = "light", 346 | -- sunlight_propagates = true, -- Sunlight shines through 347 | -- walkable = false, -- Would make the player collide with the air node 348 | -- pointable = false, -- You can't select the node 349 | -- diggable = false, -- You can't dig the node 350 | -- buildable_to = true, 351 | -- drop = "", 352 | -- groups = {not_in_creative_inventory=1}, 353 | -- after_place_node = function(pos, placer, itemstack, pointed_thing) 354 | -- local r = 3; 355 | -- for i = -r,r do 356 | -- for j = -r,r do 357 | -- for k = -r,r do 358 | -- local p = {x=pos.x+i,y=pos.y+j,z=pos.z+k}; 359 | -- if minetest.get_node(p).name == "air" then 360 | -- minetest.set_node(p,{name = "basic_machines:air"}) 361 | -- end 362 | -- end 363 | -- end 364 | -- end 365 | -- end 366 | 367 | -- }) 368 | 369 | -- minetest.register_abm({ 370 | -- nodenames = {"basic_machines:air"}, 371 | -- neighbors = {"air"}, 372 | -- interval = 10, 373 | -- chance = 1, 374 | -- action = function(pos, node, active_object_count, active_object_count_wider) 375 | -- minetest.set_node(pos,{name = "air"}) 376 | -- end 377 | -- }); 378 | 379 | 380 | minetest.register_on_punchplayer( -- bring gravity closer to normal with each punch 381 | function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage) 382 | 383 | if player:get_physics_override() == nil then return end 384 | local pos = player:get_pos(); if pos.y>= space_start then return end 385 | 386 | local gravity = player:get_physics_override().gravity; 387 | if gravity<1 then 388 | gravity = 1; 389 | player:set_physics_override({gravity=gravity}) 390 | end 391 | end 392 | 393 | ) 394 | 395 | minetest.register_privilege("include", { 396 | description = "allow player to move in exclusion zone", 397 | }) 398 | 399 | 400 | -- RECIPE: extremely expensive 401 | 402 | -- minetest.register_craft({ 403 | -- output = "basic_machines:enviro", 404 | -- recipe = { 405 | -- {"basic_machines:generator", "basic_machines:clockgen","basic_machines:generator"}, 406 | -- {"basic_machines:generator", "basic_machines:generator","basic_machines:generator"}, 407 | -- {"basic_machines:generator", "basic_machines:generator", "basic_machines:generator"} 408 | -- } 409 | -- }) -------------------------------------------------------------------------------- /grinder.lua: -------------------------------------------------------------------------------- 1 | -- There is a certain fuel cost to operate 2 | 3 | -- recipe list: [in] ={fuel cost, out, quantity of material required for processing} 4 | basic_machines.grinder_recipes = { 5 | ["default:stone"] = {2,"default:sand",1}, 6 | ["default:desert_stone"] = {2,"default:desert_sand 4",1}, 7 | ["default:cobble"] = {1,"default:gravel",1}, 8 | ["default:gravel"] = {0.5,"default:dirt",1}, 9 | ["default:dirt"] = {0.5,"default:clay_lump 4",1}, 10 | ["default:obsidian_shard"] = {199,"default:lava_source",1}, 11 | ["gloopblocks:basalt"] = {1, "default:cobble",1}, -- enable coble farms with gloopblocks mod 12 | ["default:ice"] = {1, "default:snow 4",1}, 13 | ["darkage:silt_lump"]={1,"darkage:chalk_powder",1}, 14 | }; 15 | 16 | 17 | local grinder_process = function(pos) 18 | 19 | local node = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; 20 | local meta = minetest.get_meta(pos);local inv = meta:get_inventory(); 21 | 22 | -- activation limiter: 1/s 23 | local t0 = meta:get_int("t") 24 | local t1 = minetest.get_gametime(); 25 | 26 | if t1-t0>0 then 27 | meta:set_int("t",t1) 28 | else 29 | return 30 | end 31 | 32 | -- PROCESS: check out inserted items 33 | local stack = inv:get_stack("src",1); 34 | if stack:is_empty() then return end; -- nothing to do 35 | 36 | local src_item = stack:to_string(); 37 | 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 38 | 39 | local def = basic_machines.grinder_recipes[src_item]; 40 | if not def then 41 | meta:set_string("infotext", "please insert valid materials"); return 42 | end-- unknown node 43 | 44 | local steps = math.floor(stack:get_count() / def[3]) -- how many steps to process inserted stack 45 | 46 | if steps<1 then 47 | meta:set_string("infotext", "Recipe requires at least " .. def[3] .. " " .. src_item); 48 | return 49 | end 50 | 51 | local upgrade = meta:get_int("upgrade")+1 52 | if steps>upgrade then steps = upgrade end 53 | 54 | -- FUEL CHECK 55 | local fuel = meta:get_float("fuel"); 56 | local fuel_req = def[1]*steps; 57 | 58 | if fuel-fuel_req <0 then -- we need new fuel, check chest below 59 | local fuellist = inv:get_list("fuel") 60 | if not fuellist then return end 61 | 62 | local fueladd, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist}) 63 | 64 | local supply=0; 65 | if fueladd.time == 0 then -- no fuel inserted, try look for outlet 66 | -- No valid fuel in fuel list 67 | supply = basic_machines.check_power({x=pos.x,y=pos.y-1,z=pos.z} , fuel_req) or 0; -- tweaked so 1 coal = 1 energy 68 | if supply>0 then 69 | fueladd.time = supply -- same as 10 coal 70 | else 71 | meta:set_string("infotext", "Please insert fuel"); 72 | return; 73 | end 74 | else 75 | if supply==0 then -- Take fuel from fuel list if no supply available 76 | inv:set_stack("fuel",1,afterfuel.items[1]) 77 | fueladd.time=fueladd.time*0.1/4 -- thats 1 for coal 78 | end 79 | end 80 | if fueladd.time>0 then 81 | fuel=fuel + fueladd.time 82 | meta:set_float("fuel",fuel); 83 | meta:set_string("infotext", "added fuel furnace burn time " .. fueladd.time .. ", fuel status " .. fuel); 84 | end 85 | if fuel-fuel_req<0 then 86 | meta:set_string("infotext", "need at least " .. fuel_req-fuel .. " fuel to complete operation "); return 87 | end 88 | 89 | end 90 | 91 | -- process items 92 | local addstack = ItemStack(def[2]); 93 | if steps>1 then -- multiply stack 94 | local count = addstack:get_count(); 95 | addstack:set_count(count*steps) 96 | end 97 | if inv:room_for_item("dst", addstack) then 98 | inv:add_item("dst",addstack); 99 | else return 100 | end 101 | 102 | --take 'steps' items from src inventory for each activation 103 | stack=stack:take_item(steps); inv:remove_item("src", stack) 104 | 105 | minetest.sound_play("grinder", {pos=pos,gain=0.5,max_hear_distance = 16,}) 106 | 107 | fuel = fuel-fuel_req; -- burn fuel 108 | meta:set_float("fuel",fuel); 109 | meta:set_string("infotext", "fuel " .. fuel); 110 | 111 | end 112 | 113 | 114 | local grinder_update_meta = function(pos) 115 | local meta = minetest.get_meta(pos); 116 | local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z 117 | local form = 118 | "size[8,8]".. -- width, height 119 | --"size[6,10]".. -- width, height 120 | "label[0,0;IN] label[1,0;OUT] label[0,2;FUEL] label[5,0;UPGRADE]".. 121 | "list["..list_name..";src;0.,0.5;1,1;]".. 122 | "list["..list_name..";dst;1.,0.5;3,3;]".. 123 | "list["..list_name..";fuel;0.,2.5;1,1;]".. 124 | "list["..list_name..";upgrade;5.,0.5;2,1;]".. 125 | "list[current_player;main;0,4;8,4;]".. 126 | "button[7,0.5;1,1;OK;OK]".. 127 | "button[7,1.5;1,1;help;help]".. 128 | "listring["..list_name..";dst]".. 129 | "listring[current_player;main]".. 130 | "listring["..list_name..";src]".. 131 | "listring[current_player;main]".. 132 | "listring["..list_name..";fuel]".. 133 | "listring[current_player;main]" 134 | meta:set_string("formspec", form) 135 | end 136 | 137 | local upgrade_grinder = function(meta) 138 | local inv = meta:get_inventory(); 139 | local stack = inv:get_stack("upgrade", 1); local item = stack:get_name(); local count = stack:get_count(); 140 | if item ~= "basic_machines:grinder" then count = 0 end; meta:set_int("upgrade", count) 141 | end 142 | 143 | minetest.register_node("basic_machines:grinder", { 144 | description = "Grinder", 145 | tiles = {"grinder.png"}, 146 | groups = {cracky=3, mesecon_effector_on = 1}, 147 | sounds = default.node_sound_wood_defaults(), 148 | after_place_node = function(pos, placer) 149 | local meta = minetest.get_meta(pos); 150 | meta:set_string("infotext", "Grinder: To operate it insert fuel, then insert item to grind or use keypad to activate it.") 151 | meta:set_string("owner", placer:get_player_name()); 152 | meta:set_float("fuel",0); 153 | local inv = meta:get_inventory();inv:set_size("src", 1);inv:set_size("dst",9);inv:set_size("fuel",1); 154 | inv:set_size("upgrade",1); 155 | end, 156 | 157 | on_rightclick = function(pos, node, player, itemstack, pointed_thing) 158 | local meta = minetest.get_meta(pos); 159 | local privs = minetest.get_player_privs(player:get_player_name()); 160 | if minetest.is_protected(pos, player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler 161 | grinder_update_meta(pos); 162 | end, 163 | 164 | allow_metadata_inventory_put = function(pos, listname, index, stack, player) 165 | local meta = minetest.get_meta(pos); 166 | local privs = minetest.get_player_privs(player:get_player_name()); 167 | if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end 168 | return stack:get_count(); 169 | end, 170 | 171 | allow_metadata_inventory_take = function(pos, listname, index, stack, player) 172 | local meta = minetest.get_meta(pos); 173 | local privs = minetest.get_player_privs(player:get_player_name()); 174 | if meta:get_string("owner")~=player:get_player_name() and not privs.privs then return 0 end 175 | return stack:get_count(); 176 | end, 177 | 178 | on_metadata_inventory_take = function(pos, listname, index, stack, player) 179 | if listname == "upgrade" then upgrade_grinder(minetest.get_meta(pos)) end 180 | end, 181 | 182 | on_metadata_inventory_put = function(pos, listname, index, stack, player) 183 | if listname =="dst" then return end 184 | if listname == "upgrade" then upgrade_grinder(minetest.get_meta(pos)) end 185 | grinder_process(pos); 186 | end, 187 | 188 | allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) 189 | return 0; 190 | end, 191 | 192 | effector = { 193 | action_on = function (pos, node,ttl) 194 | if type(ttl)~="number" then ttl = 1 end 195 | if ttl<0 then return end -- machines_TTL prevents infinite recursion 196 | grinder_process(pos); 197 | end 198 | }, 199 | 200 | on_receive_fields = function(pos, formname, fields, sender) 201 | if fields.quit then return end 202 | local meta = minetest.get_meta(pos); 203 | 204 | if fields.help then 205 | --recipe list: [in] ={fuel cost, out, quantity of material required for processing} 206 | --basic_machines.grinder_recipes 207 | local text = "HELP & RECIPES\n\nTo upgrade grinder put grinders in upgrade slot. Each upgrade adds ability to process additional materials.\n\n"; 208 | for key,v in pairs(basic_machines.grinder_recipes) do 209 | text = text .. "INPUT ".. key .. " " .. v[3] .. " OUTPUT " .. v[2] .. "\n" 210 | end 211 | 212 | local form = "size [6,7] textarea[0,0;6.5,8.5;grinderhelp;GRINDER RECIPES;".. text.."]" 213 | minetest.show_formspec(sender:get_player_name(), "grinderhelp", form) 214 | 215 | end 216 | grinder_update_meta(pos); 217 | end, 218 | 219 | can_dig = function(pos) 220 | local meta = minetest.get_meta(pos); 221 | local inv = meta:get_inventory(); 222 | 223 | 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 224 | 225 | return true 226 | 227 | end 228 | 229 | }) 230 | 231 | 232 | -- minetest.register_craft({ 233 | -- output = "basic_machines:grinder", 234 | -- recipe = { 235 | -- {"default:diamond","default:mese","default:diamond"}, 236 | -- {"default:mese","default:diamondblock","default:mese"}, 237 | -- {"default:diamond","default:mese","default:diamond"}, 238 | 239 | -- } 240 | -- }) 241 | 242 | 243 | 244 | 245 | -- REGISTER DUSTS 246 | -- dust_00 (mix)-> extractor (smelt) -> dust_33 (smelt) -> dust_66 (smelt) -> ingot 247 | 248 | local function register_dust(name,input_node_name,ingot,grindcost,cooktime,R,G,B) 249 | 250 | if not R then R = "FF" end 251 | if not G then G = "FF" end 252 | if not B then B = "FF" end 253 | 254 | local purity_table = {"00","33","66"}; 255 | 256 | for i = 1,#purity_table do 257 | local purity = purity_table[i]; 258 | minetest.register_craftitem("basic_machines:"..name.."_dust_".. purity, { 259 | description = name.. " dust purity " .. purity .. "%" .. (purity=="00" and " (combine with chemicals to create " .. name .. " extractor )" or " (smelt to increase purity)") , 260 | inventory_image = "basic_machines_dust.png^[colorize:#"..R..G..B..":180", 261 | }) 262 | end 263 | 264 | basic_machines.grinder_recipes[input_node_name] = {grindcost,"basic_machines:"..name.."_dust_".. purity_table[1].." 2",1} -- register grinder recipe 265 | 266 | if ingot~="" then 267 | 268 | for i = 2,#purity_table-1 do -- all dusts but first one are cookable 269 | minetest.register_craft({ 270 | type = "cooking", 271 | recipe = "basic_machines:"..name.."_dust_".. purity_table[i], 272 | output = "basic_machines:"..name.."_dust_".. purity_table[i+1], 273 | cooktime = cooktime 274 | }) 275 | end 276 | 277 | minetest.register_craft({ 278 | type = "cooking", 279 | recipe = "basic_machines:"..name.."_dust_".. purity_table[#purity_table], 280 | groups = {not_in_creative_inventory=1}, 281 | output = ingot, 282 | cooktime = cooktime 283 | }) 284 | 285 | end 286 | end 287 | 288 | 289 | register_dust("iron","default:iron_lump","default:steel_ingot",4,8,"99","99","99") 290 | register_dust("copper","default:copper_lump","default:copper_ingot",4,8,"C8","80","0D") --c8800d 291 | register_dust("tin","default:tin_lump","default:tin_ingot",4,8,"9F","9F","9F") 292 | register_dust("gold","default:gold_lump","default:gold_ingot",6,25,"FF","FF","00") 293 | 294 | -- grinding ingots gives dust too 295 | basic_machines.grinder_recipes["default:steel_ingot"] = {4,"basic_machines:iron_dust_00 2",1}; 296 | basic_machines.grinder_recipes["default:copper_ingot"] = {4,"basic_machines:copper_dust_00 2",1}; 297 | basic_machines.grinder_recipes["default:gold_ingot"] = {6,"basic_machines:gold_dust_00 2",1}; 298 | basic_machines.grinder_recipes["default:tin_ingot"] = {4,"basic_machines:tin_dust_00 2",1}; 299 | 300 | -- are moreores (tin, silver, mithril) present? 301 | 302 | local table = minetest.registered_items["moreores:tin_lump"]; if table then 303 | --register_dust("tin","moreores:tin_lump","moreores:tin_ingot",4,8,"FF","FF","FF") 304 | register_dust("silver","moreores:silver_lump","moreores:silver_ingot",5,15,"BB","BB","BB") 305 | register_dust("mithril","moreores:mithril_lump","moreores:mithril_ingot",16,750,"00","00","FF") 306 | 307 | basic_machines.grinder_recipes["moreores:tin_ingot"] = {4,"basic_machines:tin_dust_00 2",1}; 308 | basic_machines.grinder_recipes["moreores:silver_ingot"] = {5,"basic_machines:silver_dust_33 2",1}; -- silver doesnt need extractor yet 309 | basic_machines.grinder_recipes["moreores:mithril_ingot"] = {16,"basic_machines:mithril_dust_00 2",1}; 310 | end 311 | 312 | 313 | register_dust("mese","default:mese_crystal","default:mese_crystal",8,250,"CC","CC","00") 314 | register_dust("diamond","default:diamond","default:diamond",16,500,"00","EE","FF") -- 0.3hr cooking time to make diamond! 315 | 316 | -- darkage recipes and ice 317 | minetest.register_craft({ 318 | type = "cooking", 319 | recipe = "default:ice", 320 | output = "default:water_flowing", 321 | cooktime = 4 322 | }) 323 | 324 | minetest.register_craft({ 325 | type = "cooking", 326 | recipe = "default:stone", 327 | output = "darkage:basalt", 328 | cooktime = 60 329 | }) 330 | 331 | minetest.register_craft({ 332 | type = "cooking", 333 | recipe = "darkage:slate", 334 | output = "darkage:schist", 335 | cooktime = 20 336 | }) 337 | 338 | -- dark age recipe: cook schist to get gneiss 339 | 340 | minetest.register_craft({ 341 | type = "cooking", 342 | recipe = "darkage:gneiss", 343 | output = "darkage:marble", 344 | cooktime = 20 345 | }) 346 | 347 | 348 | minetest.register_craft({ 349 | output = "darkage:serpentine", 350 | recipe = { 351 | {"darkage:marble","default:cactus"} 352 | } 353 | }) 354 | 355 | minetest.register_craft({ 356 | output = "darkage:mud", 357 | recipe = { 358 | {"default:dirt","default:water_flowing"} 359 | } 360 | }) 361 | 362 | 363 | -- EXTRACTORS, their recipes and smelting recipes 364 | 365 | local function register_extractor(name, R,G,B) 366 | 367 | if not R then R = "FF" end 368 | if not G then G = "FF" end 369 | if not B then B = "FF" end 370 | 371 | minetest.register_craftitem("basic_machines:"..name.."_extractor", { 372 | description = "smelt to get " .. name , 373 | inventory_image = "ore_extractor.png^[colorize:#"..R..G..B..":180", 374 | }) 375 | 376 | 377 | end 378 | 379 | register_extractor("iron","99","99","99") 380 | register_extractor("copper","C8","80","0D") 381 | register_extractor("tin","C8","9F","9F") 382 | register_extractor("gold","FF","FF","00") 383 | register_extractor("mese","CC","CC","00") 384 | register_extractor("diamond","00","EE","FF") 385 | register_extractor("mithril","00","00","FF") 386 | 387 | minetest.register_craft({ 388 | output = 'basic_machines:iron_extractor', 389 | recipe = { 390 | {'default:leaves','default:leaves','basic_machines:iron_dust_00'}, 391 | } 392 | }) 393 | 394 | -- extractor smelts to dust_33 395 | 396 | minetest.register_craft({ 397 | type = "cooking", 398 | recipe = "basic_machines:iron_extractor", 399 | output = "basic_machines:iron_dust_33", 400 | cooktime = 10 401 | }) 402 | 403 | minetest.register_craft({ 404 | output = 'basic_machines:copper_extractor', 405 | recipe = { 406 | {'default:papyrus','default:papyrus','basic_machines:copper_dust_00'}, 407 | } 408 | }) 409 | 410 | minetest.register_craft({ 411 | type = "cooking", 412 | recipe = "basic_machines:copper_extractor", 413 | output = "basic_machines:copper_dust_33", 414 | cooktime = 10 415 | }) 416 | 417 | minetest.register_craft({ 418 | output = 'basic_machines:tin_extractor', 419 | recipe = { 420 | {'farming:cocoa_beans','farming:cocoa_beans','basic_machines:tin_dust_00'}, 421 | } 422 | }) 423 | 424 | minetest.register_craft({ 425 | type = "cooking", 426 | recipe = "basic_machines:tin_extractor", 427 | output = "basic_machines:tin_dust_33", 428 | cooktime = 10 429 | }) 430 | 431 | minetest.register_craft({ 432 | output = 'basic_machines:gold_extractor', 433 | recipe = { 434 | {'basic_machines:tin_extractor','basic_machines:copper_extractor','basic_machines:gold_dust_00'}, 435 | } 436 | }) 437 | 438 | minetest.register_craft({ 439 | type = "cooking", 440 | recipe = "basic_machines:gold_extractor", 441 | output = "basic_machines:gold_dust_33", 442 | cooktime = 10 443 | }) 444 | 445 | minetest.register_craft({ 446 | output = 'basic_machines:mese_extractor', 447 | recipe = { 448 | {'farming:rhubarb','farming:rhubarb', 'basic_machines:mese_dust_00'}, 449 | } 450 | }) 451 | 452 | minetest.register_craft({ 453 | type = "cooking", 454 | recipe = "basic_machines:mese_extractor", 455 | output = "basic_machines:mese_dust_33", 456 | cooktime = 10 457 | }) 458 | 459 | minetest.register_craft({ 460 | output = 'basic_machines:diamond_extractor', 461 | recipe = { 462 | {'farming:wheat','farming:cotton', 'basic_machines:diamond_dust_00'}, 463 | } 464 | }) 465 | 466 | minetest.register_craft({ 467 | type = "cooking", 468 | recipe = "basic_machines:diamond_extractor", 469 | output = "basic_machines:diamond_dust_33", 470 | cooktime = 10 471 | }) 472 | 473 | minetest.register_craft({ 474 | output = 'basic_machines:mithril_extractor', 475 | recipe = { 476 | {'flowers:geranium','flowers:geranium', 'basic_machines:mithril_dust_00'}, -- blue flowers 477 | } 478 | }) 479 | 480 | 481 | minetest.register_craft({ 482 | type = "cooking", 483 | recipe = "basic_machines:mithril_extractor", 484 | output = "basic_machines:mithril_dust_33", 485 | cooktime = 10 486 | }) -------------------------------------------------------------------------------- /init.lua: -------------------------------------------------------------------------------- 1 | -- BASIC_MACHINES: lightweight automation mod for minetest 2 | -- minetest 0.4.14 3 | -- (c) 2015-2016 rnd 4 | 5 | -- This program is free software: you can redistribute it and/or modify 6 | -- it under the terms of the GNU General Public License as published by 7 | -- the Free Software Foundation, either version 3 of the License, or 8 | -- (at your option) any later version. 9 | 10 | -- This program is distributed in the hope that it will be useful, 11 | -- but WITHOUT ANY WARRANTY; without even the implied warranty of 12 | -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 | -- GNU General Public License for more details. 14 | 15 | -- You should have received a copy of the GNU General Public License 16 | -- along with this program. If not, see . 17 | 18 | 19 | basic_machines = {}; 20 | 21 | 22 | 23 | dofile(minetest.get_modpath("basic_machines").."/mark.lua") -- used for markings, borrowed and adapted from worldedit mod 24 | dofile(minetest.get_modpath("basic_machines").."/mover.lua") -- mover, detector, keypad, distributor 25 | dofile(minetest.get_modpath("basic_machines").."/technic_power.lua") -- technic power for mover 26 | dofile(minetest.get_modpath("basic_machines").."/recycler.lua") -- recycle old used tools 27 | dofile(minetest.get_modpath("basic_machines").."/grinder.lua") -- grind materials into dusts 28 | dofile(minetest.get_modpath("basic_machines").."/autocrafter.lua") -- borrowed and adapted from pipeworks mod 29 | dofile(minetest.get_modpath("basic_machines").."/constructor.lua") -- enable crafting of all machines 30 | 31 | dofile(minetest.get_modpath("basic_machines").."/protect.lua") -- enable interaction with players, adds local on protect/chat event handling 32 | 33 | -- MESECON functionality 34 | if mesecon then -- DO NOT REMOVE THIS CHECK or it wont work with mesecons 35 | dofile(minetest.get_modpath("basic_machines").."/mesecon_adapter.lua") 36 | basic_machines.craft_recipes["mesecon_adapter"] = {item = "basic_machines:mesecon_adapter", description = "interface between machines and mesecons", craft = {"default:mese_crystal_fragment"}, tex = "jeija_luacontroller_top"} 37 | basic_machines.craft_recipe_order[1+#basic_machines.craft_recipe_order] = "mesecon_adapter" 38 | end 39 | -- OPTIONAL ADDITIONAL STUFF ( comment to disable ) 40 | 41 | dofile(minetest.get_modpath("basic_machines").."/ball.lua") -- interactive flying ball, can activate blocks or be used as a weapon 42 | 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 43 | 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 44 | minetest.after(0, function() 45 | dofile(minetest.get_modpath("basic_machines").."/mesecon_lights.lua") -- adds ability for other light blocks to toggle light 46 | end) 47 | 48 | 49 | -- MACHINE PRIVILEGE 50 | minetest.register_privilege("machines", { 51 | description = "Player is expert basic_machine user: his machines work while not present on server, can spawn more than 2 balls at once", 52 | }) 53 | 54 | -- machines fuel related recipes 55 | -- CHARCOAL 56 | 57 | minetest.register_craftitem("basic_machines:charcoal", { 58 | description = "Wood charcoal", 59 | inventory_image = "charcoal.png", 60 | }) 61 | 62 | 63 | minetest.register_craft({ 64 | type = 'cooking', 65 | recipe = "default:tree", 66 | cooktime = 30, 67 | output = "basic_machines:charcoal", 68 | }) 69 | 70 | minetest.register_craft({ 71 | output = "default:coal_lump", 72 | recipe = { 73 | {"basic_machines:charcoal"}, 74 | {"basic_machines:charcoal"}, 75 | } 76 | }) 77 | 78 | minetest.register_craft({ 79 | type = "fuel", 80 | recipe = "basic_machines:charcoal", 81 | -- note: to make it you need to use 1 tree block for fuel + 1 tree block, thats 2, caloric value 2*30=60 82 | burntime = 40, -- coal lump has 40, tree block 30, coal block 370 (9*40=360!) 83 | }) 84 | 85 | -- add since minetest doesnt have moreores tin ingot recipe 86 | minetest.register_craft({ 87 | output = "default:tin_ingot", 88 | recipe = { 89 | {"moreores:tin_ingot"}, 90 | } 91 | }) 92 | 93 | -- COMPATIBILITY 94 | minetest.register_alias("basic_machines:battery", "basic_machines:battery_0") 95 | 96 | 97 | print("[MOD] basic_machines " .. basic_machines.version .. " loaded.") -------------------------------------------------------------------------------- /mark.lua: -------------------------------------------------------------------------------- 1 | -- rnd: code borrowed from machines, mark.lua 2 | 3 | -- need for marking 4 | machines = {}; 5 | 6 | machines.pos1 = {};machines.pos11 = {}; machines.pos2 = {}; 7 | machines.marker1 = {} 8 | machines.marker11 = {} 9 | machines.marker2 = {} 10 | machines.marker_region = {} 11 | 12 | 13 | --marks machines region position 1 14 | machines.mark_pos1 = function(name) 15 | local pos1, pos2 = machines.pos1[name], machines.pos2[name] 16 | 17 | if pos1 ~= nil then 18 | --make area stay loaded 19 | local manip = minetest.get_voxel_manip() 20 | manip:read_from_map(pos1, pos1) 21 | end 22 | 23 | if not machines[name] then machines[name]={} end 24 | machines[name].timer = 10; 25 | if machines.marker1[name] ~= nil then --marker already exists 26 | machines.marker1[name]:remove() --remove marker 27 | machines.marker1[name] = nil 28 | end 29 | if pos1 ~= nil then 30 | --add marker 31 | machines.marker1[name] = minetest.add_entity(pos1, "machines:pos1") 32 | if machines.marker1[name] ~= nil then 33 | machines.marker1[name]:get_luaentity().name = name 34 | end 35 | end 36 | end 37 | 38 | --marks machines region position 1 39 | machines.mark_pos11 = function(name) 40 | local pos11 = machines.pos11[name]; 41 | 42 | if pos11 ~= nil then 43 | --make area stay loaded 44 | local manip = minetest.get_voxel_manip() 45 | manip:read_from_map(pos11, pos11) 46 | end 47 | 48 | if not machines[name] then machines[name]={} end 49 | machines[name].timer = 10; 50 | if machines.marker11[name] ~= nil then --marker already exists 51 | machines.marker11[name]:remove() --remove marker 52 | machines.marker11[name] = nil 53 | end 54 | if pos11 ~= nil then 55 | --add marker 56 | machines.marker11[name] = minetest.add_entity(pos11, "machines:pos11") 57 | if machines.marker11[name] ~= nil then 58 | machines.marker11[name]:get_luaentity().name = name 59 | end 60 | end 61 | end 62 | 63 | --marks machines region position 2 64 | machines.mark_pos2 = function(name) 65 | local pos1, pos2 = machines.pos1[name], machines.pos2[name] 66 | 67 | if pos2 ~= nil then 68 | --make area stay loaded 69 | local manip = minetest.get_voxel_manip() 70 | manip:read_from_map(pos2, pos2) 71 | end 72 | 73 | if not machines[name] then machines[name]={} end 74 | machines[name].timer = 10; 75 | if machines.marker2[name] ~= nil then --marker already exists 76 | machines.marker2[name]:remove() --remove marker 77 | machines.marker2[name] = nil 78 | end 79 | if pos2 ~= nil then 80 | --add marker 81 | machines.marker2[name] = minetest.add_entity(pos2, "machines:pos2") 82 | if machines.marker2[name] ~= nil then 83 | machines.marker2[name]:get_luaentity().name = name 84 | end 85 | end 86 | end 87 | 88 | 89 | 90 | minetest.register_entity(":machines:pos1", { 91 | initial_properties = { 92 | visual = "cube", 93 | visual_size = {x=1.1, y=1.1}, 94 | textures = {"machines_pos1.png", "machines_pos1.png", 95 | "machines_pos1.png", "machines_pos1.png", 96 | "machines_pos1.png", "machines_pos1.png"}, 97 | collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55}, 98 | physical = false, 99 | }, 100 | on_step = function(self, dtime) 101 | if not machines[self.name] then machines[self.name]={}; machines[self.name].timer = 10 end 102 | machines[self.name].timer = machines[self.name].timer - dtime 103 | if machines[self.name].timer<=0 or machines.marker1[self.name] == nil then 104 | self.object:remove() 105 | end 106 | end, 107 | on_punch = function(self, hitter) 108 | self.object:remove() 109 | machines.marker1[self.name] = nil 110 | machines[self.name].timer = 10 111 | end, 112 | }) 113 | 114 | minetest.register_entity(":machines:pos11", { 115 | initial_properties = { 116 | visual = "cube", 117 | visual_size = {x=1.1, y=1.1}, 118 | textures = {"machines_pos11.png", "machines_pos11.png", 119 | "machines_pos11.png", "machines_pos11.png", 120 | "machines_pos11.png", "machines_pos11.png"}, 121 | collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55}, 122 | physical = false, 123 | }, 124 | on_step = function(self, dtime) 125 | if not machines[self.name] then machines[self.name]={}; machines[self.name].timer = 10 end 126 | machines[self.name].timer = machines[self.name].timer - dtime 127 | if machines[self.name].timer<=0 or machines.marker11[self.name] == nil then 128 | self.object:remove() 129 | end 130 | end, 131 | on_punch = function(self, hitter) 132 | self.object:remove() 133 | machines.marker11[self.name] = nil 134 | machines[self.name].timer = 10 135 | end, 136 | }) 137 | 138 | minetest.register_entity(":machines:pos2", { 139 | initial_properties = { 140 | visual = "cube", 141 | visual_size = {x=1.1, y=1.1}, 142 | textures = {"machines_pos2.png", "machines_pos2.png", 143 | "machines_pos2.png", "machines_pos2.png", 144 | "machines_pos2.png", "machines_pos2.png"}, 145 | collisionbox = {-0.55, -0.55, -0.55, 0.55, 0.55, 0.55}, 146 | physical = false, 147 | }, 148 | on_step = function(self, dtime) 149 | if not machines[self.name] then machines[self.name]={}; machines[self.name].timer = 10 end 150 | if machines[self.name].timer<=0 or machines.marker2[self.name] == nil then 151 | self.object:remove() 152 | end 153 | end, 154 | }) 155 | -------------------------------------------------------------------------------- /mesecon_adapter.lua: -------------------------------------------------------------------------------- 1 | 2 | -- block that can be activated/activate mesecon blocks 3 | 4 | local adapter_effector = { 5 | action_on = function (pos, node,ttl) 6 | if type(ttl)~="number" then ttl = 2 end 7 | if not(ttl>0) then return end 8 | 9 | pos.y=pos.y+1; 10 | local node = minetest.get_node(pos); 11 | if not node.name then return end -- error 12 | local table = minetest.registered_nodes[node.name]; 13 | if not table then return end 14 | if table.effector and table.effector.action_on then -- activate basic_machine 15 | local effector=table.effector; 16 | effector.action_on(pos,node,ttl); 17 | else -- table.mesecons and table.mesecons.effector then -- activate mesecons 18 | pos.y=pos.y-1 19 | mesecon.receptor_on(pos, mesecon.rules.buttonlike_get(node)) 20 | --local effector=table.mesecons.effector; 21 | --effector.action_on(pos,node); 22 | end 23 | end, 24 | 25 | action_off = function (pos, node,ttl) 26 | if type(ttl)~="number" then ttl = 2 end 27 | if not(ttl>0) then return end 28 | 29 | pos.y=pos.y+1; 30 | local node = minetest.get_node(pos); 31 | if not node.name then return end -- error 32 | local table = minetest.registered_nodes[node.name]; 33 | if not table then return end 34 | if table.effector and table.effector.action_off then -- activate basic_machine 35 | local effector=table.effector; 36 | effector.action_off(pos,node,ttl); 37 | else -- table.mesecons and table.mesecons.effector then -- activate mesecons 38 | pos.y=pos.y-1 39 | mesecon.receptor_off(pos, mesecon.rules.buttonlike_get(node)) 40 | --local effector=table.mesecons.effector; 41 | --effector.action_off(pos,node); 42 | end 43 | end, 44 | } 45 | 46 | minetest.register_node("basic_machines:mesecon_adapter", { 47 | description = "interface between machines and mesecons - place block to be activated on top of it.", 48 | tiles = {"basic_machine_clock_generator.png","basic_machine_clock_generator.png", 49 | "jeija_luacontroller_top.png","jeija_luacontroller_top.png","jeija_luacontroller_top.png","jeija_luacontroller_top.png" 50 | }, 51 | groups = {cracky=3,mesecon_effector_on = 1,mesecon_effector_off=1,mesecon_needs_receiver = 1,}, 52 | sounds = default.node_sound_wood_defaults(), 53 | 54 | 55 | effector = adapter_effector, 56 | mesecons = { 57 | effector = adapter_effector, 58 | receptor = { 59 | rules = mesecon.rules.buttonlike_get, 60 | state = mesecon.state.off 61 | }, 62 | }, 63 | 64 | }) -------------------------------------------------------------------------------- /mesecon_doors.lua: -------------------------------------------------------------------------------- 1 | -- make doors open/close with signal 2 | 3 | tablecopy = function(a) 4 | local b = {}; 5 | for k,v in pairs(a) do 6 | if type(v) == "table" then 7 | b[k] = tablecopy(v) 8 | else 9 | b[k]=v 10 | end 11 | end 12 | return b 13 | end 14 | 15 | local function door_signal_overwrite(name) 16 | 17 | local table = minetest.registered_nodes[name]; if not table then return end 18 | --if table.mesecons then return end -- already exists, don't change 19 | local door_on_rightclick = table.on_rightclick; 20 | 21 | minetest.override_item(name, 22 | { 23 | effector = { 24 | action_on = function(pos,node) 25 | local meta = minetest.get_meta(pos);local name = meta:get_string("owner"); 26 | -- create virtual player 27 | local clicker = {}; 28 | function clicker:get_wielded_item() 29 | local item = {} 30 | function item:get_name() return "" end 31 | return item 32 | end 33 | 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 34 | function clicker:is_player() return true end; -- method needed for mods that check this: like denaid areas mod 35 | if door_on_rightclick then door_on_rightclick(pos, nil, clicker,ItemStack(""),{}) end -- safety if it doesnt exist 36 | --minetest.swap_node(pos, {name = "protector:trapdoor", param1 = node.param1, param2 = node.param2}) -- more direct approach?, need to set param2 then too 37 | end 38 | } 39 | } 40 | ) 41 | 42 | end 43 | 44 | door_signal_overwrite("doors:door_wood");door_signal_overwrite("doors:door_steel") 45 | door_signal_overwrite("doors:door_wood_a");door_signal_overwrite("doors:door_wood_b"); 46 | door_signal_overwrite("doors:door_steel_a");door_signal_overwrite("doors:door_steel_b"); 47 | door_signal_overwrite("doors:door_glass_a");door_signal_overwrite("doors:door_glass_b"); 48 | door_signal_overwrite("doors:door_obsidian_glass_a");door_signal_overwrite("doors:door_obsidian_glass_b"); 49 | 50 | door_signal_overwrite("doors:trapdoor");door_signal_overwrite("doors:trapdoor_open"); 51 | door_signal_overwrite("doors:trapdoor_steel");door_signal_overwrite("doors:trapdoor_steel_open"); 52 | 53 | local function make_it_noclip(name) 54 | minetest.override_item(name,{walkable = false}); -- cant be walked on 55 | end 56 | 57 | make_it_noclip("doors:trapdoor_open"); 58 | make_it_noclip("doors:trapdoor_steel_open"); 59 | 60 | -- minetest bug: using override_item to change group.level = 99 does nothing - door still diggable 61 | -- using minetest.register_node(":"..name, table2) with table2.group.level = 99 does work. why? 62 | 63 | --[[ 64 | 65 | local function make_it_nondiggable_but_removable(name, dropname) 66 | 67 | local table = minetest.registered_nodes[name]; if not table then return end 68 | local table2 = tablecopy(table.groups); 69 | 70 | table2.level = 99; -- cant be digged, but it can be removed by owner or if not protected 71 | 72 | minetest.override_item(name, 73 | { 74 | on_punch = function(pos, node, puncher, pointed_thing) -- remove node if owner repeatedly punches it 3x 75 | local pname = puncher:get_player_name(); 76 | local meta = minetest.get_meta(pos); 77 | local owner = meta:get_string("doors_owner") 78 | if pname==owner or not minetest.is_protected(pos,pname) then -- can be dug by owner or if unprotected 79 | local t0 = meta:get_int("punchtime");local count = meta:get_int("punchcount"); 80 | local t = minetest.get_gametime(); 81 | 82 | if t-t0<2 then count = (count +1 ) % 3 else count = 0 end 83 | 84 | if count == 1 then 85 | minetest.chat_send_player(pname, "#steel door: punch me one more time to remove me"); 86 | end 87 | if count == 2 then -- remove steel door and drop it 88 | minetest.set_node(pos, {name = "air"}); 89 | local stack = ItemStack(dropname);minetest.add_item(pos,stack) 90 | end 91 | 92 | meta:set_int("punchcount",count);meta:set_int("punchtime",t); 93 | --minetest.chat_send_all("punch by "..name .. " count " .. count) 94 | end 95 | end 96 | }) 97 | 98 | minetest.override_item(name, {diggable = false}) 99 | 100 | --minetest.register_node(":"..name, table2) 101 | end 102 | 103 | --minetest.after(0,function() 104 | make_it_nondiggable_but_removable("doors:door_steel_a","doors:door_steel"); 105 | make_it_nondiggable_but_removable("doors:door_steel_b","doors:door_steel"); 106 | 107 | make_it_nondiggable_but_removable("doors:trapdoor_steel","doors:trapdoor_steel"); 108 | make_it_nondiggable_but_removable("doors:trapdoor_steel_open","doors:trapdoor_steel"); 109 | --end); 110 | --]] -------------------------------------------------------------------------------- /mesecon_lights.lua: -------------------------------------------------------------------------------- 1 | -- make other light blocks work with mesecon signals - can toggle on/off 2 | 3 | local function enable_toggle_light(name) 4 | 5 | local table = minetest.registered_nodes[name]; if not table then return end 6 | local table2 = {} 7 | for i,v in pairs(table) do table2[i] = v end 8 | 9 | if table2.mesecons then return end -- we dont want to overwrite existing stuff! 10 | 11 | local offname = "basic_machines:"..string.gsub(name, ":", "_").. "_OFF"; 12 | 13 | table2.effector = { -- action to toggle light off 14 | action_off = function (pos,node,ttl) 15 | minetest.swap_node(pos,{name = offname}); 16 | end 17 | }; 18 | 19 | 20 | minetest.register_node(":"..name, table2) -- redefine item 21 | 22 | -- 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!??? 23 | -- STRANGE BUG2: if you dont make new table3.. original node automatically changes to OFF node when placed ???? 24 | 25 | local table3 = {} 26 | for i,v in pairs(table) do 27 | table3[i] = v 28 | end 29 | 30 | table3.light_source = 0; -- off block has light off 31 | table3.effector = { 32 | action_on = function (pos,node,ttl) 33 | minetest.swap_node(pos,{name = name}); 34 | end 35 | }; 36 | 37 | -- REGISTER OFF BLOCK 38 | minetest.register_node(":"..offname, table3); 39 | 40 | end 41 | 42 | 43 | enable_toggle_light("xdecor:wooden_lightbox"); 44 | enable_toggle_light("xdecor:iron_lightbox"); 45 | enable_toggle_light("moreblocks:slab_meselamp_1"); 46 | enable_toggle_light("moreblocks:slab_super_glow_glass"); 47 | 48 | enable_toggle_light("darkage:lamp"); -------------------------------------------------------------------------------- /mod.conf: -------------------------------------------------------------------------------- 1 | name = basic_machines 2 | depends = default 3 | optional_depends = protector,doors,areas,boneworld,moreores,mesecons 4 | description = automate gameplay with machines. build logic mechanisms. -------------------------------------------------------------------------------- /plans.txt: -------------------------------------------------------------------------------- 1 | grinder: creates dust_0, must be mixed with extractor chemical to get dust_33 -------------------------------------------------------------------------------- /protect.lua: -------------------------------------------------------------------------------- 1 | -- adds event handler for attempt to dig in protected area 2 | 3 | -- tries to activate specially configured nearby distributor at points with coordinates of form 20i, registers dig attempts in radius 10 around 4 | -- distributor must have first target filter set to 0 ( disabled ) to handle dig events 5 | 6 | local old_is_protected = minetest.is_protected 7 | local round = math.floor; 8 | local machines_TTL = basic_machines.machines_TTL or 16 9 | 10 | function minetest.is_protected(pos, digger) 11 | 12 | local is_protected = old_is_protected(pos, digger); 13 | if is_protected then -- only if protected 14 | 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} 15 | 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 16 | local meta = minetest.get_meta(p); 17 | if meta:get_int("active1") == 0 then -- first output is disabled, indicating ready to be used as event handler 18 | if meta:get_int("x1") ~= 0 then -- trigger protection event 19 | meta:set_string("infotext",digger); -- record diggers name onto distributor 20 | local table = minetest.registered_nodes["basic_machines:distributor"]; 21 | local effector=table.effector; 22 | local node = nil; 23 | effector.action_on(p,node,machines_TTL); 24 | end 25 | end 26 | end 27 | end 28 | return is_protected; 29 | 30 | end 31 | 32 | minetest.register_on_chat_message(function(name, message) 33 | local player = minetest.get_player_by_name(name); 34 | if not player then return end 35 | local pos = player:get_pos(); 36 | 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} 37 | --minetest.chat_send_all(minetest.pos_to_string(p)) 38 | if minetest.get_node(p).name == "basic_machines:distributor" then -- attempt to activate distributor at special grid location: coordinates of the form 20*i 39 | local meta = minetest.get_meta(p); 40 | if meta:get_int("active1") == 0 then -- first output is disabled, indicating ready to be used as event handler 41 | local y1 = meta:get_int("y1"); 42 | if y1 ~= 0 then -- chat event, positive relays message, negative drops it 43 | meta:set_string("infotext",message); -- record diggers message 44 | local table = minetest.registered_nodes["basic_machines:distributor"]; 45 | local effector=table.mesecons.effector; 46 | local node = nil; 47 | effector.action_on(p,node,machines_TTL); 48 | if y1<0 then return true 49 | end 50 | end 51 | end 52 | end 53 | end 54 | ) -------------------------------------------------------------------------------- /recycler.lua: -------------------------------------------------------------------------------- 1 | -- rnd 2015: 2 | 3 | -- 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. 4 | -- There is a fuel cost to recycle 5 | 6 | -- prevent unrealistic recyclings 7 | local no_recycle_list = { 8 | ["default:steel_ingot"]=1,["default:copper_ingot"]=1,["default:bronze_ingot"]=1,["default:gold_ingot"]=1, 9 | ["dye:white"]=1,["dye:grey"]=1,["dye:dark_grey"]=1,["dye:black"]=1, 10 | ["dye:violet"]=1,["dye:blue"]=1,["dye:cyan"]=1,["dye:dark_green"]=1, 11 | ["dye:green"]=1,["dye:yellow"]=1,["dye:brown"]=1,["dye:orange"]=1, 12 | ["dye:red"]=1,["dye:magenta"]=1,["dye:pink"]=1,["default:mossycobble"]=1, 13 | } 14 | 15 | 16 | local recycler_process = function(pos) 17 | 18 | local node = minetest.get_node({x=pos.x,y=pos.y-1,z=pos.z}).name; 19 | local meta = minetest.get_meta(pos);local inv = meta:get_inventory(); 20 | 21 | -- FUEL CHECK 22 | local fuel = meta:get_float("fuel"); 23 | 24 | if fuel-1<0 then -- we need new fuel, check chest below 25 | local fuellist = inv:get_list("fuel") 26 | if not fuellist then return end 27 | 28 | local fueladd, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist}) 29 | 30 | local supply=0; 31 | if fueladd.time == 0 then -- no fuel inserted, try look for outlet 32 | -- No valid fuel in fuel list 33 | supply = basic_machines.check_power({x=pos.x,y=pos.y-1,z=pos.z},1) or 0; 34 | if supply>0 then 35 | fueladd.time = 40*supply -- same as 10 coal 36 | else 37 | meta:set_string("infotext", "Please insert fuel."); 38 | return; 39 | end 40 | else 41 | if supply==0 then -- Take fuel from fuel list if no supply available 42 | inv:set_stack("fuel", 1, afterfuel.items[1]) 43 | fueladd.time = fueladd.time*0.1; -- thats 4 for coal 44 | end 45 | end 46 | if fueladd.time>0 then 47 | fuel=fuel + fueladd.time 48 | meta:set_float("fuel",fuel); 49 | meta:set_string("infotext", "added fuel furnace burn time " .. fueladd.time .. ", fuel status " .. fuel); 50 | end 51 | if fuel-1<0 then return end 52 | end 53 | 54 | 55 | 56 | -- RECYCLING: check out inserted items 57 | local stack = inv:get_stack("src",1); 58 | if stack:is_empty() then return end; -- nothing to do 59 | 60 | local src_item = stack:to_string(); 61 | 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 62 | 63 | -- look if we already handled this item 64 | local known_recipe=true; 65 | if src_item~=meta:get_string("node") then-- did we already handle this? if yes read from cache 66 | meta:set_string("node",src_item); 67 | meta:set_string("itemlist","{}"); 68 | meta:set_int("reqcount",0); 69 | known_recipe=false; 70 | end 71 | 72 | local itemlist, reqcount; 73 | reqcount = 1; -- needed count of materials for recycle to work 74 | 75 | if not known_recipe then 76 | 77 | if no_recycle_list[src_item] then meta:set_string("node","") return end -- dont allow recycling of forbidden items 78 | 79 | local recipe = minetest.get_all_craft_recipes( src_item ); 80 | local recipe_id = tonumber(meta:get_int("recipe")) or 1; 81 | 82 | if not recipe then 83 | return 84 | else 85 | itemlist = recipe[recipe_id]; 86 | if not itemlist then meta:set_string("node","") return end; 87 | itemlist=itemlist.items; 88 | end 89 | local output = recipe[recipe_id].output or ""; 90 | if string.find(output," ") then 91 | local par = string.find(output," "); 92 | --if (tonumber(string.sub(output, par)) or 0)>1 then itemlist = {} end 93 | 94 | if par then 95 | reqcount = tonumber(string.sub(output, par)) or 1; 96 | end 97 | 98 | end 99 | 100 | meta:set_string("itemlist",minetest.serialize(itemlist)); -- read cached itemlist 101 | meta:set_int("reqcount",reqcount); 102 | else 103 | itemlist=minetest.deserialize(meta:get_string("itemlist")) or {}; 104 | reqcount = meta:get_int("reqcount") or 1; 105 | end 106 | 107 | if stack:get_count()0 then 73 | if pos.y>1500 then add_energy=2*add_energy end -- in space recharge is more efficient 74 | crystal = true; 75 | if add_energy<=capacity then 76 | stack:take_item(1); 77 | inv:set_stack("fuel", 1, stack) 78 | else 79 | meta:set_string("infotext", "recharge problem: capacity " .. capacity .. ", needed " .. energy+add_energy) 80 | return energy 81 | end 82 | else -- try do determine caloric value of fuel inside battery 83 | local fuellist = inv:get_list("fuel");if not fuellist then return energy end 84 | local fueladd, afterfuel = minetest.get_craft_result({method = "fuel", width = 1, items = fuellist}) 85 | if fueladd.time > 0 then 86 | add_energy = fueladd.time/40; 87 | if energy+add_energy<=capacity then 88 | inv:set_stack("fuel", 1, afterfuel.items[1]); 89 | else 90 | meta:set_string("infotext", "recharge problem: capacity " .. capacity .. ", needed " .. energy+add_energy) 91 | return energy 92 | end 93 | end 94 | end 95 | 96 | if add_energy>0 then 97 | energy=energy+add_energy 98 | if energy<0 then energy = 0 end 99 | if energy>capacity then energy = capacity end -- excess energy is wasted 100 | meta:set_float("energy",energy); 101 | meta:set_string("infotext", "(R) energy: " .. math.ceil(energy*10)/10 .. " / ".. capacity); 102 | minetest.sound_play("electric_zap", {pos=pos,gain=0.05,max_hear_distance = 8,}) 103 | end 104 | 105 | local full_coef = math.floor(energy/capacity*3); 106 | if capacity == 0 then full_coef = 0 end 107 | if full_coef > 2 then full_coef = 2 end 108 | minetest.swap_node(pos,{name = "basic_machines:battery_".. full_coef}) -- graphic energy 109 | 110 | return energy; -- new battery energy level 111 | end 112 | 113 | battery_upgrade = function(pos) 114 | local meta = minetest.get_meta(pos); 115 | local inv = meta:get_inventory(); 116 | local count1,count2;count1=0;count2=0; 117 | local stack,item,count; 118 | for i=1,4 do 119 | stack = inv:get_stack("upgrade", i);item = stack:get_name();count= stack:get_count(); 120 | if item == "default:mese" then 121 | count1=count1+count 122 | elseif item == "default:diamondblock" then 123 | count2=count2+count 124 | end 125 | end 126 | --if count11500 then count1 = 2*count1; count2=2*count2 end -- space increases efficiency 129 | 130 | 131 | meta:set_int("upgrade",count2); -- diamond for power 132 | -- adjust capacity 133 | --yyy 134 | local capacity = 3+3*count1; -- mese for capacity 135 | local maxpower = 1+count2*2; -- old 99 upgrade -> 200 power 136 | 137 | capacity = math.ceil(capacity*10)/10; 138 | local energy = 0; 139 | meta:set_float("capacity",capacity) 140 | meta:set_float("maxpower",maxpower) 141 | meta:set_float("energy",0) 142 | meta:set_string("infotext", "energy: " .. math.ceil(energy*10)/10 .. " / ".. capacity); 143 | end 144 | 145 | local machines_activate_furnace = minetest.registered_nodes["default:furnace"].on_metadata_inventory_put; -- this function will activate furnace 146 | 147 | minetest.register_node("basic_machines:battery_0", { 148 | description = "battery - stores energy, generates energy from fuel, can power nearby machines, or accelerate/run furnace above it. Its upgradeable.", 149 | tiles = {"basic_machine_outlet.png","basic_machine_battery.png","basic_machine_battery_0.png"}, 150 | groups = {cracky=3}, 151 | sounds = default.node_sound_wood_defaults(), 152 | 153 | after_place_node = function(pos, placer) 154 | local meta = minetest.get_meta(pos); 155 | 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"); 156 | meta:set_string("owner",placer:get_player_name()); 157 | local inv = meta:get_inventory();inv:set_size("fuel", 1*1); -- place to put crystals 158 | inv:set_size("upgrade", 2*2); 159 | meta:set_int("upgrade",0); -- upgrade level determines energy storage capacity and max energy output 160 | meta:set_float("capacity",3);meta:set_float("maxpower",1); 161 | meta:set_float("energy",0); 162 | end, 163 | 164 | effector = { 165 | action_on = function (pos, node,ttl) 166 | if type(ttl)~="number" then ttl = 1 end 167 | if ttl<0 then return end -- machines_TTL prevents infinite recursion 168 | 169 | local meta = minetest.get_meta(pos); 170 | local energy = meta:get_float("energy"); 171 | local capacity = meta:get_float("capacity"); 172 | local full_coef = math.floor(energy/capacity*3); 173 | 174 | -- try to power furnace on top of it 175 | if energy>=1 then -- need at least 1 energy 176 | pos.y=pos.y+1; local node = minetest.get_node(pos).name; 177 | 178 | if node== "default:furnace" or node=="default:furnace_active" then 179 | local fmeta = minetest.get_meta(pos); 180 | local fuel_totaltime = fmeta:get_float("fuel_totaltime") or 0; 181 | local fuel_time = fmeta:get_float("fuel_time") or 0; 182 | local t0 = meta:get_int("ftime"); -- furnace time 183 | local t1 = minetest.get_gametime(); 184 | 185 | if t1-t04 then -- accelerated cooking 193 | local src_time = fmeta:get_float("src_time") or 0 194 | energy = energy - 0.25*upgrade; -- use energy to accelerate burning 195 | 196 | 197 | 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 198 | 199 | fmeta:set_float("fuel_totaltime",60);fmeta:set_float("fuel_time",0) -- add 60 second burn time to furnace 200 | energy=energy-0.5; -- use up energy to add fuel 201 | 202 | -- make furnace start if not already started 203 | if node~="default:furnace_active" and machines_activate_furnace then machines_activate_furnace(pos) end 204 | -- update energy display 205 | end 206 | 207 | 208 | if energy<0 then 209 | energy = 0 210 | else -- only accelerate if we had enough energy, note: upgrade*0.1*0.25=1 then -- no need to recharge yet, will still work next time 219 | local full_coef_new = math.floor(energy/capacity*3); if full_coef_new>2 then full_coef_new = 2 end 220 | if capacity == 0 then full_coef_new = 0 end 221 | pos.y = pos.y-1; 222 | if full_coef_new ~= full_coef then minetest.swap_node(pos,{name = "basic_machines:battery_".. full_coef_new}) end 223 | return 224 | else 225 | local infotext = meta:get_string("infotext"); 226 | local new_infotext = "furnace needs at least 1 energy"; 227 | if new_infotext~=infotext then -- dont update unnecesarilly 228 | meta:set_string("infotext", new_infotext); 229 | pos.y=pos.y-1; -- so that it points to battery again! 230 | end 231 | end 232 | else 233 | pos.y=pos.y-1; 234 | end 235 | 236 | end 237 | 238 | -- try to recharge by converting inserted fuel/power cells into energy 239 | 240 | if energy2 then full_coef_new = 2 end 246 | if full_coef_new ~= full_coef then minetest.swap_node(pos,{name = "basic_machines:battery_".. full_coef_new}) end 247 | 248 | end 249 | }, 250 | 251 | on_rightclick = function(pos, node, player, itemstack, pointed_thing) 252 | local meta = minetest.get_meta(pos); 253 | local privs = minetest.get_player_privs(player:get_player_name()); 254 | if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler 255 | battery_update_meta(pos); 256 | end, 257 | 258 | on_receive_fields = function(pos, formname, fields, sender) 259 | if fields.quit then return end 260 | local meta = minetest.get_meta(pos); 261 | 262 | 263 | if fields.OK then battery_update_meta(pos) return end 264 | if fields.help then 265 | local name = sender:get_player_name(); 266 | local text = "Battery provides power to machines or furnace. It can either ".. 267 | "use power cells or convert ordinary furnace fuels into energy. 1 coal lump gives 1 energy.\n\n".. 268 | "UPGRADE with diamondblocks for more available power output or with ".. 269 | "meseblocks for more power storage capacity" 270 | 271 | local form = "size [6,7] textarea[0,0;6.5,8.5;help;BATTERY HELP;".. text.."]" 272 | minetest.show_formspec(name, "basic_machines:help_battery", form) 273 | end 274 | end, 275 | 276 | allow_metadata_inventory_put = function(pos, listname, index, stack, player) 277 | local meta = minetest.get_meta(pos); 278 | local privs = minetest.get_player_privs(player:get_player_name()); 279 | if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return 0 end 280 | return stack:get_count(); 281 | end, 282 | 283 | allow_metadata_inventory_take = function(pos, listname, index, stack, player) 284 | local meta = minetest.get_meta(pos); 285 | local privs = minetest.get_player_privs(player:get_player_name()); 286 | if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return 0 end 287 | return stack:get_count(); 288 | end, 289 | 290 | on_metadata_inventory_put = function(pos, listname, index, stack, player) 291 | if listname=="fuel" then 292 | battery_recharge(pos); 293 | battery_update_meta(pos); 294 | elseif listname == "upgrade" then 295 | battery_upgrade(pos); 296 | battery_update_meta(pos); 297 | end 298 | return stack:get_count(); 299 | end, 300 | 301 | on_metadata_inventory_take = function(pos, listname, index, stack, player) 302 | if listname == "upgrade" then 303 | battery_upgrade(pos); 304 | battery_update_meta(pos); 305 | end 306 | return stack:get_count(); 307 | end, 308 | 309 | allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) 310 | return 0; 311 | end, 312 | 313 | can_dig = function(pos) 314 | local meta = minetest.get_meta(pos); 315 | local inv = meta:get_inventory(); 316 | 317 | 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 318 | 319 | return true 320 | 321 | end 322 | 323 | }) 324 | 325 | 326 | 327 | -- GENERATOR 328 | 329 | local generator_update_meta = function(pos) 330 | local meta = minetest.get_meta(pos); 331 | local list_name = "nodemeta:"..pos.x..','..pos.y..','..pos.z 332 | 333 | local form = 334 | "size[8,6.5]" .. -- width, height 335 | "label[0,0;POWER CRYSTALS] ".."label[6,0;UPGRADE] ".. 336 | "label[1,1;UPGRADE LEVEL ".. meta:get_int("upgrade").." (generator)]".. 337 | "list["..list_name..";fuel;0.,0.5;1,1;]".. 338 | "list["..list_name..";upgrade;6.,0.5;2,1;]".. 339 | "list[current_player;main;0,2.5;8,4;]".. 340 | "button[4.5,1.5;1.5,1;OK;REFRESH]".. 341 | "button[6,1.5;1.5,1;help;help]".. 342 | "listring["..list_name..";fuel]".. 343 | "listring[current_player;main]".. 344 | "listring["..list_name..";upgrade]".. 345 | "listring[current_player;main]" 346 | meta:set_string("formspec", form) 347 | end 348 | 349 | 350 | 351 | generator_upgrade = function(pos) 352 | local meta = minetest.get_meta(pos); 353 | local inv = meta:get_inventory(); 354 | local stack,item,count; 355 | count = 0 356 | for i=1,2 do 357 | stack = inv:get_stack("upgrade", i);item = stack:get_name(); 358 | if item == "basic_machines:generator" then 359 | count= count + stack:get_count(); 360 | end 361 | end 362 | meta:set_int("upgrade",count); 363 | end 364 | 365 | --local genstat = {}; -- generator statistics for each player 366 | minetest.register_node("basic_machines:generator", { 367 | description = "Generator - very expensive, generates power crystals that provide power. Its upgradeable.", 368 | tiles = {"basic_machine_generator.png"}, 369 | groups = {cracky=3}, 370 | sounds = default.node_sound_wood_defaults(), 371 | after_place_node = function(pos, placer) 372 | 373 | --check to prevent too many generators being placed at one place 374 | if minetest.find_node_near(pos, 15, {"basic_machines:generator"}) then 375 | minetest.set_node(pos,{name="air"}) 376 | minetest.add_item(pos,"basic_machines:generator") 377 | minetest.chat_send_player(placer:get_player_name(),"#generator: interference from nearby generator detected.") 378 | return 379 | end 380 | 381 | local meta = minetest.get_meta(pos); 382 | meta:set_string("infotext","generator - generates power crystals that provide power. Upgrade with up to 50 generators."); 383 | meta:set_string("owner",placer:get_player_name()); 384 | local inv = meta:get_inventory(); 385 | inv:set_size("fuel", 1*1); -- here generated power crystals are placed 386 | inv:set_size("upgrade", 2*1); 387 | meta:set_int("upgrade",0); -- upgrade level determines quality of produced crystals 388 | 389 | end, 390 | 391 | on_rightclick = function(pos, node, player, itemstack, pointed_thing) 392 | local meta = minetest.get_meta(pos); 393 | local privs = minetest.get_player_privs(player:get_player_name()); 394 | if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return end -- only owner can interact with recycler 395 | generator_update_meta(pos); 396 | end, 397 | 398 | on_receive_fields = function(pos, formname, fields, sender) 399 | if fields.quit then return end 400 | if fields.help then 401 | 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\""; 402 | local form = "size [6,7] textarea[0,0;6.5,8.5;help;GENERATOR HELP;".. text.."]" 403 | minetest.show_formspec(sender:get_player_name(), "basic_machines:help_mover", form) 404 | return 405 | end 406 | local meta = minetest.get_meta(pos); 407 | generator_update_meta(pos); 408 | end, 409 | 410 | allow_metadata_inventory_put = function(pos, listname, index, stack, player) 411 | local meta = minetest.get_meta(pos); 412 | local privs = minetest.get_player_privs(player:get_player_name()); 413 | if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return 0 end 414 | return stack:get_count(); 415 | end, 416 | 417 | allow_metadata_inventory_take = function(pos, listname, index, stack, player) 418 | local meta = minetest.get_meta(pos); 419 | local privs = minetest.get_player_privs(player:get_player_name()); 420 | if minetest.is_protected(pos,player:get_player_name()) and not privs.privs then return 0 end 421 | return stack:get_count(); 422 | end, 423 | 424 | on_metadata_inventory_put = function(pos, listname, index, stack, player) 425 | if listname == "upgrade" then 426 | generator_upgrade(pos); 427 | generator_update_meta(pos); 428 | end 429 | return stack:get_count(); 430 | end, 431 | 432 | on_metadata_inventory_take = function(pos, listname, index, stack, player) 433 | if listname == "upgrade" then 434 | generator_upgrade(pos); 435 | generator_update_meta(pos); 436 | end 437 | return stack:get_count(); 438 | end, 439 | 440 | allow_metadata_inventory_move = function(pos, from_list, from_index, to_list, to_index, count, player) 441 | return 0; 442 | end, 443 | 444 | can_dig = function(pos) 445 | local meta = minetest.get_meta(pos); 446 | local inv = meta:get_inventory(); 447 | 448 | if not inv:is_empty("upgrade") then return false end -- fuel inv is not so important as generator generates it 449 | 450 | return true 451 | end, 452 | }) 453 | 454 | minetest.register_abm({ 455 | nodenames = {"basic_machines:generator"}, 456 | neighbors = {}, 457 | interval = 19, 458 | chance = 1, 459 | action = function(pos, node, active_object_count, active_object_count_wider) 460 | local meta = minetest.get_meta(pos); 461 | 462 | local upgrade = meta:get_int("upgrade"); 463 | local inv = meta:get_inventory(); 464 | local stack = inv:get_stack("fuel", 1); 465 | local crystal, text; 466 | 467 | if upgrade > 50 then meta:set_string("infotext","error: max upgrade is 50"); return end 468 | 469 | if upgrade >= 20 then 470 | crystal = "basic_machines:power_rod " .. math.floor(1+(upgrade-20)*9/178) 471 | text = "high upgrade: power rod"; 472 | elseif upgrade >=5 then 473 | crystal ="basic_machines:power_block " .. math.floor(1+(upgrade-5)*9/15); 474 | text = "medium upgrade: power block"; 475 | else 476 | crystal ="basic_machines:power_cell " .. math.floor(1+2*upgrade); 477 | text = "low upgrade: power cell"; 478 | end 479 | local morecrystal = ItemStack(crystal) 480 | stack:add_item(morecrystal); 481 | inv:set_stack("fuel", 1, stack) 482 | meta:set_string("infotext",text) 483 | end 484 | }) 485 | 486 | 487 | 488 | 489 | -- API for power distribution 490 | function basic_machines.check_power(pos, power_draw) -- mover checks power source - battery 491 | 492 | local batname = "basic_machines:battery"; 493 | if not string.find(minetest.get_node(pos).name,batname) then -- check with hashtables probably faster? 494 | return -1 -- battery not found! 495 | end 496 | 497 | local meta = minetest.get_meta(pos); 498 | local energy = meta:get_float("energy"); 499 | local capacity = meta:get_float("capacity"); 500 | local maxpower = meta:get_float("maxpower"); 501 | local full_coef = math.floor(energy/capacity*3); -- 0,1,2 502 | 503 | if power_draw>maxpower then 504 | meta:set_string("infotext", "Power draw required : " .. power_draw .. " maximum power output " .. maxpower .. ". Please upgrade battery") 505 | return 0; 506 | end 507 | 508 | if power_draw>energy then 509 | energy = battery_recharge(pos); -- try recharge battery and continue operation immidiately 510 | if not energy then return 0 end 511 | end 512 | 513 | energy = energy-power_draw; 514 | if energy<0 then 515 | meta:set_string("infotext", "used fuel provides too little power for current power draw ".. power_draw); 516 | return 0 517 | end -- recharge wasnt enough, needs to be repeated manually, return 0 power available 518 | meta:set_float("energy", energy); 519 | -- update energy display 520 | meta:set_string("infotext", "energy: " .. math.ceil(energy*10)/10 .. " / ".. capacity); 521 | 522 | local full_coef_new = math.floor(energy/capacity*3); if full_coef_new>2 then full_coef_new = 2 end 523 | if full_coef_new ~= full_coef then minetest.swap_node(pos,{name = "basic_machines:battery_".. full_coef_new}) end -- graphic energy level display 524 | 525 | return power_draw; 526 | 527 | end 528 | 529 | ------------------------ 530 | -- CRAFTS 531 | ------------------------ 532 | 533 | -- minetest.register_craft({ 534 | -- output = "basic_machines:battery", 535 | -- recipe = { 536 | -- {"","default:steel_ingot",""}, 537 | -- {"default:steel_ingot","default:mese","default:steel_ingot"}, 538 | -- {"","default:diamond",""}, 539 | 540 | -- } 541 | -- }) 542 | 543 | -- minetest.register_craft({ 544 | -- output = "basic_machines:generator", 545 | -- recipe = { 546 | -- {"","",""}, 547 | -- {"default:diamondblock","basic_machines:battery","default:diamondblock"}, 548 | -- {"default:diamondblock","default:diamondblock","default:diamondblock"} 549 | 550 | -- } 551 | -- }) 552 | 553 | minetest.register_craftitem("basic_machines:power_cell", { 554 | description = "Power cell - provides 1 power", 555 | inventory_image = "power_cell.png", 556 | stack_max = 25 557 | }) 558 | 559 | minetest.register_craftitem("basic_machines:power_block", { 560 | description = "Power block - provides 11 power", 561 | inventory_image = "power_block.png", 562 | stack_max = 25 563 | }) 564 | 565 | minetest.register_craftitem("basic_machines:power_rod", { 566 | description = "Power rod - provides 100 power", 567 | inventory_image = "power_rod.png", 568 | stack_max = 25 569 | }) 570 | 571 | -- various battery levels: 0,1,2 (2 >= 66%, 1 >= 33%,0>=0%) 572 | local batdef = {}; 573 | for k,v in pairs(minetest.registered_nodes["basic_machines:battery_0"]) do batdef[k] = v end 574 | 575 | for i = 1,2 do 576 | batdef.tiles[3] = "basic_machine_battery_" .. i ..".png" 577 | minetest.register_node("basic_machines:battery_"..i, batdef) 578 | end 579 | -------------------------------------------------------------------------------- /textures/basic_machine_battery.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_battery.png -------------------------------------------------------------------------------- /textures/basic_machine_battery_0.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_battery_0.png -------------------------------------------------------------------------------- /textures/basic_machine_battery_1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_battery_1.png -------------------------------------------------------------------------------- /textures/basic_machine_battery_2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_battery_2.png -------------------------------------------------------------------------------- /textures/basic_machine_clock_generator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_clock_generator.png -------------------------------------------------------------------------------- /textures/basic_machine_generator.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_generator.png -------------------------------------------------------------------------------- /textures/basic_machine_mover_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_mover_side.png -------------------------------------------------------------------------------- /textures/basic_machine_outlet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_outlet.png -------------------------------------------------------------------------------- /textures/basic_machine_side.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machine_side.png -------------------------------------------------------------------------------- /textures/basic_machines_ball.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machines_ball.png -------------------------------------------------------------------------------- /textures/basic_machines_dust.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machines_dust.png -------------------------------------------------------------------------------- /textures/basic_machines_stars.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/basic_machines_stars.png -------------------------------------------------------------------------------- /textures/charcoal.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/charcoal.png -------------------------------------------------------------------------------- /textures/compass_top.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/compass_top.png -------------------------------------------------------------------------------- /textures/constructor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/constructor.png -------------------------------------------------------------------------------- /textures/detector.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/detector.png -------------------------------------------------------------------------------- /textures/distributor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/distributor.png -------------------------------------------------------------------------------- /textures/enviro.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/enviro.png -------------------------------------------------------------------------------- /textures/grinder.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/grinder.png -------------------------------------------------------------------------------- /textures/keypad.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/keypad.png -------------------------------------------------------------------------------- /textures/light.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/light.png -------------------------------------------------------------------------------- /textures/light_off.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/light_off.png -------------------------------------------------------------------------------- /textures/machines_pos1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/machines_pos1.png -------------------------------------------------------------------------------- /textures/machines_pos11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/machines_pos11.png -------------------------------------------------------------------------------- /textures/machines_pos2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/machines_pos2.png -------------------------------------------------------------------------------- /textures/ore_extractor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/ore_extractor.png -------------------------------------------------------------------------------- /textures/pipeworks_autocrafter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/pipeworks_autocrafter.png -------------------------------------------------------------------------------- /textures/power_block.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/power_block.png -------------------------------------------------------------------------------- /textures/power_cell.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/power_cell.png -------------------------------------------------------------------------------- /textures/power_rod.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/power_rod.png -------------------------------------------------------------------------------- /textures/recycler.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ac-minetest/basic_machines/56164fb61a20a5ed0314feb18c69cbf0a172a37b/textures/recycler.png --------------------------------------------------------------------------------