-- Enable Minetest forever. minetest = core firefly = { -- Set to `true` to enable testing admin things without having to open a server. singleplayer_admin = true } local ns = firefly function include(file) return dofile(minetest.get_modpath(minetest.get_current_modname()).."/"..file) end function enum(cases) local out = {} local i = 0 for _, x in ipairs(cases) do out[x] = i i = i +1 end return out end say = minetest.chat_send_all function extend(dst, src) for k, v in pairs(src) do dst[k] = v end return dst end Promise = { resolve = function(e, ...) if e.resolved then return end e.resolved = true if e._after then e._after(...) end end, after = function(e, fn) e._after = fn end } Promise.__index = Promise setmetatable(Promise, { __call = function(_, fn) local e = {resolved = false} setmetatable(e, Promise) fn(function(...) e:resolve(...) end) return e end }) EventTarget = { init = function() local e = { listeners = {} } return setmetatable(e, EventTarget) end, listen = function(e, channel, fn) if not e.listeners[channel] then e.listeners[channel] = {} end local l = e.listeners[channel] l[#l +1] = fn end, unlisten = function(e, channel, fn) if not e.listeners[channel] then return end local l = e.listeners[channel] local idx = table.indexof(l, fn) if idx < 0 then return end table.remove(l, idx) end, dispatch = function(e, channel, ...) local l = e.listeners[channel] if not l then return end for i = 1, #l do l[i](...) end end } EventTarget.__index = EventTarget setmetatable(EventTarget, { __call = function(_, ...) return EventTarget.init(...) end }) -- HACK: Lookup table for getting a rotation from a -- facedir (because Minetest doesn't have any way -- to get this information normally). local facedir_rotations = { -- +Y [0] = vector.new(0, 0, 0), [1] = vector.new(0, math.pi * 1.5, 0), [2] = vector.new(0, math.pi * 1.0, 0), [3] = vector.new(0, math.pi * 0.5, 0), -- +Z [4] = vector.new(math.pi * 1.5, 0, 0), [5] = vector.new(0, math.pi * 1.5, math.pi * 1.5), [6] = vector.new(math.pi * 0.5, math.pi * 1.0, 0), [7] = vector.new(0, math.pi * 0.5, math.pi * 0.5), -- -Z [8] = vector.new(math.pi * 0.5, 0, 0), [9] = vector.new(0, math.pi * 1.5, math.pi * 0.5), [10] = vector.new(math.pi * 1.5, math.pi * 1.0, 0), [11] = vector.new(0, math.pi * 0.5, math.pi * 1.5), -- +X [12] = vector.new(0, 0, math.pi * 0.5), [13] = vector.new(math.pi * 1.5, math.pi * 1.5, 0), [14] = vector.new(0, math.pi * 1.0, math.pi * 1.5), [15] = vector.new(math.pi * 0.5, math.pi * 0.5, 0), -- -X [16] = vector.new(0, 0, math.pi * 1.5), [17] = vector.new(math.pi * 0.5, math.pi * 1.5, 0), [18] = vector.new(0, math.pi * 1.0, math.pi * 0.5), [19] = vector.new(math.pi * 1.5, math.pi * 0.5, 0), -- -Y [20] = vector.new(0, 0, math.pi * 1.0), [21] = vector.new(0, math.pi * 0.5, math.pi * 1.0), [22] = vector.new(0, math.pi * 1.0, math.pi * 1.0), [23] = vector.new(0, math.pi * 1.5, math.pi * 1.0), } function ns.facedir_to_rotation(facedir) return facedir_rotations[facedir] or minetest.facedir_to_dir(facedir):dir_to_rotation() end function ns.rotate_selectionbox(box, rot) local a = vector.new(box[1], box[2], box[3]):rotate(rot) local b = vector.new(box[4], box[5], box[6]):rotate(rot) return { a.x, a.y, a.z, b.x, b.y, b.z } end function ns.register_item(name, def) local needs_alias if not name:find ":" then def._name = name name = "firefly:"..name needs_alias = true end minetest.register_craftitem(":"..name, def) if needs_alias then minetest.register_alias(def._name, name) end end function ns.solid_color_frames(frames) local out = "[fill:1x"..#frames..":0,0:#000" for i, x in ipairs(frames) do out = out.."^[fill:1x3:0,"..(i -1)..":"..x end return out end local digit_widths = { [0] = 7, 6, 7, 7, 7, 7, 7, 7, 7, 7, } function ns.texture_from_number(num) local w = 0 local digits = {} repeat w = w +1 digits[w] = num %10 num = math.floor(num /10) until num <= 0 local width = 0 local out = "" for i = w, 1, -1 do out = out..":"..width..",1=firefly_number_"..digits[i]..".png" width = width +digit_widths[digits[i]] +(i > 1 and 2 or 0) end return "[combine:"..width.."x16"..out, width end function ns.read_file(path) local f = io.open(path) if not f then return false end local out = f:read("a") f:close() return out end function ns.manhattan_distance(a, b) return math.abs(b.x -a.x) +math.abs(b.y -a.y) +math.abs(b.z -a.z) end minetest.register_lbm { name = ":firefly:on_load", nodenames = {"group:call_on_load"}, action = function(pos, node) minetest.registered_nodes[node.name].on_load(pos) end } ns.timer = EventTarget() local last_time = minetest.get_us_time() minetest.register_globalstep(function() local time = minetest.get_us_time() if time -last_time > 1000000 then ns.timer:dispatch("every_second") last_time = time end end) minetest.register_entity(":display", { initial_properties = { visual = "sprite", textures = {"blank.png"}, pointable = false, static_save = false }, on_activate = function(e) e.object:set_armor_groups{immortal = 1} end }) if minetest.get_modpath("testtools") then minetest.override_item("testtools:param2tool", { pointabilities = { nodes = {["group:everything"] = true}, objects = {["group:immortal"] = false} } }) end