Merge pull request 'Add support for external custom skins mod' (#3653) from skins into master

Reviewed-on: https://git.minetest.land/MineClone2/MineClone2/pulls/3653
Reviewed-by: ancientmarinerdev <ancientmariner_dev@proton.me>
This commit is contained in:
ancientmarinerdev 2023-05-26 20:55:53 +00:00
commit 019717cab0
28 changed files with 276 additions and 228 deletions

@ -158,8 +158,10 @@ minetest.register_on_player_inventory_action(function(player, action, inventory,
end) end)
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
mcl_player.player_set_model(player, "mcl_armor_character.b3d")
player:get_inventory():set_size("armor", 5) player:get_inventory():set_size("armor", 5)
if not minetest.global_exists("mcl_skins") then
mcl_player.player_set_model(player, "mcl_armor_character.b3d")
end
minetest.after(1, function() minetest.after(1, function()
if player:is_player() then if player:is_player() then

@ -2,4 +2,4 @@ name = mcl_meshhand
author = jordan4ibanez author = jordan4ibanez
description = Applies the player skin texture to the hand. description = Applies the player skin texture to the hand.
depends = mcl_tools, mcl_player depends = mcl_tools, mcl_player
optional_depends = mcl_skins optional_depends = mcl_skins, mcl_custom_skins

@ -28,28 +28,6 @@ function mcl_player.player_register_model(name, def)
models[name] = def models[name] = def
end end
-- Default player appearance
mcl_player.player_register_model("character.b3d", {
animation_speed = 30,
textures = {"character.png", },
animations = {
-- Standard animations.
stand = {x= 0, y= 79},
lay = {x=162, y=166},
walk = {x=168, y=187},
mine = {x=189, y=198},
walk_mine = {x=200, y=219},
sit = {x= 81, y=160},
sneak_stand = {x=222, y=302},
sneak_mine = {x=346, y=366},
sneak_walk = {x=304, y=323},
sneak_walk_mine = {x=325, y=344},
run_walk = {x=440, y=460},
run_walk_mine = {x=461, y=481},
sit_mount = {x=484, y=484},
},
})
-- Player stats and animations -- Player stats and animations
local player_model = {} local player_model = {}
local player_textures = {} local player_textures = {}
@ -112,6 +90,7 @@ function mcl_player.player_set_model(player, model_name)
if player_model[name] == model_name then if player_model[name] == model_name then
return return
end end
player_model[name] = model_name
player:set_properties({ player:set_properties({
mesh = model_name, mesh = model_name,
visual = "mesh", visual = "mesh",
@ -119,14 +98,20 @@ function mcl_player.player_set_model(player, model_name)
damage_texture_modifier = "^[colorize:red:130", damage_texture_modifier = "^[colorize:red:130",
}) })
update_player_textures(player) update_player_textures(player)
mcl_player.player_set_animation(player, "stand")
local new_anim = "stand"
local model_animations = models[model_name].animations
local old_anim = player_anim[name]
if model_animations and old_anim and model_animations[old_anim] then
new_anim = old_anim
end
mcl_player.player_set_animation(player, new_anim)
else else
player:set_properties({ player:set_properties({
textures = { "player.png", "player_back.png", }, textures = { "player.png", "player_back.png", },
visual = "upright_sprite", visual = "upright_sprite",
}) })
end end
player_model[name] = model_name
end end
function mcl_player.player_set_visibility(player, visible) function mcl_player.player_set_visibility(player, visible)
@ -180,7 +165,6 @@ minetest.register_on_joinplayer(function(player)
local name = player:get_player_name() local name = player:get_player_name()
mcl_player.player_attached[name] = false mcl_player.player_attached[name] = false
player_visible[name] = true player_visible[name] = true
mcl_player.player_set_model(player, "character.b3d")
player_textures[name] = {"character.png", "blank.png", "blank.png"} player_textures[name] = {"character.png", "blank.png", "blank.png"}
--player:set_local_animation({x=0, y=79}, {x=168, y=187}, {x=189, y=198}, {x=200, y=219}, 30) --player:set_local_animation({x=0, y=79}, {x=168, y=187}, {x=189, y=198}, {x=200, y=219}, 30)
player:set_fov(86.1) -- see <https://minecraft.gamepedia.com/Options#Video_settings>>>> player:set_fov(86.1) -- see <https://minecraft.gamepedia.com/Options#Video_settings>>>>

@ -3,9 +3,11 @@
This mod allows advanced skin customization. This mod allows advanced skin customization.
Use the /skin command to open the skin configuration screen. Use the /skin command to open the skin configuration screen.
To include custom skins in MineClone2, please download [mcl_custom_skins](https://git.minetest.land/mineclone2/mcl_custom_skins)
## License ## License
Code under MIT license Code under MIT license
Author: TenPlus1, Zeg9, MrRar Author: MrRar
See image_credits.txt for image licensing. See image_credits.txt for image licensing.
@ -15,6 +17,7 @@ See image_credits.txt for image licensing.
Register a skin item. `item` is a table with item properties listed below. Register a skin item. `item` is a table with item properties listed below.
### Item properties ### Item properties
`type` `type`
Set the item type. Valid values are: "base", "footwear", "eye", "mouth", "bottom", "top", "hair", "headwear" Set the item type. Valid values are: "base", "footwear", "eye", "mouth", "bottom", "top", "hair", "headwear"
@ -28,16 +31,40 @@ Coloring only works for "base", "bottom, "top", and "hair".
`preview_rotation` `preview_rotation`
A table containing properties x and y. x and y represent the x and y rotation of the item preview. A table containing properties x and y. x and y represent the x and y rotation of the item preview.
`alex` `template2`
If set to true the item will be default for female character. If set to true the item will be default for female template.
`steve` `template1`
If set to true the item will be default for male character. If set to true the item will be default for male template.
`rank`
This property is used to change the application order of the skin item when applied to a player.
The default ranks for each item type are:
base: 10
footwear: 20
eye: 30
mouth: 40
bottom: 50
top: 60
hair: 70
headwear: 80
Lower ranks are applied to the player first and can thus be covered by higher rank items.
### `mcl_skins.show_formspec(player, active_tab, page_num)` ### `mcl_skins.show_formspec(player, active_tab, page_num)`
Show the skin configuration screen. Show the skin configuration screen.
`player` is a player ObjectRef. `player` is a player ObjectRef.
`active_tab` is the tab that will be displayed. This parameter is optional. `active_tab` is the tab that will be displayed. This parameter is optional.
Can be one of: "arm", "base", "footwear", "eye", "mouth", "bottom", "top", "hair", "headwear" Can be one of: "arm", "base", "footwear", "eye", "mouth", "bottom", "top", "hair", "headwear"
@ -50,7 +77,9 @@ Returns an array of tables containing information about each skin.
Each table contains the following properties: Each table contains the following properties:
`id`: A string representing the node ID. A node can be registered using this node ID. `id`: A string representing the node ID. A node can be registered using this node ID.
`texture`: A texture string that can be used in the node defintion. `texture`: A texture string that can be used in the node defintion.
`slim_arms`: A boolean value. If true, this texture is used with the "female" player mesh. Otherwise the regular mesh is to be used. `slim_arms`: A boolean value. If true, this texture is used with the "female" player mesh. Otherwise the regular mesh is to be used.
### `mcl_skins.get_node_id_by_player(player)` ### `mcl_skins.get_node_id_by_player(player)`
@ -71,10 +100,19 @@ These colors are separate from `mcl_skins.color` because some mods register two
### `mcl_skins.color` ### `mcl_skins.color`
A table of ColorSpec integers that the player can select to color colorable skin items. A table of ColorSpec integers that the player can select to color colorable skin items.
### `mcl_skins.players` ### `mcl_skins.player_skins`
A table mapped by player ObjectRef containing tables holding the player's selected skin items and colors. A table mapped by player ObjectRef containing tables holding the player's selected skin items and colors.
Only stores skin information for logged in users. Only stores skin information for logged in users.
### mcl_skins.compile_skin(skin) ### `mcl_skins.compile_skin(skin)`
`skin` is a table with skin item properties. `skin` is a table with skin item properties.
Returns an image string. Returns an image string.
### `mcl_skins.register_simple_skin(skin)`
`skin` is a table with the following properties:
`texture`
The texture of the skin.
`slim_arms`
A boolean value. If set to true, the slim armed player mesh will be used with this skin.

@ -1,10 +1,12 @@
local S = minetest.get_translator("mcl_skins") local S = minetest.get_translator("mcl_skins")
local color_to_string = minetest.colorspec_to_colorstring local color_to_string = minetest.colorspec_to_colorstring
local EDIT_SKIN_KEY = -1 -- The key used for edit skin in the mcl_skins.simple_skins table
mcl_skins = { mcl_skins = {
simple_skins = {}, simple_skins = {},
texture_to_simple_skin = {},
item_names = {"base", "footwear", "eye", "mouth", "bottom", "top", "hair", "headwear"}, item_names = {"base", "footwear", "eye", "mouth", "bottom", "top", "hair", "headwear"},
tab_names = {"template", "base", "headwear", "hair", "eye", "mouth", "top", "arm", "bottom", "footwear"}, tab_names = {"skin", "template", "base", "headwear", "hair", "eye", "mouth", "top", "arm", "bottom", "footwear"},
tab_descriptions = { tab_descriptions = {
template = S("Templates"), template = S("Templates"),
arm = S("Arm size"), arm = S("Arm size"),
@ -18,8 +20,8 @@ mcl_skins = {
headwear = S("Headwears"), headwear = S("Headwears"),
skin = S("Skins"), skin = S("Skins"),
}, },
steve = {}, -- Stores skin values for Steve skin template1 = {}, -- Stores edit skin values for template1
alex = {}, -- Stores skin values for Alex skin template2 = {}, -- Stores edit skin values for template2
base = {}, -- List of base textures base = {}, -- List of base textures
-- Base color is separate to keep the number of junk nodes registered in check -- Base color is separate to keep the number of junk nodes registered in check
@ -50,63 +52,90 @@ mcl_skins = {
headwear = {}, headwear = {},
masks = {}, masks = {},
preview_rotations = {}, preview_rotations = {},
players = {} ranks = {},
player_skins = {},
player_formspecs = {},
} }
function mcl_skins.register_item(item) function mcl_skins.register_item(item)
assert(mcl_skins[item.type], "Skin item type " .. item.type .. " does not exist.") assert(mcl_skins[item.type], "Skin item type " .. item.type .. " does not exist.")
local texture = item.texture or "blank.png" local texture = item.texture or "blank.png"
if item.steve then if item.template1 then
mcl_skins.steve[item.type] = texture mcl_skins.template1[item.type] = texture
end end
if item.alex then if item.template2 then
mcl_skins.alex[item.type] = texture mcl_skins.template2[item.type] = texture
end end
table.insert(mcl_skins[item.type], texture) table.insert(mcl_skins[item.type], texture)
mcl_skins.masks[texture] = item.mask mcl_skins.masks[texture] = item.mask
if item.preview_rotation then mcl_skins.preview_rotations[texture] = item.preview_rotation
mcl_skins.preview_rotations[texture] = item.preview_rotation mcl_skins.ranks[texture] = item.rank
end
function mcl_skins.register_simple_skin(skin)
if skin.index then
mcl_skins.simple_skins[skin.index] = skin
else
table.insert(mcl_skins.simple_skins, skin)
end end
mcl_skins.texture_to_simple_skin[skin.texture] = skin
end end
function mcl_skins.save(player) function mcl_skins.save(player)
local skin = mcl_skins.players[player] local skin = mcl_skins.player_skins[player]
if not skin then return end if not skin then return end
local meta = player:get_meta() local meta = player:get_meta()
meta:set_string("mcl_skins:skin", minetest.serialize(skin)) meta:set_string("mcl_skins:skin", minetest.serialize(skin))
meta:set_string("mcl_skins:skin_id", tostring(skin.simple_skins_id or "")) -- Clear out the old way of storing the simple skin ID
meta:set_string("mcl_skins:skin_id", "")
end end
minetest.register_chatcommand("skin", { minetest.register_chatcommand("skin", {
description = S("Open skin configuration screen."), description = S("Open skin configuration screen."),
privs = {}, privs = {},
func = function(name, param) mcl_skins.show_formspec(minetest.get_player_by_name(name)) end func = function(name, param)
local player = minetest.get_player_by_name(name)
local formspec_data = mcl_skins.player_formspecs[player]
local active_tab = formspec_data.active_tab
local page_num = formspec_data.page_num
mcl_skins.show_formspec(player, active_tab, page_num)
end
}) })
function mcl_skins.compile_skin(skin) function mcl_skins.compile_skin(skin)
if not skin then return "blank.png" end
if skin.simple_skins_id then if skin.simple_skins_id then
return mcl_skins.simple_skins[skin.simple_skins_id].texture return skin.simple_skins_id
end end
local output = "" local ranks = {}
for i, item in pairs(mcl_skins.item_names) do local layers = {}
for i, item in ipairs(mcl_skins.item_names) do
local texture = skin[item] local texture = skin[item]
local layer = ""
local rank = mcl_skins.ranks[texture] or i * 10
if texture and texture ~= "blank.png" then if texture and texture ~= "blank.png" then
if skin[item .. "_color"] and mcl_skins.masks[texture] then if skin[item .. "_color"] and mcl_skins.masks[texture] then
if #output > 0 then output = output .. "^" end
local color = color_to_string(skin[item .. "_color"]) local color = color_to_string(skin[item .. "_color"])
output = output .. layer = "(" .. mcl_skins.masks[texture] .. "^[colorize:" .. color .. ":alpha)"
"(" .. mcl_skins.masks[texture] .. "^[colorize:" .. color .. ":alpha)"
end end
if #output > 0 then output = output .. "^" end if #layer > 0 then layer = layer .. "^" end
output = output .. texture layer = layer .. texture
layers[rank] = layer
table.insert(ranks, rank)
end end
end end
table.sort(ranks)
local output = ""
for i, rank in ipairs(ranks) do
if #output > 0 then output = output .. "^" end
output = output .. layers[rank]
end
return output return output
end end
@ -115,13 +144,13 @@ function mcl_skins.update_player_skin(player)
return return
end end
local skin = mcl_skins.players[player] local skin = mcl_skins.player_skins[player]
mcl_player.player_set_skin(player, mcl_skins.compile_skin(skin)) mcl_player.player_set_skin(player, mcl_skins.compile_skin(skin))
local slim_arms local slim_arms
if skin.simple_skins_id then if skin.simple_skins_id then
slim_arms = mcl_skins.simple_skins[skin.simple_skins_id].slim_arms slim_arms = mcl_skins.texture_to_simple_skin[skin.simple_skins_id].slim_arms
else else
slim_arms = skin.slim_arms slim_arms = skin.slim_arms
end end
@ -131,33 +160,35 @@ end
-- Load player skin on join -- Load player skin on join
minetest.register_on_joinplayer(function(player) minetest.register_on_joinplayer(function(player)
local function table_get_random(t)
return t[math.random(#t)]
end
local skin = player:get_meta():get_string("mcl_skins:skin") local skin = player:get_meta():get_string("mcl_skins:skin")
if skin then if skin then
skin = minetest.deserialize(skin) skin = minetest.deserialize(skin)
end end
if skin then if skin then
-- If the player moves a slider and then quickly exits the game, form_send_job gets saved. if not mcl_skins.texture_to_simple_skin[skin.simple_skins_id] then
-- This should never have been put in with the skin data in the first place. skin.simple_skins_id = nil
skin.form_send_job = nil end
mcl_skins.players[player] = skin mcl_skins.player_skins[player] = skin
else else
if math.random() > 0.5 then if math.random() > 0.5 then
skin = table.copy(mcl_skins.steve) skin = table.copy(mcl_skins.template1)
else else
skin = table.copy(mcl_skins.alex) skin = table.copy(mcl_skins.template2)
end end
mcl_skins.players[player] = skin mcl_skins.player_skins[player] = skin
end end
mcl_skins.players[player].simple_skins_id = nil mcl_skins.player_formspecs[player] = {
active_tab = "skin",
page_num = 1
}
if #mcl_skins.simple_skins > 0 then if #mcl_skins.simple_skins > 0 then
local skin_id = tonumber(player:get_meta():get_string("mcl_skins:skin_id")) local skin_id = tonumber(player:get_meta():get_string("mcl_skins:skin_id"))
if skin_id and mcl_skins.simple_skins[skin_id] then if skin_id and mcl_skins.simple_skins[skin_id] then
mcl_skins.players[player].simple_skins_id = skin_id local texture = mcl_skins.simple_skins[skin_id].texture
mcl_skins.player_skins[player].simple_skins_id = texture
end end
end end
mcl_skins.save(player) mcl_skins.save(player)
@ -165,33 +196,30 @@ minetest.register_on_joinplayer(function(player)
end) end)
minetest.register_on_leaveplayer(function(player) minetest.register_on_leaveplayer(function(player)
mcl_skins.players[player] = nil mcl_skins.player_skins[player] = nil
mcl_skins.player_formspecs[player] = nil
end) end)
function mcl_skins.show_formspec(player, active_tab, page_num) local function calculate_page_count(tab)
local skin = mcl_skins.players[player] if tab == "skin" then
local default = #mcl_skins.simple_skins > 0 and "skin" or "template" return math.ceil((#mcl_skins.simple_skins + 2) / 8)
active_tab = active_tab or default elseif mcl_skins[tab] then
page_num = page_num or 1 return math.ceil(#mcl_skins[tab] / 16)
local page_count
if page_num < 1 then page_num = 1 end
if mcl_skins[active_tab] then
page_count = math.ceil(#mcl_skins[active_tab] / 16)
if page_num > page_count then
page_num = page_count
end
elseif active_tab == "skin" then
page_count = math.ceil((#mcl_skins.simple_skins + 2) / 8)
if page_num > page_count then
page_num = page_count
end
else
page_num = 1
page_count = 1
end end
return 1
end
function mcl_skins.show_formspec(player, active_tab, page_num)
local formspec_data = mcl_skins.player_formspecs[player]
local skin = mcl_skins.player_skins[player]
formspec_data.active_tab = active_tab
local formspec = "formspec_version[3]size[13.2,11]" local page_count = calculate_page_count(active_tab)
if page_num < 1 then page_num = 1 end
if page_num > page_count then page_num = page_count end
formspec_data.page_num = page_num
local formspec = "formspec_version[3]size[14.2,11]"
for i, tab in pairs(mcl_skins.tab_names) do for i, tab in pairs(mcl_skins.tab_names) do
if tab == active_tab then if tab == active_tab then
@ -201,21 +229,23 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
local y = 0.3 + (i - 1) * 0.8 local y = 0.3 + (i - 1) * 0.8
formspec = formspec .. formspec = formspec ..
"button[0.3," .. y .. ";3,0.8;" .. tab .. ";" .. mcl_skins.tab_descriptions[tab] .. "]" "style[" .. tab .. ";content_offset=16,0]" ..
"button[0.3," .. y .. ";4,0.8;" .. tab .. ";" .. mcl_skins.tab_descriptions[tab] .. "]" ..
"image[0.4," .. y + 0.1 .. ";0.6,0.6;mcl_skins_icons.png^[verticalframe:11:" .. i - 1 .. "]"
if skin.simple_skins_id then break end if skin.simple_skins_id then break end
end end
local slim_arms local slim_arms
if skin.simple_skins_id then if skin.simple_skins_id then
slim_arms = mcl_skins.simple_skins[skin.simple_skins_id].slim_arms slim_arms = mcl_skins.texture_to_simple_skin[skin.simple_skins_id].slim_arms
else else
slim_arms = skin.slim_arms slim_arms = skin.slim_arms
end end
local mesh = slim_arms and "mcl_armor_character_female.b3d" or "mcl_armor_character.b3d" local mesh = slim_arms and "mcl_armor_character_female.b3d" or "mcl_armor_character.b3d"
formspec = formspec .. formspec = formspec ..
"model[10,0.3;3,7;player_mesh;" .. mesh .. ";" .. "model[11,0.3;3,7;player_mesh;" .. mesh .. ";" ..
mcl_skins.compile_skin(skin) .. mcl_skins.compile_skin(skin) ..
",blank.png,blank.png;0,180;false;true;0,0]" ",blank.png,blank.png;0,180;false;true;0,0]"
@ -224,23 +254,24 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
local page_end = math.min(page_start + 8 - 1, #mcl_skins.simple_skins) local page_end = math.min(page_start + 8 - 1, #mcl_skins.simple_skins)
formspec = formspec .. formspec = formspec ..
"style_type[button;bgcolor=#00000000]" "style_type[button;bgcolor=#00000000]"
local skin = table.copy(skin) local skin = table.copy(skin)
local skin_id = skin.simple_skins_id or -1 local simple_skins_id = skin.simple_skins_id
skin.simple_skins_id = nil skin.simple_skins_id = nil
mcl_skins.simple_skins[EDIT_SKIN_KEY] = {
local skins = table.copy(mcl_skins.simple_skins)
skins[-1] = {
slim_arms = skin.slim_arms, slim_arms = skin.slim_arms,
texture = mcl_skins.compile_skin(skin), texture = mcl_skins.compile_skin(skin),
} }
simple_skins_id = simple_skins_id or
mcl_skins.simple_skins[EDIT_SKIN_KEY].texture
for i = page_start, page_end do for i = page_start, page_end do
local skin = skins[i] local skin = mcl_skins.simple_skins[i]
local j = i - page_start - 1 local j = i - page_start - 1
local mesh = skin.slim_arms and "mcl_armor_character_female.b3d" or "mcl_armor_character.b3d" local mesh = skin.slim_arms and "mcl_armor_character_female.b3d" or
"mcl_armor_character.b3d"
local x = 3.5 + (j + 1) % 4 * 1.6 local x = 4.5 + (j + 1) % 4 * 1.6
local y = 0.3 + math.floor((j + 1) / 4) * 3.1 local y = 0.3 + math.floor((j + 1) / 4) * 3.1
formspec = formspec .. formspec = formspec ..
@ -248,7 +279,7 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
skin.texture .. skin.texture ..
",blank.png,blank.png;0,180;false;true;0,0]" ",blank.png,blank.png;0,180;false;true;0,0]"
if skin_id == i then if simple_skins_id == skin.texture then
formspec = formspec .. formspec = formspec ..
"style[" .. i .. "style[" .. i ..
";bgcolor=;bgimg=mcl_skins_select_overlay.png;" .. ";bgcolor=;bgimg=mcl_skins_select_overlay.png;" ..
@ -258,22 +289,22 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
"button[" .. x .. "," .. y .. ";1.5,3;" .. i .. ";]" "button[" .. x .. "," .. y .. ";1.5,3;" .. i .. ";]"
end end
if page_start == -1 then if page_start == EDIT_SKIN_KEY then
formspec = formspec .. "image[3.85,1;0.8,0.8;mcl_skins_button.png]" formspec = formspec .. "image[4.85,1;0.8,0.8;mcl_skins_button.png]"
end end
elseif active_tab == "template" then elseif active_tab == "template" then
formspec = formspec .. formspec = formspec ..
"model[4,2;2,3;player_mesh;mcl_armor_character.b3d;" .. "model[5,2;2,3;player_mesh;mcl_armor_character.b3d;" ..
mcl_skins.compile_skin(mcl_skins.steve) .. mcl_skins.compile_skin(mcl_skins.template1) ..
",blank.png,blank.png;0,180;false;true;0,0]" .. ",blank.png,blank.png;0,180;false;true;0,0]" ..
"button[4,5.2;2,0.8;steve;" .. S("Select") .. "]" .. "button[5,5.2;2,0.8;template1;" .. S("Select") .. "]" ..
"model[6.5,2;2,3;player_mesh;mcl_armor_character_female.b3d;" .. "model[7.5,2;2,3;player_mesh;mcl_armor_character_female.b3d;" ..
mcl_skins.compile_skin(mcl_skins.alex) .. mcl_skins.compile_skin(mcl_skins.template2) ..
",blank.png,blank.png;0,180;false;true;0,0]" .. ",blank.png,blank.png;0,180;false;true;0,0]" ..
"button[6.5,5.2;2,0.8;alex;" .. S("Select") .. "]" "button[7.5,5.2;2,0.8;template2;" .. S("Select") .. "]"
elseif mcl_skins[active_tab] then elseif mcl_skins[active_tab] then
formspec = formspec .. formspec = formspec ..
@ -308,7 +339,7 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
end end
i = i - 1 i = i - 1
local x = 3.5 + i % 4 * 1.6 local x = 4.5 + i % 4 * 1.6
local y = 0.3 + math.floor(i / 4) * 1.6 local y = 0.3 + math.floor(i / 4) * 1.6
formspec = formspec .. formspec = formspec ..
"model[" .. x .. "," .. y .. "model[" .. x .. "," .. y ..
@ -325,11 +356,12 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
formspec = formspec .. "button[" .. x .. "," .. y .. ";1.5,1.5;" .. texture .. ";]" formspec = formspec .. "button[" .. x .. "," .. y .. ";1.5,1.5;" .. texture .. ";]"
end end
elseif active_tab == "arm" then elseif active_tab == "arm" then
local x = skin.slim_arms and 4.7 or 3.6 local x = skin.slim_arms and 5.7 or 4.6
formspec = formspec .. formspec = formspec ..
"image_button[3.6,0.3;1,1;mcl_skins_thick_arms.png;thick_arms;]" .. "image_button[4.6,0.3;1,1;mcl_skins_thick_arms.png;thick_arms;]" ..
"image_button[4.7,0.3;1,1;mcl_skins_slim_arms.png;slim_arms;]" .. "image_button[5.7,0.3;1,1;mcl_skins_slim_arms.png;slim_arms;]" ..
"style[arm;bgcolor=;bgimg=mcl_skins_select_overlay.png;bgimg_middle=14,14;bgimg_pressed=mcl_skins_select_overlay.png]" .. "style[arm;bgcolor=;bgimg=mcl_skins_select_overlay.png;" ..
"bgimg_middle=14,14;bgimg_pressed=mcl_skins_select_overlay.png]" ..
"button[" .. x .. ",0.3;1,1;arm;]" "button[" .. x .. ",0.3;1,1;arm;]"
end end
@ -343,7 +375,7 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
for i, colorspec in pairs(colors) do for i, colorspec in pairs(colors) do
local color = color_to_string(colorspec) local color = color_to_string(colorspec)
i = i - 1 i = i - 1
local x = 3.6 + i % 6 * 0.9 local x = 4.6 + i % 6 * 0.9
local y = 8 + math.floor(i / 6) * 0.9 local y = 8 + math.floor(i / 6) * 0.9
formspec = formspec .. formspec = formspec ..
"image_button[" .. x .. "," .. y .. "image_button[" .. x .. "," .. y ..
@ -353,7 +385,8 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
if selected_color == colorspec then if selected_color == colorspec then
formspec = formspec .. formspec = formspec ..
"style[" .. color .. "style[" .. color ..
";bgcolor=;bgimg=mcl_skins_select_overlay.png;bgimg_middle=14,14;bgimg_pressed=mcl_skins_select_overlay.png]" .. ";bgcolor=;bgimg=mcl_skins_select_overlay.png;bgimg_middle=14,14;" ..
"bgimg_pressed=mcl_skins_select_overlay.png]" ..
"button[" .. x .. "," .. y .. ";0.8,0.8;" .. color .. ";]" "button[" .. x .. "," .. y .. ";0.8,0.8;" .. color .. ";]"
end end
@ -365,7 +398,7 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
local green = math.floor(selected_color / 0x100) - 0xff0000 - red * 0x100 local green = math.floor(selected_color / 0x100) - 0xff0000 - red * 0x100
local blue = selected_color - 0xff000000 - red * 0x10000 - green * 0x100 local blue = selected_color - 0xff000000 - red * 0x10000 - green * 0x100
formspec = formspec .. formspec = formspec ..
"container[9.2,8]" .. "container[10.2,8]" ..
"scrollbaroptions[min=0;max=255;smallstep=20]" .. "scrollbaroptions[min=0;max=255;smallstep=20]" ..
"box[0.4,0;2.49,0.38;red]" .. "box[0.4,0;2.49,0.38;red]" ..
@ -386,49 +419,42 @@ function mcl_skins.show_formspec(player, active_tab, page_num)
"container_end[]" "container_end[]"
end end
end end
if page_num > 1 then if page_num > 1 then
formspec = formspec .. formspec = formspec ..
"image_button[3.5,6.7;1,1;mcl_skins_arrow.png^[transformFX;previous_page;]" "image_button[4.5,6.7;1,1;mcl_skins_arrow.png^[transformFX;previous_page;]"
end end
if page_num < page_count then if page_num < page_count then
formspec = formspec .. formspec = formspec ..
"image_button[8.8,6.7;1,1;mcl_skins_arrow.png;next_page;]" "image_button[9.8,6.7;1,1;mcl_skins_arrow.png;next_page;]"
end end
if page_count > 1 then if page_count > 1 then
formspec = formspec .. formspec = formspec ..
"label[6.3,7.2;" .. page_num .. " / " .. page_count .. "]" "label[7.3,7.2;" .. page_num .. " / " .. page_count .. "]"
end end
local player_name = player:get_player_name() local player_name = player:get_player_name()
minetest.show_formspec(player_name, "mcl_skins:" .. active_tab .. "_" .. page_num, formspec) minetest.show_formspec(player_name, "mcl_skins:skins", formspec)
end end
minetest.register_on_player_receive_fields(function(player, formname, fields) minetest.register_on_player_receive_fields(function(player, formname, fields)
local formspec_data = mcl_skins.player_formspecs[player]
local active_tab = formspec_data.active_tab
local page_num = formspec_data.page_num
if fields.__mcl_skins then if fields.__mcl_skins then
mcl_skins.show_formspec(player) mcl_skins.show_formspec(player, active_tab, page_num)
return false return false
end end
if formname ~= "mcl_skins:skins" then return false end
if not formname:find("^mcl_skins:") then return false end
local _, _, active_tab, page_num = formname:find("^mcl_skins:(%a+)_(%d+)")
local active_tab_found = false
for _, tab in pairs(mcl_skins.tab_names) do
if tab == active_tab then active_tab_found = true end
end
active_tab = active_tab_found and active_tab or "template"
if not page_num or not active_tab then return true end
page_num = math.floor(tonumber(page_num) or 1)
-- Cancel formspec resend after scrollbar move -- Cancel formspec resend after scrollbar move
if mcl_skins.players[player].form_send_job then if formspec_data.form_send_job then
mcl_skins.players[player].form_send_job:cancel() formspec_data.form_send_job:cancel()
mcl_skins.players[player].form_send_job = nil formspec_data.form_send_job = nil
end end
if fields.quit then if fields.quit then
@ -436,13 +462,13 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
return true return true
end end
if fields.alex then if fields.template2 then
mcl_skins.players[player] = table.copy(mcl_skins.alex) mcl_skins.player_skins[player] = table.copy(mcl_skins.template2)
mcl_skins.update_player_skin(player) mcl_skins.update_player_skin(player)
mcl_skins.show_formspec(player, active_tab, page_num) mcl_skins.show_formspec(player, active_tab, page_num)
return true return true
elseif fields.steve then elseif fields.template1 then
mcl_skins.players[player] = table.copy(mcl_skins.steve) mcl_skins.player_skins[player] = table.copy(mcl_skins.template1)
mcl_skins.update_player_skin(player) mcl_skins.update_player_skin(player)
mcl_skins.show_formspec(player, active_tab, page_num) mcl_skins.show_formspec(player, active_tab, page_num)
return true return true
@ -450,12 +476,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
for i, tab in pairs(mcl_skins.tab_names) do for i, tab in pairs(mcl_skins.tab_names) do
if fields[tab] then if fields[tab] then
mcl_skins.show_formspec(player, tab, page_num) mcl_skins.show_formspec(player, tab, 1)
return true return true
end end
end end
local skin = mcl_skins.players[player] local skin = mcl_skins.player_skins[player]
if not skin then return true end if not skin then return true end
if fields.next_page then if fields.next_page then
@ -496,12 +522,12 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
local color = 0xff000000 + red * 0x10000 + green * 0x100 + blue local color = 0xff000000 + red * 0x10000 + green * 0x100 + blue
if color >= 0 and color <= 0xffffffff then if color >= 0 and color <= 0xffffffff then
-- We delay resedning the form because otherwise it will break dragging scrollbars -- We delay resedning the form because otherwise it will break dragging scrollbars
mcl_skins.players[player].form_send_job = minetest.after(0.2, function() formspec_data.form_send_job = minetest.after(0.2, function()
if player and player:is_player() then if player and player:is_player() then
skin[active_tab .. "_color"] = color skin[active_tab .. "_color"] = color
mcl_skins.update_player_skin(player) mcl_skins.update_player_skin(player)
mcl_skins.show_formspec(player, active_tab, page_num) mcl_skins.show_formspec(player, active_tab, page_num)
mcl_skins.players[player].form_send_job = nil formspec_data.form_send_job = nil
end end
end) end)
return true return true
@ -517,14 +543,14 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end end
if field and active_tab == "skin" then if field and active_tab == "skin" then
local skin_id = tonumber(field) local index = tonumber(field)
skin_id = skin_id and math.floor(skin_id) or 0 index = index and math.floor(index) or 0
mcl_skins.simple_skins[EDIT_SKIN_KEY].texture = nil
if if
#mcl_skins.simple_skins > 0 and #mcl_skins.simple_skins > 0 and
skin_id >= -1 and skin_id <= #mcl_skins.simple_skins index >= EDIT_SKIN_KEY and index <= #mcl_skins.simple_skins
then then
if skin_id == -1 then skin_id = nil end skin.simple_skins_id = mcl_skins.simple_skins[index].texture
skin.simple_skins_id = skin_id
mcl_skins.update_player_skin(player) mcl_skins.update_player_skin(player)
mcl_skins.show_formspec(player, active_tab, page_num) mcl_skins.show_formspec(player, active_tab, page_num)
end end
@ -532,15 +558,14 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end end
-- See if field is a texture -- See if field is a texture
if field and mcl_skins[active_tab] then if
for i, texture in pairs(mcl_skins[active_tab]) do field and mcl_skins[active_tab] and
if texture == field then table.indexof(mcl_skins[active_tab], field) ~= -1
skin[active_tab] = texture then
mcl_skins.update_player_skin(player) skin[active_tab] = field
mcl_skins.show_formspec(player, active_tab, page_num) mcl_skins.update_player_skin(player)
return true mcl_skins.show_formspec(player, active_tab, page_num)
end return true
end
end end
-- See if field is a color -- See if field is a color
@ -559,15 +584,6 @@ minetest.register_on_player_receive_fields(function(player, formname, fields)
end) end)
local function init() local function init()
local function file_exists(name)
local f = io.open(name)
if not f then
return false
end
f:close()
return true
end
local f = io.open(minetest.get_modpath("mcl_skins") .. "/list.json") local f = io.open(minetest.get_modpath("mcl_skins") .. "/list.json")
assert(f, "Can't open the file list.json") assert(f, "Can't open the file list.json")
local data = f:read("*all") local data = f:read("*all")
@ -579,17 +595,27 @@ local function init()
for _, item in pairs(json) do for _, item in pairs(json) do
mcl_skins.register_item(item) mcl_skins.register_item(item)
end end
mcl_skins.steve.base_color = mcl_skins.base_color[2] mcl_skins.template1.base_color = mcl_skins.base_color[2]
mcl_skins.steve.hair_color = 0xff5d473b mcl_skins.template1.hair_color = 0xff5d473b
mcl_skins.steve.top_color = 0xff993535 mcl_skins.template1.top_color = 0xff993535
mcl_skins.steve.bottom_color = 0xff644939 mcl_skins.template1.bottom_color = 0xff644939
mcl_skins.steve.slim_arms = false mcl_skins.template1.slim_arms = false
mcl_skins.alex.base_color = mcl_skins.base_color[1] mcl_skins.template2.base_color = mcl_skins.base_color[1]
mcl_skins.alex.hair_color = 0xff715d57 mcl_skins.template2.hair_color = 0xff715d57
mcl_skins.alex.top_color = 0xff346840 mcl_skins.template2.top_color = 0xff346840
mcl_skins.alex.bottom_color = 0xff383532 mcl_skins.template2.bottom_color = 0xff383532
mcl_skins.alex.slim_arms = true mcl_skins.template2.slim_arms = true
mcl_skins.register_simple_skin({
index = 0,
texture = "character.png"
})
mcl_skins.register_simple_skin({
index = 1,
texture = "mcl_skins_character_1.png",
slim_arms = true
})
end end
init() init()

@ -2,8 +2,9 @@
{ {
"type": "footwear", "type": "footwear",
"texture": "mcl_skins_footwear_1.png", "texture": "mcl_skins_footwear_1.png",
"steve": true, "template1": true,
"alex": true "template2": true,
"rank": 55
}, },
{ {
"type": "footwear", "type": "footwear",
@ -19,12 +20,12 @@
{ {
"type": "eye", "type": "eye",
"texture": "mcl_skins_eye_1.png", "texture": "mcl_skins_eye_1.png",
"alex": true "template2": true
}, },
{ {
"type": "eye", "type": "eye",
"texture": "mcl_skins_eye_2.png", "texture": "mcl_skins_eye_2.png",
"steve": true "template1": true
}, },
{ {
"type": "eye", "type": "eye",
@ -49,7 +50,7 @@
{ {
"type": "mouth", "type": "mouth",
"texture": "mcl_skins_mouth_1.png", "texture": "mcl_skins_mouth_1.png",
"steve": true "template1": true
}, },
{ {
"type": "mouth", "type": "mouth",
@ -77,19 +78,19 @@
}, },
{ {
"type": "mouth", "type": "mouth",
"alex": true "template1": true
}, },
{ {
"type": "hair", "type": "hair",
"texture": "mcl_skins_hair_1.png", "texture": "mcl_skins_hair_1.png",
"mask": "mcl_skins_hair_1_mask.png", "mask": "mcl_skins_hair_1_mask.png",
"alex": true "template2": true
}, },
{ {
"type": "hair", "type": "hair",
"texture": "mcl_skins_hair_2.png", "texture": "mcl_skins_hair_2.png",
"mask": "mcl_skins_hair_2_mask.png", "mask": "mcl_skins_hair_2_mask.png",
"steve": true "template1": true
}, },
{ {
"type": "hair", "type": "hair",
@ -146,7 +147,7 @@
{ {
"type": "headwear", "type": "headwear",
"texture": "mcl_skins_headwear_2.png", "texture": "mcl_skins_headwear_2.png",
"alex": true "template2": true
}, },
{ {
"type": "headwear", "type": "headwear",
@ -174,14 +175,14 @@
}, },
{ {
"type": "headwear", "type": "headwear",
"steve": true "template1": true
}, },
{ {
"type": "bottom", "type": "bottom",
"texture": "mcl_skins_bottom_1.png", "texture": "mcl_skins_bottom_1.png",
"mask": "mcl_skins_bottom_1_mask.png", "mask": "mcl_skins_bottom_1_mask.png",
"steve": true, "template1": true,
"alex": true "template2": true
}, },
{ {
"type": "bottom", "type": "bottom",
@ -198,12 +199,18 @@
"texture": "mcl_skins_bottom_4.png", "texture": "mcl_skins_bottom_4.png",
"mask": "mcl_skins_bottom_4_mask.png" "mask": "mcl_skins_bottom_4_mask.png"
}, },
{
"type": "bottom",
"texture": "mcl_skins_bottom_5.png",
"mask": "mcl_skins_bottom_5_mask.png",
"rank": 65
},
{ {
"type": "top", "type": "top",
"texture": "mcl_skins_top_1.png", "texture": "mcl_skins_top_1.png",
"mask": "mcl_skins_top_1_mask.png", "mask": "mcl_skins_top_1_mask.png",
"steve": true, "template1": true,
"alex": true "template2": true
}, },
{ {
"type": "top", "type": "top",
@ -254,7 +261,7 @@
"type": "base", "type": "base",
"texture": "mcl_skins_base_1.png", "texture": "mcl_skins_base_1.png",
"mask": "mcl_skins_base_1_mask.png", "mask": "mcl_skins_base_1_mask.png",
"steve": true, "template1": true,
"alex": true "template2": true
} }
] ]

@ -21,6 +21,9 @@ mcl_skins_bottom_3.png
mcl_skins_eye_7.png mcl_skins_eye_7.png
mcl_skins_mouth_7.png mcl_skins_mouth_7.png
mcl_skins_hair_10.png mcl_skins_hair_10.png
mcl_skins_hair_6.png
mcl_skins_eye_6.png
mcl_skins_bottom_5.png
Original work by MrRar Original work by MrRar
License: CC BY-SA 4.0 License: CC BY-SA 4.0
@ -66,13 +69,6 @@ Author: hansuke123. Adapted for mcl_skins by MrRar.
License: CC BY-SA 3.0 License: CC BY-SA 3.0
Source: http://minetest.fensta.bplaced.net/#!page:1,filtertype:Id,filter:291 Source: http://minetest.fensta.bplaced.net/#!page:1,filtertype:Id,filter:291
mcl_skins_hair_6.png
mcl_skins_eye_6.png
Name: Mumbo Jumbo
Author: ZestyZachary
License: CC 0 (1.0)
Source: http://minetest.fensta.bplaced.net/#!page:1,filtertype:Id,filter:2100
mcl_skins_eye_4.png mcl_skins_eye_4.png
Name: lisa Name: lisa
Author: hansuke123 Author: hansuke123

@ -14,7 +14,9 @@ function mcl_skins.get_skin_list()
for _, game_mode in pairs({"_crea", "_surv"}) do for _, game_mode in pairs({"_crea", "_surv"}) do
for _, base in pairs(mcl_skins.base) do for _, base in pairs(mcl_skins.base) do
for _, base_color in pairs(mcl_skins.base_color) do for _, base_color in pairs(mcl_skins.base_color) do
local id = base:gsub(".png$", "") .. minetest.colorspec_to_colorstring(base_color):gsub("#", "") local id = base:gsub(".png$", "")
.. minetest.colorspec_to_colorstring(base_color):gsub("#", "")
local female = { local female = {
texture = make_texture(base, base_color), texture = make_texture(base, base_color),
slim_arms = true, slim_arms = true,
@ -36,7 +38,8 @@ function mcl_skins.get_skin_list()
table.insert(list, { table.insert(list, {
texture = skin.texture, texture = skin.texture,
slim_arms = skin.slim_arms, slim_arms = skin.slim_arms,
id = skin.texture:gsub(".png$", "") .. (skin.slim_arms and "_female" or "_male") .. game_mode, id = skin.texture:gsub(".png$", "")
.. (skin.slim_arms and "_female" or "_male") .. game_mode,
creative = game_mode == "_crea" creative = game_mode == "_crea"
}) })
end end
@ -45,10 +48,10 @@ function mcl_skins.get_skin_list()
end end
function mcl_skins.get_node_id_by_player(player) function mcl_skins.get_node_id_by_player(player)
local skin = mcl_skins.players[player] local skin = mcl_skins.player_skins[player]
local simple_skin = skin.simple_skins_id local simple_skin = skin.simple_skins_id
if simple_skin then if simple_skin then
skin = mcl_skins.simple_skins[skin.simple_skins_id] skin = mcl_skins.texture_to_simple_skin[skin.simple_skins_id]
end end
local creative = minetest.is_creative_enabled(player:get_player_name()) local creative = minetest.is_creative_enabled(player:get_player_name())
local append = (skin.slim_arms and "_female" or "_male") .. (creative and "_crea" or "_surv") local append = (skin.slim_arms and "_female" or "_male") .. (creative and "_crea" or "_surv")

@ -1,3 +0,0 @@
name = "Steve",
author = "%TEXTURE_PACK_AUTHOR%",
gender = "male",

@ -1,3 +0,0 @@
name = "Alex",
author = "%TEXTURE_PACK_AUTHOR%",
gender = "female",

@ -1,5 +1,5 @@
local function init_simple_skins() local function init_simple_skins()
local id, f, data, skin = 0 local id, f, data, skin = 2
local mod_path = minetest.get_modpath("mcl_skins") local mod_path = minetest.get_modpath("mcl_skins")
while true do while true do
@ -35,18 +35,13 @@ local function init_simple_skins()
end end
-- add metadata to list -- add metadata to list
mcl_skins.simple_skins[id] = { mcl_skins.register_simple_skin({
index = id,
texture = skin, texture = skin,
slim_arms = data and data.gender == "female", slim_arms = data and data.gender == "female",
} })
id = id + 1 id = id + 1
end end
if #mcl_skins.simple_skins > 0 then
table.insert(mcl_skins.tab_names, 1, "skin")
else
mcl_skins.simple_skins = {}
end
end end
init_simple_skins() init_simple_skins()

@ -0,0 +1,3 @@
To add custom skins to the game, please use the mcl_custom_skins mod.
Download it from https://git.minetest.land/mineclone2/mcl_custom_skins
Support for adding custom skins to mcl_skins will be removed in a future MineClone2 release.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 226 B

After

Width:  |  Height:  |  Size: 375 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 B

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 680 B

After

Width:  |  Height:  |  Size: 749 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 117 B

After

Width:  |  Height:  |  Size: 142 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 891 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 B

Before

Width:  |  Height:  |  Size: 4.7 KiB

After

Width:  |  Height:  |  Size: 4.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 135 B

After

Width:  |  Height:  |  Size: 141 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 501 B

After

Width:  |  Height:  |  Size: 535 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 134 B

After

Width:  |  Height:  |  Size: 168 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 554 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 124 B

After

Width:  |  Height:  |  Size: 110 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.0 KiB

After

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 122 B

After

Width:  |  Height:  |  Size: 151 B