-- -- formspec_anim SSCSM helper -- -- Copyright © 2019 by luk3yx -- License: https://git.minetest.land/luk3yx/formspec_anim/src/branch/master/LICENSE.md -- local open_formname, open_formspec, elapsed, next_frame local function cleanup() open_formname, open_formspec, elapsed, next_frame = nil, nil, nil, nil end local function cmd(data) minetest.run_server_chatcommand('formspec_anim', '\001' .. data) end local function send_formspec_input(formname, fields) assert(not formname:find('\001', nil, true)) cmd(formname .. '\001' .. minetest.write_json(fields)) end minetest.register_on_formspec_input(function(formname, fields) if formname:sub(1, 14) ~= 'formspec_anim:' then return end if fields.quit then cleanup() end send_formspec_input(formname:sub(15), fields) return true end) -- (Re)draw the formspec. This uses a primitive pattern that works with edge -- cases because the formspec has been interpreted by formspec_ast on the -- server. -- Note that duration is in seconds here, it gets converted server-side. local p = '(image%[[^;]+;[^;]+;)\\%[\001formspec_anim\001:([^,]+)([%+%.])' .. '\\,([^,]+)\\,([^\001]*)\001' local function redraw() next_frame = math.huge local formspec = open_formspec:gsub(p, function(prefix, count, one_shot, duration, texture) count, duration = tonumber(count), tonumber(duration) local step = math.floor(elapsed / duration) if one_shot == '.' then -- One-shot step = math.min(step + 1, count) if step < count then next_frame = math.min(next_frame, step * duration) end else -- Loop next_frame = math.min(next_frame, (step + 1) * duration) step = (step % count) + 1 end -- Numbers are casted to strings automatically. return prefix .. texture .. step end) minetest.show_formspec('formspec_anim:' .. open_formname, formspec) end -- Draw the next frame when ready minetest.register_globalstep(function(dtime) if not open_formname then return end elapsed = elapsed + dtime if elapsed >= next_frame then redraw() end end) -- Handle formspec opens local sscsm_msg_prefix = '\001FORMSPEC_ANIM\001' minetest.register_on_receiving_chat_message(function(message) if message:sub(1, #sscsm_msg_prefix) ~= sscsm_msg_prefix then return end message = message:sub(#sscsm_msg_prefix + 1) if message == '' then cleanup() return true end local s, e = message:find('\001', nil, true) if not s or not e then return end local formname = message:sub(1, s - 1) if formname ~= open_formname then elapsed = 0 open_formname = formname end open_formspec = message:sub(e + 1) redraw() return true end) cmd('OK')