entity_anim/init.lua

107 lines
4.4 KiB
Lua

local TEST = false
local function get_file_dimensions(path)
local f = io.open(path, "rb")
f:seek("set", 1)
if f:read(3) == "PNG" then
f:seek("set", 16)
return {
w = tonumber(string.format("0x%x%x%x%x", f:read(4):byte(1, 4))),
h = tonumber(string.format("0x%x%x%x%x", f:read(4):byte(1, 4))),
}
end
end
local function find_textures(path, files)
files = files or {}
for _, filename in pairs(minetest.get_dir_list(path, false)) do
if filename:sub(-4) == ".png" then
files[filename] = get_file_dimensions(path .. "/" .. filename)
end
end
for _, dir in pairs(minetest.get_dir_list(path, true)) do
files = find_textures(path .. "/" .. dir, files)
end
return files
end
-- List of dimensions for all textures
local texturelist = {}
for _, modname in pairs(minetest.get_modnames()) do
texturelist = find_textures(minetest.get_modpath(modname), texturelist)
end
local old_dynamic_add = minetest.dynamic_add_media
minetest.dynamic_add_media = function(filepath, ...)
local success = old_dynamic_add(filepath, ...)
if success then texturelist[filepath:match("[^/\\]+$")] = get_file_dimensions(filepath) end
return success
end
-- Animation handler
minetest.register_globalstep(function(dtime)
for _, ent in pairs(minetest.luaentities) do
-- _tiles and properties must exist
if ent._tiles and ent.object:get_properties() then
local textures = ent.object:get_properties().textures
local tiles = table.copy(ent._tiles)
for i, tile in pairs(tiles) do
if type(tile) == "table" then
if tile.animation then
local anim = tile.animation
assert(anim.type and (anim.type == "vertical_frames" or anim.type == "sheet_2d"), ("Invalid animated tile type in entity '%s'."):format(ent.name))
tiles[i]._timer = (tile._timer or 0) + dtime
tiles[i]._frame = tile._frame or 0
local size = texturelist[tile.name] or {w = 16, h = 16}
if anim.type == "vertical_frames" then
assert(anim.aspect_w and anim.aspect_h and anim.length, ("Invalid vertical_frames animation definition in entity '%s'."):format(ent.name))
-- height / frame height (width / aspect ratio)
local total_frames = size.h / (size.w * (anim.aspect_h / anim.aspect_w))
-- floor(timer / seconds per frame)
tile._frame = math.floor(tile._timer / (anim.length / total_frames)) % total_frames
tile._timer = tile._timer % anim.length
textures[i] = ("%s^[verticalframe:%s:%s"):format(tile.name, total_frames, tile._frame)
else -- type == "sheet_2d"
assert(anim.frames_w and anim.frames_h and anim.frame_length, ("Invalid sheet_2d animation definition in entity '%s'."):format(ent.name))
local total_frames = anim.frames_w * anim.frames_h
local tile_w, tile_h = size.w / anim.frames_w, size.h / anim.frames_h
tile._frame = math.floor((tile._timer / anim.frame_length)) % total_frames
tile._timer = tile._timer % (total_frames * anim.frame_length)
textures[i] = ("[combine:%sx%s:%s,%s=%s"):format(tile_w, tile_h, -(tile._frame % anim.frames_w) * tile_h, -math.floor(tile._frame / anim.frames_w) * tile_w, tile.name)
end
else
textures[i] = tile.name
end
else
textures[i] = tile
end
end
ent._tiles = tiles
ent.object:set_properties({textures = textures})
end
end
end)
-- Test
if TEST then
local speed = 1
minetest.register_entity("entity_anim:verticalframes", {
_tiles = {{name = "entity_anim_test_verticalframes.png", animation = {type = "vertical_frames", aspect_w = 1, aspect_h = 1, length = speed}}}
})
minetest.register_entity("entity_anim:sheet2d", {
_tiles = {{name = "entity_anim_test_sheet2d.png", animation = {type = "sheet_2d", frames_w = 2, frames_h = 2, frame_length = speed / 4}}}
})
end