-- =================================================================== ocutil = {} -- =================================================================== -- This function is from the Lua "sort" docs. You can use it as fol- -- lows to iterate through a dictionary in sorted ascending key order: -- -- local lines = { some dictionary data } -- for name, line in ocutil.pairsByKeys (lines) do -- print (name, line) -- end ocutil.pairsByKeys = function (t, f) local a = {} for n in pairs (t) do table.insert (a, n) end table.sort (a, f) local i = 0 local iter = function() i = i+1 if a [i] == nil then return nil else return a [i], t [a [i]] end end return iter end -- =================================================================== -- "tsmods" is used by "resolve_nodename". It's a mod list that's in- -- itialized after all mods are loaded. local tsmods = {} -- "hgcfirst" is true if, and only if, "tsmods" hasn't been initializ- -- ed yet. local hgcfirst = true -- "mbvirtual" is a list of mods that may, or may not, be virtual on- -- ly. local mbvirtual = { "aluminum" , "dessert" , "dogtreats" , "emeralds" , "extra" , "food" , "gloopblocks" , "glow" , "glowcrystals" , "glowstone" , "grassblock" , "lmb" , "lmbb" , "modernpack" , "myretro" , "oboe" , "ocean" , "prefab" , "quartz" , "snowball" , "titanium" , "toys" , "weapons" , "xdecor" , "xpanes" , } -- =================================================================== ocutil.fixnil = function (s) if s == nil then s = "(nil)" elseif s == "" then s = "(empty)" end return s end -- =================================================================== ocutil.log = function (s) minetest.log ("action", s) end -- =================================================================== ocutil.fatal_error = function (s) minetest.log ("action", "Fatal Error: " .. s) os.exit (1) return nil -- Shouldn't be reached end -- =================================================================== ocutil.panic = function (s) minetest.log ("action", "Internal Error: " .. s) os.exit (1) return nil -- Shouldn't be reached end -- =================================================================== ocutil.str_empty = function (x) if x == nil or x == "" then return true else return false end end -- =================================================================== ocutil.str_nonempty = function (x) if x == nil or x == "" then return false else return true end end -- =================================================================== ocutil.table_empty = function (tab) local next = next if next (tab) == nil then return true end return false end -- =================================================================== ocutil.table_nonempty = function (tab) if ocutil.table_empty (tab) then return false end return true end -- =================================================================== ocutil.str_contains = function (a, b) if string.match (a, b) then return true else return false end end -- =================================================================== ocutil.str_omits = function (a, b) if string.match (a, b) then return false else return true end end -- =================================================================== -- This function checks a string for an explicit false (or equivalent) -- setting (as opposed to empty or nil). ocutil.str_false = function (x) if x == "false" or x == "no" or x == "off" or x == "0" or x == "nope" or x == "nyet" or x == false or x == 0 then return true else return false end end -- =================================================================== -- This function checks a string for an explicit true (or equivalent) -- setting (as opposed to empty or nil). ocutil.str_true = function (x) if x == "true" or x == "yes" or x == "on" or x == "1" or x == true or x == 1 then return true else return false end end -- =================================================================== ocutil.bool_default = function (flagname, defvalue) if defvalue == nil then defvalue = false end local x = minetest.setting_get (flagname) if x ~= nil then if ocutil.str_false (x) then return false end if ocutil.str_true (x) then return true end end return defvalue end ocutil.bool_setting = ocutil.bool_default -- =================================================================== ocutil.any_bool_setting = function (flaglist, defvalue) if defvalue == nil then defvalue = false end for _, flagname in ipairs (flaglist) do local x = minetest.setting_get (flagname) if x ~= nil then if ocutil.str_false (x) then return false end if ocutil.str_true (x) then return true end end end return defvalue end -- =================================================================== ocutil.setting_empty = function (name) local x = minetest.setting_get (name) if x == nil or x == "" then return true end return false end -- =================================================================== ocutil.setting_nonempty = function (name) local x = minetest.setting_get (name) if x ~= nil and x ~= "" then return true end return false end -- =================================================================== ocutil.starts_with = function (String, Start) if string.sub (String, 1, string.len (Start)) == Start then return true else return false end end -- =================================================================== ocutil.not_starts_with = function (String, Start) if string.sub (String, 1, string.len (Start)) == Start then return false else return true end end -- =================================================================== ocutil.starts_with_any_of = function (String, StartList) for _, str in ipairs (StartList) do if string.sub (String, 1, string.len (str)) == str then return true end end return false end -- =================================================================== ocutil.firstch = function (str) return string.sub (str, 1, 1) end -- =================================================================== ocutil.first_to_upper = function (str) return (str:gsub ("^%l", string.upper)) end -- =================================================================== ocutil.all_first_to_upper = function (str, cvtspace) str = str:gsub ("^%l", string.upper) str = str:gsub ("[_ ]%l", function (a) return string.upper (a) end) if ocutil.str_true (cvtspace) then str = str:gsub ("_", " ") end return (str) end -- =================================================================== ocutil.strtok = function (source, delimitch) if delimitch == nil then delimitch = " " end local parts = {} local pattern = '([^' .. delimitch .. ']+)' string.gsub (source, pattern, function (value) value = value:gsub ("^ *", "") value = value:gsub (" *$", "") parts [#parts + 1] = value end) return parts end -- =================================================================== ocutil.round = function (n) local x = n % 1 >= 0.5 and math.ceil (n) or math.floor (n) if x == -0 then x = 0 end return x end -- =================================================================== ocutil.swap_key_value = function (itable) if itable == nil then return nil end local otable = {} for key, value in pairs (itable) do otable [value] = key end return otable end -- =================================================================== ocutil.ktable_to_vtable = function (ktable) local vtable = {} if ktable == nil then return vtable end for key, _ in pairs (ktable) do table.insert (vtable, key) end table.sort (vtable) return vtable end -- =================================================================== ocutil.vtable_to_ktable = function (vtable, set_to) local ktable = {} if vtable == nil then return ktable end if set_to == nil then set_to = true end for _, value in pairs (vtable) do ktable [value] = set_to end return ktable end -- =================================================================== local cache_modpath = {} ocutil.get_modpath = function (modname) local result = cache_modpath [modname] if result ~= nil then return result end result = minetest.get_modpath (modname) cache_modpath [modname] = result return result end -- =================================================================== ocutil.entity_exists = function (foo) if minetest.registered_entities [foo] == nil then return false else return true end end -- =================================================================== ocutil.entity_missing = function (foo) if minetest.registered_entities [foo] == nil then return true else return false end end -- =================================================================== ocutil.item_exists = function (foo) if minetest.registered_items [foo] == nil then return false else return true end end -- =================================================================== ocutil.item_missing = function (foo) if minetest.registered_items [foo] == nil then return true else return false end end -- =================================================================== ocutil.mod_exists = function (modname) if ocutil.get_modpath (modname) then return true end return false end -- =================================================================== ocutil.mod_missing = function (modname) if ocutil.get_modpath (modname) then return false end return true end -- =================================================================== ocutil.node_exists = function (foo) if minetest.registered_nodes [foo] == nil then return false else return true end end -- =================================================================== ocutil.node_missing = function (foo) if minetest.registered_nodes [foo] == nil then return true else return false end end -- =================================================================== ocutil.tool_exists = function (foo) if minetest.registered_tools [foo] == nil then return false else return true end end -- =================================================================== ocutil.tool_missing = function (foo) if minetest.registered_tools [foo] == nil then return true else return false end end -- =================================================================== ocutil.thing_exists = function (thing) if thing == nil or thing == "" then return false end thing = minetest.registered_aliases [thing] or thing if ocutil.item_exists (thing) or ocutil.node_exists (thing) or ocutil.tool_exists (thing) or ocutil.entity_exists (thing) or ocutil.starts_with (thing, "group:") then return true else return false end end -- =================================================================== ocutil.thing_missing = function (thing) if ocutil.thing_exists (thing) then return false else return true end end -- =================================================================== ocutil.has_group = function (name, group) local n = minetest.get_item_group (name, group) if n and n ~= 0 then return true else return false end end -- =================================================================== ocutil.safe_register_alias = function (foo, bar) if foo == bar then return end if minetest.registered_items [foo] == nil and minetest.registered_nodes [foo] == nil and minetest.registered_tools [foo] == nil then bar = minetest.registered_aliases [bar] or bar if minetest.registered_items [bar] ~= nil or minetest.registered_nodes [bar] ~= nil or minetest.registered_tools [bar] ~= nil then minetest.register_alias (foo, bar) end end end -- =================================================================== ocutil.safe_register_item = function (itemname, def) if def == nil then def = {} end if ocutil.starts_with (itemname, ":") then itemname = string.gsub (itemname, ":", "", 1) end if ocutil.thing_missing (itemname) then itemname = ":" .. itemname if def.inventory_image == nil and def.image ~= nil and type (def.image) == "string" and ocutil.str_nonempty (def.image) then def.inventory_image = def.image end minetest.register_craftitem (itemname, def) end end -- =================================================================== ocutil.safe_register_node = function (nodename, def) if ocutil.starts_with (nodename, ":") then nodename = string.gsub (nodename, ":", "", 1) end if ocutil.thing_missing (nodename) then nodename = ":" .. nodename if def.inventory_image == nil and def.image ~= nil and type (def.image) == "string" and ocutil.str_nonempty (def.image) then def.inventory_image = def.image end minetest.register_node (nodename, def) end end -- =================================================================== ocutil.safe_register_tool = function (toolname, def) if ocutil.starts_with (toolname, ":") then itemname = string.gsub (toolname, ":", "", 1) end if ocutil.thing_missing (toolname) then toolname = ":" .. toolname if def.inventory_image == nil and def.image ~= nil and type (def.image) == "string" and ocutil.str_nonempty (def.image) then def.inventory_image = def.image end minetest.register_tool (toolname, def) end end -- =================================================================== ocutil.ends_with = function (String, End) return End == '' or string.sub (String, -string.len (End)) == End end -- =================================================================== ocutil.numset = function (name) return tonumber (minetest.setting_get (name)) end ocutil.numsetting = ocutil.numset -- =================================================================== ocutil.numsetzero = function (name) return (tonumber (minetest.setting_get (name))) or 0 end -- =================================================================== -- This function makes a set out of a list. Usage: -- -- local color_set = ocutil.makeset { "red", "green", "blue" } -- if color_set ["red"] ~= nil then print ("Supported color") end ocutil.make_set = function (list) local set = {} for _, l in ipairs (list) do set [l] = true end return set end ocutil.makeset = ocutil.make_set -- =================================================================== ocutil.pos_exists = function (pos) if pos == nil then return false end local inf = 1/0 if pos.x ~= nil and pos.y ~= nil and pos.z ~= nil and pos.x == pos.x and pos.y == pos.y and pos.z == pos.z and pos.x ~= inf and pos.y ~= inf and pos.z ~= inf and tonumber (pos.x) ~= nil and tonumber (pos.y) ~= nil and tonumber (pos.z) ~= nil then return true end return false end -- =================================================================== ocutil.pos_missing = function (pos) if ocutil.pos_exists (pos) then return false end return true end -- =================================================================== ocutil.velocity_exists = ocutil.pos_exists ocutil.velocity_missing = ocutil.pos_missing -- =================================================================== ocutil.pos_to_str = function (pos) return pos.x .. "," .. pos.y .. "," .. pos.z end -- =================================================================== ocutil.is_water = function (uppos) local upname = minetest.get_node (uppos).name if (upname == "default:water_flowing" or upname == "default:water_source" or upname == "default:water_gel" or upname == "codersea:water_flowing" or upname == "codersea:water_source") then return true end return false end -- =================================================================== ocutil.clone_table = function (original) if original == nil then return nil end local copy = {} for k, v in pairs (original) do if type(v) == 'table' then v = ocutil.clone_table (v) end copy [k] = v end return copy end ocutil.table_clone = ocutil.clone_table -- =================================================================== ocutil.global_exists = minetest.global_exists or function (name) return (_G [name] ~= nil) end -- =================================================================== ocutil.file_exists = function (path) local file, err file, err = io.open (path, "rb") if err then return false end file:close() return true end -- =================================================================== ocutil.file_missing = function (path) if ocutil.file_exists (path) then return false end return true end -- =================================================================== ocutil.lua_exists = function (modname, luafile) local modpath = ocutil.get_modpath (modname) if not modpath then return false end local luapath = modpath .. "/" .. luafile return ocutil.file_exists (luapath) or ocutil.file_exists (luapath .. ".lua") end -- =================================================================== ocutil.lua_missing = function (modname, luafile) if ocutil.lua_exists (modname, luafile) then return false end return true end -- =================================================================== ocutil.model_exists = function (modname, m3dfile) local modpath = ocutil.get_modpath (modname) if not modpath then return false end local m3dpath = modpath .. "/models/" .. m3dfile return ocutil.file_exists (m3dpath) end -- =================================================================== ocutil.model_missing = function (modname, m3dfile) if ocutil.model_exists (modname, m3dfile) then return false end return true end -- =================================================================== ocutil.mod_texture_exists = function (modname, imgfile) local modpath = ocutil.get_modpath (modname) if not modpath then return false end local imgpath = modpath .. "/textures/" .. imgfile return ocutil.file_exists (imgpath) end ocutil.mod_image_exists = ocutil.mod_texture_exists -- =================================================================== ocutil.mod_texture_missing = function (modname, imgfile) if ocutil.mod_texture_exists (modname, imgfile) then return false else return true end end ocutil.mod_image_missing = ocutil.mod_texture_missing -- =================================================================== ocutil.mod_texture_missing = function (modname, imgfile) if ocutil.mod_texture_exists (modname, imgfile) then return false else return true end end -- =================================================================== ocutil.loadwe = function (pos, path) if not ocutil.get_modpath ("worldedit") or not ocutil.global_exists ("worldedit") or not worldedit.deserialize then return end local file, err file, err = io.open (path, "rb") if err then return end local value = file:read ("*a") file:close() local version = worldedit.read_header (value) if version == 0 then return end worldedit.deserialize (pos, value) end -- =================================================================== ocutil.resolve_nodename = function (nodename) -- =================================================================== -- Initialize "tsmods". if hgcfirst then hgcfirst = false tsmods = minetest.get_modnames() -- Check "default" mod first table.insert (tsmods, 1, "default") -- Support mods that may be virtual only -- Note: These should be appended as opposed to prepended for _, modname in ipairs (mbvirtual) do table.insert (tsmods, modname) end end -- =================================================================== if not string.match (nodename, ":") and core.registered_items [nodename] == nil and core.registered_nodes [nodename] == nil then for _,tsmod in ipairs (tsmods) do local tsnode = tsmod .. ":" .. nodename if core.registered_items [tsnode] ~= nil or core.registered_nodes [tsnode] ~= nil then nodename = tsnode break end end end return nodename end -- =================================================================== -- "ocutil.register_node_bucketable" is part of the Bucket API al- -- though it's defined in the "default" mod. -- ------------------------------------------------------------------- -- 1. This routine takes the same parameters as "minetest.register_ -- node" with two special cases: -- 1a. The second, or table, parameter may include an optional element -- named "bucket_base". The interpretation of "bucket_base" is ex- -- plained further down. -- 1b. The "tiles" element of the parameter table must be a list whose -- first element specifies a PNG image file as opposed to, for exam- -- ple, a lower-level list. -- ------------------------------------------------------------------- -- 2. This routine passes its parameters to "minetest.register_node" -- and so registers a specified node type. -- ------------------------------------------------------------------- -- 3. Subsequently, this routine adds the node type to an internal -- list of nodes that should be "bucketable"; i.e., diggable and pla- -- ceable by bucket. -- If the "bucket" mod has not yet been loaded, the list is processed -- by "bucket.empty_autobucket" after "bucket" has been loaded. -- If the "bucket" mod has been loaded, this routine calls "bucket. -- empty_autobucket" immediately. -- ------------------------------------------------------------------- -- 4. Assume that this routine is called to register a new node type. -- If the "bucket_base" parameter is provided, in a given call, the -- node name of the bucket that holds a node of the registered type is -- "bucket:" plus the value of "bucket_base". -- For example, suppose that this routine is called to register a node -- type named "dinosaur:salad". If "bucket_base" is "dinosalad", the -- node name of a bucket that contains "dinosaur:salad" will be -- "bucket:dinosalad". -- ------------------------------------------------------------------- -- 5. If the "bucket_base" parameter is *not* provided, a node name is -- created for the bucket automatically using the full name of the -- registered type as a base. -- For example, suppose that this routine is called to register -- "dinosaur:salad" and that "bucket_base" isn't specified. In this -- case, the node name of a bucket that contains "dinosaur:salad" will -- be "bucket:dinosaur_salad". -- ------------------------------------------------------------------- -- 6. It's recommended that this routine be used to register only -- node types that are powdery, soft, or breakable by hand. -- The reason is that, otherwise, buckets will become the equivalent -- of super-powered picks. -- If the "bucket" mod is revised to support expensive buckets with -- limited lifespans, the rule wouldn't need to apply to buckets of -- those types. -- ------------------------------------------------------------------- -- 7. This routine shouldn't be used to register water- or lava- based -- node types. -- To register water-based node types, use the Water API; in particul- -- ar, the routine "default.register_water". -- To register lava-based node types, extend the code in "default/ -- nodes_lava.lua". -- ------------------------------------------------------------------- -- 8. You don't need to use this routine to register a solid node that -- is bucket-compatible. You can call "minetest.register_node" and -- "bucket.register_bucket_hash" separately. -- For an example of this approach, see the "Bucket O'Cheese" code in -- the "coderblocks" mod. -- =================================================================== default.autobucket = {} ocutil.register_node_bucketable = function (name, def) minetest.register_node (name, def) local abc = def.bucket_base if abc == nil then abc = string.gsub (name, ":", "_") end default.autobucket [name] = { source_node = name , bucket_node = "bucket:" .. abc , invsource_image = def.tiles [1] , desc = def.description .. " Bucket" , } if ocutil.bucket_loaded ~= nil and bucket ~= nil and bucket.empty_autobucket ~= nil then bucket.empty_autobucket() end end -- =================================================================== ocutil.register_furnace_cooking = function (output, input) if ocutil.thing_exists (output) and ocutil.thing_exists (input ) then minetest.register_craft ({ type = "cooking" , output = output , recipe = input , }) end end -- =================================================================== ocutil.is_nil_pos = function (pos) if pos == nil or pos.x == nil or pos.y == nil or pos.z == nil then return true end return false end -- =================================================================== ocutil.register_furnace_fuel = function (recipe, burntime) if ocutil.thing_exists (recipe) then minetest.register_craft ({ type = "fuel" , recipe = recipe , burntime = burntime , }) end end -- =================================================================== ocutil.nodename_light_source = function (nodename) local node = minetest.registered_nodes [nodename] if node == nil or node.paramtype == nil or node.paramtype ~= "light" then return nil end local ls = node.light_source if ls == nil then return nil end ls = tonumber (ls) if ls == nil or ls < 1 then return nil end return ls end -- =================================================================== ocutil.get_sopos = function (self) if self == nil or self.object == nil then return nil end if self.is_destroyed then return nil end local sopos = self.object:get_pos() if ocutil.is_nil_pos (sopos) then self.is_destroyed = true self.object:remove() return nil end return sopos end -- =================================================================== ocutil.add_item_groups = function (item, new_groups) local mra = minetest.registered_aliases local mri = minetest.registered_items item = mra [item] or item if mri [item] ~= nil then local groups = mri [item].groups if groups == nil then groups = {} end groups = ocutil.clone_table (groups) for key,value in pairs (new_groups) do groups [key] = value end minetest.override_item (item, { groups = groups }) end end -- =================================================================== ocutil.on_mods_loaded = function (mfunc) if minetest.register_on_mods_loaded ~= nil then minetest.register_on_mods_loaded (mfunc) end end -- =================================================================== ocutil.get_content_id = function (nodename) local results, id = pcall (minetest.get_content_id, nodename) if not results then return nil end return id end -- =================================================================== ocutil.png_exists = function (modname, skinpng) return ocutil.mod_texture_exists (modname, skinpng) end -- =================================================================== ocutil.png_missing = function (modname, skinpng) return ocutil.mod_texture_missing (modname, skinpng) end -- =================================================================== ocutil.get_png_dim = function (w) local file = io.open (w) if file then file:seek ("set", 16) local widthstr, heightstr = file:read (4), file:read (4) local width = widthstr:sub (1,1):byte() * 16777216 + widthstr:sub (2,2):byte() * 65536 + widthstr:sub (3,3):byte() * 256 + widthstr:sub (4,4):byte() local height = heightstr:sub (1,1):byte() * 16777216 + heightstr:sub (2,2):byte() * 65536 + heightstr:sub (3,3):byte() * 256 + heightstr:sub (4,4):byte() file:close() return width, height end end -- =================================================================== ocutil.combine_images = function (destination, source) local result = "" if destination ~= nil then result = destination end if source ~= nil then if result ~= "" then result = result .. "^" end result = result .. source end return result end -- =================================================================== -- In "ocutil.modpath_from_filename": -- This routine takes a filename without path component as a parame- -- ter. It checks to see if the filename starts with an MT mod name -- (such as "anvil" or "3d_armor") followed by an underscore. The mod -- name itself may include underscores but can't start or end with an -- underscore. -- If this is the case, i.e., if the filename starts with an MT mod -- name followed by an underscore, this routine returns the absolute -- pathname for the mod's directory without a trailing slash. -- If this isn't the case, this routine returns nil. -- Original author: Poikilos -- =================================================================== ocutil.modpath_from_filename = function (filename) if filename == nil then return nil end -- Minimum value needs to be 2 to prevent possible invalid 0-index local underscore_i = 2 while underscore_i < #filename do underscore_i = filename:find ("_", underscore_i) if underscore_i ~= nil then local this_mod_name = filename:sub (1, underscore_i - 1) local this_mod_path = minetest.get_modpath (this_mod_name) if this_mod_path ~= nil then return this_mod_path end -- Go past "_" to find the next "_" underscore_i = underscore_i + 1 else break end end end -- =================================================================== -- In "ocutil.match_any_pattern": -- "haystack" is an arbitrary string and "needles" is a list of Lua -- patterns. If the string matches one or more of the patterns, this -- routine returns the index (1+) in "needles" of the first matching -- pattern in the list. Otherwise, this routine returns nil. -- =================================================================== ocutil.match_any_pattern = function (haystack, needles) for found_i, expression in pairs (needles) do if haystack:match (expression) ~= nil then return found_i end end return nil end -- =================================================================== -- In "ocutil.table_contains": -- "haystacks" is a list of arbitrary strings and "needle" is a Lua -- patterns. If one of the strings in "haystacks" matches the pat- -- tern, this routine returns true. Otherwise, this routine returns -- false. -- =================================================================== function ocutil.table_contains (haystacks, needle) for _, element in pairs (haystacks) do if element == needle then return true end end return false end -- =================================================================== -- In "ocutil.sort_first": -- -- The 1st parameter, "items", is an integer-indexed string list. -- -- The 2nd parameter, "pattern", is a Lua pattern (often just an al- -- phanumeric string but it may use meta-characters). -- This routine moves entries in the list that match the pattern to -- the start of the list and returns the resulting list. Note: The -- original list isn't affected. -- The order of the entries, matching or not matching, isn't changed -- otherwise. -- Original author: Poikilos -- =================================================================== ocutil.sort_first = function (items, pattern) if items == nil then return nil end local results = {} for use_index, this_str in ipairs (items) do if this_str:find (pattern) ~= nil then results [#results + 1] = this_str end end for use_index, this_str in ipairs (items) do if this_str:find (pattern) == nil then results [#results + 1] = this_str end end return results end -- =================================================================== -- In "ocutil.sort_last": -- -- The 1st parameter, "items", is an integer-indexed string list. -- -- The 2nd parameter, "pattern", is a Lua pattern (often just an al- -- phanumeric string but it may use meta-characters). -- This routine moves entries in the list that match the pattern to -- the end of the list. The order of the entries, matching or not mat- -- ching, isn't changed otherwise. -- This routine moves entries in the list that match the pattern to -- the end of the list and returns the resulting list. Note: The orig- -- inal list isn't affected. -- The order of the entries, matching or not matching, isn't changed -- otherwise. -- Original author: Poikilos -- =================================================================== ocutil.sort_last = function (items, pattern) if items == nil then return nil end local results = {} for use_index, this_str in ipairs (items) do if this_str:find (pattern) == nil then results [#results+1] = this_str -- minetest.log ("warning", " - " .. this_str) end end for use_index, this_str in ipairs (items) do if this_str:find (pattern) ~= nil then results [#results+1] = this_str -- minetest.log ("warning", " - " .. this_str) end end return results end -- =================================================================== -- This function takes two parameters: -- -- mesh_count -- # of meshes in some 3D model (B3D, OBJ, etc.). -- texture -- There are two possible modes for "texture" as ex- -- -- plained below. -- This function returns a table in one of two possible formats de- -- pending on the nature of "texture". -- 1. "texture" may be a single string. In this case, (a) it's a tex- -- ture image-file name and (b) the returned table is simply a list of -- "mesh_count" copies of the string. -- In this case, the idea is that each mesh in the model is going to -- be assigned the same texture. -- 2. "texture" may be a list of single-element sub-lists of texture -- image-file names. For example: -- -- { -- { "horse1body.png" } , { "horse2body.png" } , ... -- } -- In this case, the returned table is the same as the input but the -- single-element sub-lists are each expanded to "mesh_count" copies -- of the string in each of those sub-lists. For example, the sample -- input indicated right above would produce the following output tab- -- le: -- -- { -- { "horse1body.png", "horse1body.png", ... } , -- { "horse2body.png", "horse2body.png", ... } , -- ... -- } -- #1 and #2 above can both be used for 3D models that have just one -- material ID. -- In the case of #2, the usual interpretation of the input is that -- the different image files provide possible variations of the tex- -- ture associated with the material ID. -- How, exactly, the returned table is used will depend on the APIs in -- the game or other framework involved. However, the point of both -- modes is to ensure that there are sufficient textures to cover all -- of the meshes in a particular model file. -- In Minetest, #1 and #2 will work with "objects" but not with -- "nodes". -- Original author: Poikilos -- =================================================================== ocutil.multiply_texture = function (texture, mesh_count) if not texture then error("You must provide a texture string or table of variants.") end if not mesh_count then error( "You must provide a mesh_count" .. " (The number of meshes in the model file)." .. " See the traceback below for what file called" .. " multiply_texture incorrectly." ) end local multi_mesh_texture = {} if type(texture) == "table" then -- It is a list-like table of variant texture filenames. local texture_filename = nil for variant_i = 1,#texture,1 do multi_mesh_texture[variant_i] = {} texture_filename = texture[variant_i] if type(texture_filename) == "table" then texture_filename = texture_filename[1] -- ^ In case the mobs-style nested table was passed, -- copy the single texture for each variant. end for multimesh_texture_i = 1,mesh_count,1 do multi_mesh_texture [variant_i] [multimesh_texture_i] = texture_filename end end else -- It's a single texture filename string. for multimesh_texture_i = 1, mesh_count, 1 do multi_mesh_texture [multimesh_texture_i] = texture end end return multi_mesh_texture end ocutil.multiply_textures = ocutil.multiply_texture ocutil.multtext = ocutil.multiply_texture -- =================================================================== -- End of file.