* improves previous commit 3b5cbef6529040620dd8e064f04e082cab5bb436 and previous 3d_armor commit d7ae85b7f614ae4c37227c7d3a0c78013507ce4f , the problem is that both server and client must be equal protocol, so this ugly hack check if this was loaded into FinalMinetest or Multicraft to dinamically check client nature and re-registering the model when server and client do not have same player models due the colisionbox check * NOTE: the check already need to check if the engine is old and the server not, so the conditional just try to do exact check agains TRUE boolean. * Closed https://codeberg.org/minenux/minetest-game-minetest/issues/12 fixed the problem of collisionbox for the lufix player in ugly way but much more effectivelly due can be mixed in FinalMinetest and Multicraft * fix model preview based on client view, not on server engine version for the skindb mod
338 lines
9.0 KiB
Lua
338 lines
9.0 KiB
Lua
|
|
-- Simple Skins mod for minetest
|
|
-- Adds a simple skin selector to the inventory by using
|
|
-- the default sfinv or inventory_plus when running.
|
|
-- Released by TenPlus1 and based on Zeg9's code under MIT license
|
|
|
|
-- check for minetest 5.x compatibility
|
|
local is_54 = minetest.has_feature("direct_velocity_on_players") or nil
|
|
local is_50 = minetest.has_feature("object_use_texture_alpha") or nil
|
|
|
|
-- Load support for translation.
|
|
local S
|
|
|
|
if minetest.get_translator ~= nil then
|
|
S = minetest.get_translator("skins")
|
|
else
|
|
if minetest.get_modpath("intllib") then
|
|
dofile(minetest.get_modpath("intllib").."/init.lua")
|
|
if intllib.make_gettext_pair then
|
|
gettext, ngettext = intllib.make_gettext_pair() -- new gettext method
|
|
else
|
|
gettext = intllib.Getter() -- old text file method
|
|
end
|
|
S = gettext
|
|
else -- boilerplate function
|
|
S = function(str, ...)
|
|
local args = {...}
|
|
return str:gsub("@%d", function(match)
|
|
return args[tonumber(match:sub(2))]
|
|
end)
|
|
end
|
|
end
|
|
end
|
|
|
|
|
|
skins = {
|
|
skins = {}, list = {}, meta = {}, formspec = {},
|
|
modpath = minetest.get_modpath("skins"),
|
|
invplus = minetest.get_modpath("inventory_plus"),
|
|
unified_inventory = minetest.get_modpath("unified_inventory"),
|
|
default_skin_tab = false,
|
|
sfinv = minetest.get_modpath("sfinv"),
|
|
transparant_list = false,
|
|
id = 1,
|
|
file = minetest.get_worldpath() .. "/skins.mt",
|
|
preview = minetest.settings:get_bool("skins_preview") or true,
|
|
translate = S,
|
|
skin_limit = tonumber(minetest.settings:get("skins_limit")) or 160
|
|
}
|
|
|
|
|
|
-- check and use specific inventory
|
|
if skins.unified_inventory then
|
|
skins.transparant_list = true
|
|
dofile(skins.modpath .. "/unified_inventory.lua")
|
|
|
|
elseif skins.invplus then
|
|
skins.transparant_list = true
|
|
dofile(skins.modpath .. "/inventory_plus.lua")
|
|
|
|
elseif skins.sfinv then
|
|
skins.default_skin_tab = not skins.invplus and not skins.unified_inventory
|
|
dofile(skins.modpath .. "/sfinv.lua")
|
|
end
|
|
|
|
|
|
-- load skin list and metadata
|
|
local f, data, skin = 1
|
|
|
|
while skins.id <= skins.skin_limit do
|
|
|
|
skin = "character_" .. skins.id
|
|
|
|
-- does skin file exist ?
|
|
f = io.open(skins.modpath .. "/textures/" .. skin .. ".png")
|
|
|
|
-- escape loop if not found and remove last entry
|
|
if not f then
|
|
skins.list[skins.id] = nil
|
|
skins.id = skins.id - 1
|
|
break
|
|
end
|
|
|
|
f:close()
|
|
|
|
table.insert(skins.list, skin)
|
|
|
|
-- does metadata exist for that skin file ?
|
|
f = io.open(skins.modpath .. "/meta/" .. skin .. ".txt")
|
|
|
|
if f then
|
|
data = minetest.deserialize("return {" .. f:read("*all") .. "}")
|
|
f:close()
|
|
end
|
|
|
|
-- add metadata to list
|
|
skins.meta[skin] = {
|
|
name = data and data.name and data.name:gsub("[%p%c]", "") or "",
|
|
author = data and data.author and data.author:gsub("[%p%c]", "") or ""
|
|
}
|
|
|
|
skins.id = skins.id + 1
|
|
end
|
|
|
|
|
|
-- load player skins file for backwards compatibility
|
|
local input = io.open(skins.file, "r")
|
|
local data = nil
|
|
|
|
if input then
|
|
data = input:read("*all")
|
|
io.close(input)
|
|
end
|
|
|
|
if data and data ~= "" then
|
|
|
|
local lines = string.split(data, "\n")
|
|
|
|
for _, line in pairs(lines) do
|
|
data = string.split(line, " ", 2)
|
|
skins.skins[data[1]] = data[2]
|
|
end
|
|
end
|
|
|
|
|
|
-- create formspec for skin selection page
|
|
skins.formspec.main = function(name)
|
|
|
|
local formspec = "label[.5,2;" .. S("Select Player Skin:") .. "]"
|
|
.. "textlist[.5,2.5;6.8,6;skins_set;"
|
|
|
|
local meta
|
|
local selected = 1
|
|
|
|
for i = 1, #skins.list do
|
|
|
|
formspec = formspec .. skins.meta[ skins.list[i] ].name
|
|
|
|
if skins.skins[name] == skins.list[i] then
|
|
selected = i
|
|
meta = skins.meta[ skins.skins[name] ]
|
|
end
|
|
|
|
if i < #skins.list then
|
|
formspec = formspec ..","
|
|
end
|
|
end
|
|
|
|
if skins.transparant_list then
|
|
formspec = formspec .. ";" .. selected .. ";true]"
|
|
else
|
|
formspec = formspec .. ";" .. selected .. ";false]"
|
|
end
|
|
|
|
if meta then
|
|
|
|
if meta.name then
|
|
formspec = formspec .. "label[2,.5;" .. S("Name: ") .. meta.name .. "]"
|
|
end
|
|
|
|
if meta.author then
|
|
formspec = formspec .. "label[2,1;" .. S("Author: ") .. meta.author .. "]"
|
|
end
|
|
end
|
|
|
|
-- if preview enabled then add player model to formspec (5.4dev only)
|
|
if skins.preview == true then
|
|
info = minetest.get_player_information(name)
|
|
if info then
|
|
local test = info.version_string or "5"
|
|
if test:find("0.4.") or test:find("4.0.") or test:find("5.0.") or test:find("5.1.") or test:find("5.2.") or test:find("5.3.") then is_54 = false else is_54 = true end
|
|
end
|
|
if is_54 then
|
|
formspec = formspec .. "model[6,-0.2;1.5,3;player;character50.b3d;"
|
|
.. skins.skins[name] .. ".png;0,180;false;true]"
|
|
else
|
|
local head = "[combine:8x8:-8,-8="..skins.skins[name]..".png"
|
|
local body = "[combine:8x12:-20,-20="..skins.skins[name]..".png"
|
|
local left_leg = "[combine:4x12:-4,-20="..skins.skins[name]..".png"
|
|
local right_leg = "[combine:4x12:-4,-20="..skins.skins[name]..".png^[transformFX"
|
|
local left_hand = "[combine:4x12:-44,-20="..skins.skins[name]..".png"
|
|
local right_hand = "[combine:4x12:-44,-20="..skins.skins[name]..".png^[transformFX"
|
|
local back_head = "[combine:8x8:-24,-8="..skins.skins[name]..".png"
|
|
local body_back = "[combine:8x12:-32,-20="..skins.skins[name]..".png"
|
|
local legs_back = "[combine:4x12:-12,-20="..skins.skins[name]..".png"
|
|
local left_leg_back = "[combine:4x12:-12,-20="..skins.skins[name]..".png"
|
|
local right_leg_back = "[combine:4x12:-12,-20="..skins.skins[name]..".png^[transformFX"
|
|
local left_hand_back = "[combine:4x12:-52,-20="..skins.skins[name]..".png"
|
|
local right_hand_back = "[combine:4x12:-52,-20="..skins.skins[name]..".png^[transformFX"
|
|
formspec = formspec .. "image[3,1;2,2;".. head .. "] image[3,2.75;2,3;".. body .. "] image[3,5.35;1,3;".. left_leg .. "] image[3.82,5.35;1,3;".. right_leg .. "] image[2.2,2.75;1,3;".. left_hand .. "] image[4.63,2.75;1,3;".. right_hand .. "] image[7,1;2,2;".. back_head .. "] image[7,2.75;2,3;".. body_back .. "] image[7,5.35;1,3;".. left_leg_back .. "] image[7.82,5.35;1,3;".. right_leg_back .. "] image[6.2,2.75;1,3;".. left_hand_back .. "] image[8.63,2.75;1,3;".. right_hand_back .. "] image_button[0,1;2,2;"..head.."]"
|
|
end
|
|
end
|
|
|
|
return formspec
|
|
end
|
|
|
|
|
|
-- Read the image size from a PNG file. (returns width, height)
|
|
local PNG_HDR = string.char(0x89, 0x50, 0x4E, 0x47, 0x0D, 0x0A, 0x1A, 0x0A)
|
|
local function read_image_size(filename)
|
|
|
|
local f = io.open(filename, "rb")
|
|
|
|
if not f then return end
|
|
|
|
f:seek("set", 0x0)
|
|
|
|
local hdr = f:read(string.len(PNG_HDR))
|
|
|
|
if hdr ~= PNG_HDR then
|
|
f:close() ; return
|
|
end
|
|
|
|
f:seek("set", 0x13) ; local ws = f:read(1)
|
|
f:seek("set", 0x17) ; local hs = f:read(1)
|
|
f:close()
|
|
|
|
return ws:byte(), hs:byte()
|
|
end
|
|
|
|
|
|
-- update player skin
|
|
skins.update_player_skin = function(player)
|
|
|
|
if not player then
|
|
return
|
|
end
|
|
|
|
local name = player:get_player_name()
|
|
|
|
if minetest.get_modpath("player_api") then
|
|
player_api.set_textures(player, {skins.skins[name] .. ".png"})
|
|
else
|
|
default.player_set_textures(player, {skins.skins[name] .. ".png"})
|
|
end
|
|
end
|
|
|
|
|
|
skins.event_CHG = function(event, player)
|
|
|
|
local name = player:get_player_name()
|
|
local index = math.min(event.index, skins.id)
|
|
|
|
if not skins.list[index] then
|
|
return -- Do not update wrong skin number
|
|
end
|
|
|
|
skins.skins[name] = skins.list[index]
|
|
|
|
skins.update_player_skin(player)
|
|
|
|
if is_50 then
|
|
|
|
local meta = player:get_meta()
|
|
|
|
meta:set_string("skins:skin", skins.skins[name])
|
|
else
|
|
player:set_attribute("skins:skin", skins.skins[name])
|
|
end
|
|
end
|
|
|
|
|
|
-- load player skin on join
|
|
minetest.register_on_joinplayer(function(player)
|
|
|
|
local name = player:get_player_name() ; if not name then return end
|
|
local skin
|
|
|
|
if is_50 then
|
|
|
|
local meta = player:get_meta()
|
|
|
|
skin = meta:get_string("skins:skin")
|
|
else
|
|
skin = player:get_attribute("skins:skins")
|
|
end
|
|
|
|
-- do we already have a skin in player attributes?
|
|
if skin and skin ~= "" then
|
|
skins.skins[name] = skin
|
|
|
|
-- otherwise use skin from skins.mt file or default if not set
|
|
elseif not skins.skins[name] then
|
|
skins.skins[name] = "character_1"
|
|
end
|
|
|
|
skins.update_player_skin(player)
|
|
end)
|
|
|
|
|
|
-- admin command to set player skin (usually for custom skins)
|
|
minetest.register_chatcommand("setskin", {
|
|
params = "<player> <skin number>",
|
|
description = S("Admin command to set player skin"),
|
|
privs = {server = true},
|
|
func = function(name, param)
|
|
|
|
local playername, skin = string.match(param, "([^ ]+) (-?%d+)")
|
|
|
|
if not playername or not skin then
|
|
return false, S("** Insufficient or wrong parameters")
|
|
end
|
|
|
|
local player = minetest.get_player_by_name(playername)
|
|
|
|
if not player then
|
|
return false, S("** Player @1 not online!", playername)
|
|
end
|
|
|
|
-- this check is only used when custom skins aren't in use
|
|
-- if not skins.list[tonumber(skin)] then
|
|
-- return false, S("** Invalid skin number (max value is @1)", id)
|
|
-- end
|
|
|
|
skins.skins[playername] = "character_" .. tonumber(skin)
|
|
|
|
skins.update_player_skin(player)
|
|
|
|
if is_50 then
|
|
|
|
local meta = player:get_meta()
|
|
|
|
meta:set_string("skins:skin", skins.skins[playername])
|
|
else
|
|
player:set_attribute("skins:skin", skins.skins[playername])
|
|
end
|
|
|
|
minetest.chat_send_player(playername,
|
|
S("Your skin has been set to") .. " character_" .. skin)
|
|
|
|
return true, "** " .. playername .. S("'s skin set to")
|
|
.. " character_" .. skin .. ".png"
|
|
end,
|
|
})
|
|
|
|
|
|
print ("[MOD] skins (Simple Skins) loaded")
|