--[[ Ore spawners let player craft ore spawneres and get back lumps from ingots. Copyright (C) 2016 - 2023 SaKeL This library is free software; you can redistribute it and/or modify it pos the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to juraj.vajda@gmail.com --]] -- main tables spawners_ores = {} local percent = 10 -- how often node timers for minerals will tick, +/- some random value function spawners_ores.tick(pos) minetest.get_node_timer(pos):start(math.random(30, 60)) end -- how often a growth failure tick is retried (e.g. not enough place to spawn minerals) function spawners_ores.tick_short(pos) minetest.get_node_timer(pos):start(math.random(15, 20)) end -- adds smoke particles function spawners_ores.add_effects(pos, radius) minetest.add_particlespawner({ amount = 32, time = 2, minpos = vector.subtract({ x = pos.x, y = pos.y + 1, z = pos.z }, radius / 2), maxpos = vector.add({ x = pos.x, y = pos.y + 1, z = pos.z }, radius / 2), minvel = { x = -0.5, y = 3, z = -0.5 }, maxvel = { x = 0.5, y = 10, z = 0.5 }, minacc = vector.new(), maxacc = vector.new(), minexptime = 0.5, maxexptime = 2, minsize = 0.5, maxsize = 8, texture = 'spawners_ores_smoke_particle.png^[transform' .. math.random(0, 3) }) end -- start spawning ores function spawners_ores.start_spawning_ores(pos, ore_name, how_many) if not pos or not ore_name then return end local _how_many = how_many or 1 for i = 1, _how_many do if i > 1 then pos = spawners_ores.get_available_node(pos, 'default:stone') if not pos then return end minetest.sound_play('spawners_ores_strike', { pos = pos, max_hear_distance = 16, gain = 10, }) minetest.set_node(pos, { name = ore_name }) spawners_ores.add_effects(pos, 1) else minetest.sound_play('spawners_ores_strike', { pos = pos, max_hear_distance = 16, gain = 10, }) minetest.set_node(pos, { name = ore_name }) spawners_ores.add_effects(pos, 1) end end end function spawners_ores.get_available_node(pos, check_node, radius) if not pos then return end local _check_node = check_node or 'default:stone' local _radius = radius or 2 local node_ore_pos = minetest.find_node_near(pos, _radius, { _check_node }) if node_ore_pos and node_ore_pos ~= nil then return node_ore_pos else return false end end -- build form for spawners function spawners_ores.get_formspec(pos, table) -- Inizialize metadata and variables local _table = table or {} local meta = minetest.get_meta(pos) local mineral = _table.ore or meta:get_string('mineral') local ingot = _table.ore or meta:get_string('mineral') local status = meta:get_string('status') local inv = meta:get_inventory() local stack = inv:get_stack('fuel', 1) -- fix different Ingot namings if ingot == 'iron' then ingot = 'steel' end -- add extra ores based on percentage local stack_per_obj = spawners_ores.stack_per({ stack_count = stack:get_count(), percent = percent }) local stack_per = stack_per_obj.stack_per or 0 local extra_per = stack_per_obj.extra_per or 0 local colorize = { gold = '^[colorize:#ffe4008C', tin = '^[colorize:#d0d0d08C', iron = '^[colorize:#b66d498C', copper = '^[colorize:#b587528C', } local colorize_arrow = '' local button = '' if status == 'active' then colorize_arrow = colorize[mineral] elseif status == 'waiting' then button = 'button[1.8,2.5;2,1.5;restart;RE-START]' end -- dynamic formspec local formspec = 'size[8,8.5]' .. default.gui_bg .. default.gui_bg_img .. default.gui_slots .. 'label[1.8,0.3;Input ' .. ingot .. ' Ingot]' .. 'list[current_name;fuel;1.8,1;1,1;]' .. 'image[1.8,1;1,1;spawners_ores_ingot_slot.png]' .. 'list[current_player;main;0,4.25;8,1;]' .. 'list[current_player;main;0,5.5;8,3;8]' .. 'image[2.9,1;1,1;gui_furnace_arrow_bg.png^[transformR270' .. colorize_arrow .. ']' .. 'label[1.8,2;' .. stack_per .. ' minerals (' .. extra_per .. ' extra)]' .. 'image[4,1;1,1;spawners_ores_stone_with_' .. mineral .. '.png]' .. button .. 'listring[current_name;fuel]' .. 'listring[current_player;main]' .. default.get_hotbar_bg(0, 4.25) meta:set_string('formspec', formspec) -- infotext local infotext = mineral .. ' fuel: ' .. inv:get_stack('fuel', 1):get_count() if inv:is_empty('fuel') then infotext = mineral .. ' ore spawner is empty.' elseif status == 'waiting' then infotext = 'Waiting - no default:stone was found near by, ' .. mineral .. ' fuel: ' .. inv:get_stack('fuel', 1):get_count() end meta:set_string('infotext', infotext) end -- check if is fuel empty in the node function spawners_ores.can_dig(pos, player) local meta = minetest.get_meta(pos); local inv = meta:get_inventory() return inv:is_empty('fuel') end -- add extra percentage on top of the stack function spawners_ores.stack_per(table) local _table = table or {} local stack_count = _table.stack_count or nil local extra_per = (stack_count / 100) * percent extra_per = math.floor(extra_per) local stack_per = extra_per + stack_count -- print(percent .. '% from stack(' .. stack_count .. ') = ' .. stack_per .. ' (' .. extra_per .. ' extra ore(s))') return { stack_per = stack_per, extra_per = extra_per } end -- set status and update formspec/infotext function spawners_ores.on_timer(pos, elapsed) local available_node = spawners_ores.get_available_node(pos, 'default:stone') local meta = minetest.get_meta(pos) local inv = meta:get_inventory() local stack = inv:get_stack('fuel', 1) local how_many = 1 local ore_name = meta:get_string('ore_name') local status = meta:get_string('status') local tick = meta:get_int('tick') -- -- active -- if available_node and inv:is_empty('fuel') ~= true then -- make sure the right node status is shown if status ~= 'active' then meta:set_string('status', 'active') spawners_ores.get_formspec(pos) minetest.swap_node(pos, { name = 'spawners_ores:' .. ore_name .. '_spawner_active' }) end -- start spawning only on proper 'tick' if tick ~= 0 then if stack:get_count() % percent == 0 then -- TODO: should get countent based on 'percent' how_many = 2 end -- enough place to spawn more ores spawners_ores.start_spawning_ores(available_node, 'default:' .. ore_name, how_many) -- take fuel stack:take_item() inv:set_stack('fuel', 1, stack) spawners_ores.get_formspec(pos) end -- dont wait for proper 'tick' and change state after 1 sec. local next_available_node = spawners_ores.get_available_node(pos, 'default:stone') if inv:is_empty('fuel') or next_available_node == false then minetest.get_node_timer(pos):start(1.0) return end if tick == 0 then -- start new proper timer meta:set_int('tick', 1) end -- start new proper timer spawners_ores.tick(pos) -- -- default -- elseif inv:is_empty('fuel') then -- empty / no fuel -- stop timer -- make sure that default status/node is shown meta:set_string('status', '') minetest.swap_node(pos, { name = 'spawners_ores:' .. ore_name .. '_spawner' }) spawners_ores.get_formspec(pos) return -- -- waiting -- else -- stop timer and show 'refresh' button in order to start timer again -- make sure that waiting status/node is shown if status ~= 'waiting' then meta:set_string('status', 'waiting') minetest.swap_node(pos, { name = 'spawners_ores:' .. ore_name .. '_spawner_waiting' }) end spawners_ores.get_formspec(pos) return end end