-- LUALOCALS < --------------------------------------------------------- local ItemStack, ipairs, math, minetest, nodecore, pairs, string, tonumber, tostring, type, unpack, vector = ItemStack, ipairs, math, minetest, nodecore, pairs, string, tonumber, tostring, type, unpack, vector local math_abs, math_cos, math_floor, math_log, math_pi, math_pow, math_random, math_sin, math_sqrt, string_gsub, string_lower = math.abs, math.cos, math.floor, math.log, math.pi, math.pow, math.random, math.sin, math.sqrt, string.gsub, string.lower -- LUALOCALS > --------------------------------------------------------- for k, v in pairs(minetest) do if type(v) == "function" then -- Late-bind in case minetest methods overridden. nodecore[k] = function(...) return minetest[k](...) end else nodecore[k] = v end end local function underride(t, u, u2, ...) if u2 then underride(u, u2, ...) end for k, v in pairs(u) do if t[k] == nil then t[k] = v elseif type(t[k]) == "table" and type(v) == "table" then underride(t[k], v) end end return t end nodecore.underride = underride function nodecore.mkreg() local t = {} local f = function(x) t[#t + 1] = x end return f, t end function nodecore.memoize(func) local cache return function() if cache then return cache[1] end cache = {func()} return cache[1] end end function nodecore.dirs() return { {n = "e", x = 1, y = 0, z = 0}, {n = "w", x = -1, y = 0, z = 0}, {n = "u", x = 0, y = 1, z = 0}, {n = "d", x = 0, y = -1, z = 0}, {n = "n", x = 0, y = 0, z = 1}, {n = "s", x = 0, y = 0, z = -1} } end function nodecore.pickrand(tbl, weight) weight = weight or function() end local t = {} local max = 0 for k, v in pairs(tbl) do local w = weight(v) or 1 if w > 0 then max = max + w t[#t + 1] = {w = w, k = k, v = v} end end if max <= 0 then return end max = math_random() * max for _, v in ipairs(t) do max = max - v.w if max <= 0 then return v.v, v.k end end end do local saved function nodecore.boxmuller() local old = saved if old then saved = nil return old end local r = math_sqrt(-2 * math_log(math_random())) local t = 2 * math_pi * math_random() saved = r * math_sin(t) return r * math_cos(t) end end function nodecore.exporand(mean) local r = 0 while r == 0 do r = math_random() end return math_floor(-math_log(r) * (mean + 0.5)) end function nodecore.extend_item(name, func) local orig = minetest.registered_items[name] or {} local copy = {} for k, v in pairs(orig) do copy[k] = v end copy = func(copy, orig) or copy minetest.register_item(":" .. name, copy) end function nodecore.fixedbox(x, ...) return {type = "fixed", fixed = { x or {-0.5, -0.5, -0.5, 0.5, 0.5, 0.5}, ... }} end function nodecore.interact(player) if not player then return end if type(player) ~= "string" then if not (player.is_player and player:is_player()) then return true end player = player:get_player_name() end return minetest.get_player_privs(player).interact end function nodecore.player_visible(player) if type(player) == "string" then player = minetest.get_player_by_name(player) end if not player then return end local vs = player:get_properties().visual_size return vs.x > 0 and vs.y > 0 end function nodecore.wieldgroup(who, group) local wielded = who and who:get_wielded_item() local nodedef = minetest.registered_nodes[wielded:get_name()] if nodedef then return nodedef.groups and nodedef.groups[group] end local caps = wielded and wielded:get_tool_capabilities() return caps and caps.groupcaps and caps.groupcaps[group] end function nodecore.toolspeed(what, groups) if not what then return end local dg = what:get_tool_capabilities().groupcaps local t for gn, lv in pairs(groups) do local gt = dg[gn] gt = gt and gt.times gt = gt and gt[lv] if gt and (not t or t > gt) then t = gt end end if (not t) and (not what:is_empty()) then return nodecore.toolspeed(ItemStack(""), groups) end return t end function nodecore.tool_digs(what, groups) local s = nodecore.toolspeed(what, groups) return s and s <= 4 end function nodecore.interval(after, func) local function go() minetest.after(after, go) return func() end minetest.after(after, go) end function nodecore.wear_wield(player, groups, qty) local wielded = player:get_wielded_item() if wielded then local wdef = wielded:get_definition() local tp = wielded:get_tool_capabilities() local dp = minetest.get_dig_params(groups, tp) if wdef and wdef.after_use then wielded = wdef.after_use(wielded, player, nil, dp) or wielded else if not minetest.settings:get_bool("creative_mode") then wielded:add_wear(dp.wear * (qty or 1)) if wielded:get_count() <= 0 and wdef.sound and wdef.sound.breaks then minetest.sound_play(wdef.sound.breaks, {object = player, gain = 0.5}) end end end return player:set_wielded_item(wielded) end end function nodecore.consume_wield(player, qty) local wielded = player:get_wielded_item() if wielded then local wdef = wielded:get_definition() if wdef.stack_max > 1 and qty then local have = wielded:get_count() - qty if have <= 0 then wielded = ItemStack("") else wielded:set_count(have) end end return player:set_wielded_item(wielded) end end function nodecore.loaded_mods() local t = {} for _, v in pairs(minetest.get_modnames()) do t[v] = true end return t end function nodecore.node_group(name, pos, node) node = node or minetest.get_node(pos) local def = minetest.registered_nodes[node.name] or {} return def.groups and def.groups[name] end function nodecore.item_eject(pos, stack, speed, qty, vel) stack = ItemStack(stack) speed = speed or 0 vel = vel or {x = 0, y = 0, z = 0} if speed == 0 and vel.x == 0 and vel.y == 0 and vel.z == 0 and nodecore.place_stack and minetest.get_node(pos).name == "air" then stack:set_count(stack:get_count() * (qty or 1)) return nodecore.place_stack(pos, stack) end for _ = 1, (qty or 1) do local v = {x = vel.x, y = vel.y, z = vel.z} if speed > 0 then local inc = math_random() * math_pi / 3 local y = math_sin(inc) local xz = math_cos(inc) local theta = math_random() * math_pi * 2 local x = math_sin(theta) * xz local z = math_cos(theta) * xz v = { x = v.x + x * speed, y = v.y + y * speed, z = v.z + z * speed } end local p = {x = pos.x, y = pos.y + 0.25, z = pos.z} local obj = minetest.add_item(p, stack) if obj then obj:set_velocity(v) end end end do local stddirs = {} for _, v in pairs(nodecore.dirs()) do if v.y <= 0 then stddirs[#stddirs + 1] = v end end function nodecore.item_disperse(pos, name, qty, outdirs) if qty < 1 then return end local dirs = {} for _, d in pairs(outdirs or stddirs) do local p = vector.add(pos, d) if nodecore.buildable_to(p) then dirs[#dirs + 1] = {pos = p, qty = 0} end end if #dirs < 1 then return nodecore.item_eject(pos, name .. " " .. qty) end for _ = 1, qty do local p = dirs[math_random(1, #dirs)] p.qty = p.qty + 1 end for _, v in pairs(dirs) do if v.qty > 0 then nodecore.item_eject(v.pos, name .. " " .. v.qty) end end end end function nodecore.find_nodes_around(pos, spec, r, s) r = r or 1 if type(r) == "number" then return minetest.find_nodes_in_area( {x = pos.x - r, y = pos.y - r, z = pos.z - r}, {x = pos.x + r, y = pos.y + r, z = pos.z + r}, spec) end s = s or r return minetest.find_nodes_in_area( {x = pos.x - (r.x or r[1]), y = pos.y - (r.y or r[2]), z = pos.z - (r.z or r[3])}, {x = pos.x + (s.x or s[1]), y = pos.y + (s.y or s[2]), z = pos.z + (s.z or s[3])}, spec) end function nodecore.quenched(pos, r) local qty = #nodecore.find_nodes_around(pos, "group:coolant", r) return (qty > 0) and qty or nil end function nodecore.node_spin_custom(...) local arr = {...} arr[0] = false local lut = {} for i = 1, #arr do lut[arr[i - 1]] = arr[i] end lut[arr[#arr]] = arr[1] local qty = #arr return function(pos, node, clicker, itemstack) node = node or minetest.get_node(pos) node.param2 = lut[node.param2] or lut[false] if clicker:is_player() then minetest.log(clicker:get_player_name() .. " spins " .. node.name .. " at " .. minetest.pos_to_string(pos) .. " to param2 " .. node.param2 .. " (" .. qty .. " total)") end minetest.swap_node(pos, node) nodecore.node_sound(pos, "place") local def = minetest.registered_items[node.name] or {} if def.on_spin then def.on_spin(pos, node) end return itemstack end end function nodecore.node_spin_filtered(func) local rots = {} for i = 0, 23 do local f = nodecore.facedirs[i] local hit for j = 1, #rots do if not hit then local o = nodecore.facedirs[rots[j]] hit = hit or func(f, o) end end if not hit then rots[#rots + 1] = f.id end end return nodecore.node_spin_custom(unpack(rots)) end function nodecore.node_change(pos, node, newname) if node.name == newname then return end return minetest.set_node(pos, underride({name = newname}, node)) end local function scrubkey(s) return string_lower(string_gsub(tostring(s), "%W+", "_")) end function nodecore.rate_adjustment(...) local rate = 1 local key = scrubkey(nodecore.product) for _, k in ipairs({...}) do if not k then break end key = key .. "_" .. scrubkey(k) local adj = tonumber(minetest.settings:get(key)) if adj then rate = rate * adj end end return rate end function nodecore.obstructed(minpos, maxpos) if not maxpos then maxpos = {x = minpos.x + 0.5, y = minpos.y + 0.5, z = minpos.z + 0.5} minpos = {x = minpos.x - 0.5, y = minpos.y - 0.5, z = minpos.z - 0.5} end local avgpos = vector.multiply(vector.add(minpos, maxpos), 0.5) local radius = 4 + vector.distance(minpos, maxpos) / 2 for _, obj in pairs(minetest.get_objects_inside_radius(avgpos, radius)) do local op = obj:get_pos() local cb = obj:get_properties().collisionbox if maxpos.x > op.x + cb[1] and minpos.x < op.x + cb[4] and maxpos.y > op.y + cb[2] and minpos.y < op.y + cb[5] and maxpos.z > op.z + cb[3] and minpos.z < op.z + cb[6] then local lua = obj.get_luaentity and obj:get_luaentity() if not ((lua and lua.is_stack) or (not nodecore.interact(obj)) or (not nodecore.player_visible(obj))) then return obj end end end end local gravity = tonumber(minetest.settings:get("movement_gravity")) or 9.81 local friction = tonumber(minetest.settings:get("nodecore_air_friction")) or 0.0004 local function air_accel_factor(v) local q = (friction * v * v) * 2 - 1 return q > 0 and q or 0 end function nodecore.grav_air_physics_player(v) return 1 - air_accel_factor(v.y) end local function air_accel_net(v) return v == 0 and 0 or v / -math_abs(v) * gravity * air_accel_factor(v) end function nodecore.grav_air_accel(v) return { x = air_accel_net(v.x), y = air_accel_net(v.y) - gravity, z = air_accel_net(v.z) } end function nodecore.grav_air_accel_ent(obj) local cur = obj:get_acceleration() local new = nodecore.grav_air_accel(obj:get_velocity()) if vector.equals(cur, new) then return end return obj:set_acceleration(new) end function nodecore.near_unloaded(pos, radius) return minetest.find_node_near(pos, radius or 1, {"ignore"}, true) end function nodecore.get_objects_at_pos(pos) pos = vector.round(pos) local t = {} for _, obj in pairs(minetest.get_objects_inside_radius(pos, 0.866025403784)) do if vector.equals(vector.round(obj:get_pos()), pos) then t[#t + 1] = obj end end return t end function nodecore.inventory_dump(player) local pos = player:get_pos() pos.y = pos.y + player:get_properties().eye_height local inv = player:get_inventory() for lname, list in pairs(inv:get_lists()) do if lname ~= "hand" then for slot, stack in pairs(list) do if not (stack:is_empty() or nodecore and nodecore.item_is_virtual(stack)) then nodecore.item_eject(pos, stack, 0.001) inv:set_stack(lname, slot, "") end end end end end function nodecore.get_depth_light(y, qty) qty = qty or 4/5 if y < 0 then qty = qty * math_pow(2, y / 64) end return qty end nodecore.light_sun = 15 nodecore.light_sky = math_floor(0.5 + nodecore.light_sun * nodecore.get_depth_light(0)) function nodecore.is_full_sun(pos) return minetest.get_node_light(pos) == nodecore.light_sun end function nodecore.get_node_light(pos) local artificial = minetest.get_node_light(pos, 0) local natural = math_floor(0.5 + minetest.get_node_light(pos, 0.5) * nodecore.get_depth_light(pos.y)) return artificial > natural and artificial or natural end