character_creator = {} character_creator.skins = dofile(minetest.get_modpath("character_creator") .. "/skins.lua") local skin_default = { gender = "Male", height = 1, width = 1, skin = "Fair Skin", face = "Human Face", hair = "Brown Hair", hair_style = "Medium Hair", eyes = "Blue Eyes", tshirt = "Green T-Shirt", pants = "Blue Pants", shoes = "Leather Shoes" } local skins = character_creator.skins local skins_array = {} minetest.after(0, function() local function associative_to_array(associative) local array = {} for key in pairs(associative) do table.insert(array, key) end return array end skins_array = { skin = associative_to_array(skins.skin), face = associative_to_array(skins.face), hair = associative_to_array(skins.hair), hair_style = associative_to_array(skins.hair_style), eyes = associative_to_array(skins.eyes), tshirt = associative_to_array(skins.tshirt), pants = associative_to_array(skins.pants), shoes = associative_to_array(skins.shoes) } end) -- Saved skins_array indexes in this local skin_indexes = {} local function show_formspec(player) local indexes = skin_indexes[player] local formspec = "size[15,9.5]" .. "no_prepend[]" .. "bgcolor[#00000000]" -- Gender .. "button[10,;2.5,.5;male;Male]" .. "button[12.5,;2.5,.5;female;Female]" -- Height .. "button[10,1.1;2.5,.5;taller;Taller]" .. "button[10,2;2.5,.5;shorter;Shorter]" -- Width .. "button[12.5,1.1;2.5,.5;wider;Wider]" .. "button[12.5,2;2.5,.5;thinner;Thinner]" -- Skin .. "button[10,2.75;5,1;skin;" .. skins_array.skin[indexes.skin] .. "]" .. "button[10,2.75;1,1;skin_back;<<]" .. "button[14,2.75;1,1;skin_next;>>]" -- Face .. "button[10,3.5;5,1;face;" .. skins_array.face[indexes.face] .. "]" .. "button[10,3.5;1,1;face_back;<<]" .. "button[14,3.5;1,1;face_next;>>]" -- Hair .. "button[10,4.25;5,1;hair;" .. skins_array.hair[indexes.hair] .. "]" .. "button[10,4.25;1,1;hair_back;<<]" .. "button[14,4.25;1,1;hair_next;>>]" -- Hair Style .. "button[10,5;5,1;hair_style;" .. skins_array.hair_style[indexes.hair_style] .. "]" .. "button[10,5;1,1;hair_style_back;<<]" .. "button[14,5;1,1;hair_style_next;>>]" -- Eyes .. "button[10,5.75;5,1;eyes;" .. skins_array.eyes[indexes.eyes] .. "]" .. "button[10,5.75;1,1;eyes_back;<<]" .. "button[14,5.75;1,1;eyes_next;>>]" -- T-Shirt .. "button[10,6.5;5,1;tshirt;" .. skins_array.tshirt[indexes.tshirt] .. "]" .. "button[10,6.5;1,1;tshirt_back;<<]" .. "button[14,6.5;1,1;tshirt_next;>>]" -- Pants .. "button[10,7.25;5,1;pants;" .. skins_array.pants[indexes.pants] .. "]" .. "button[10,7.25;1,1;pants_back;<<]" .. "button[14,7.25;1,1;pants_next;>>]" -- Shoes .. "button[10,8;5,1;shoes;" .. skins_array.shoes[indexes.shoes] .. "]" .. "button[10,8;1,1;shoes_back;<<]" .. "button[14,8;1,1;shoes_next;>>]" -- Done .. "button_exit[10,9;2.5,.5;done;Done]" .. "button_exit[12.5,9;2.5,.5;cancel;Cancel]" local playername = player:get_player_name() player_api.set_skin(player, "character_creator:"..playername) minetest.show_formspec(player:get_player_name(), "character_creator", formspec) end local function load_skin(player) skin_indexes[player] = {} local player_meta = player:get_meta() if not player_meta:contains("character_creator:gender") then player_meta:set_string("character_creator:gender", skin_default.gender) end if not player_meta:contains("character_creator:width") then player_meta:set_float("character_creator:width", skin_default.width) end if not player_meta:contains("character_creator:height") then player_meta:set_float("character_creator:height", skin_default.height) end local function load_data(data_name) local key = player_meta:get_string("character_creator:" .. data_name) local index = table.indexof(skins_array[data_name], key) if index == -1 then index = table.indexof(skins_array[data_name], skin_default[data_name]) end local indexes = skin_indexes[player] indexes[data_name] = index end load_data("skin") load_data("face") load_data("eyes") load_data("hair_style") load_data("hair") load_data("tshirt") load_data("pants") load_data("shoes") end local function save_skin(player) local player_meta = player:get_meta() if player_meta == nil then -- The player disconnected before this function was dispatched return end local function save_data(data_name) local indexes = skin_indexes[player] local index = indexes[data_name] local key = skins_array[data_name][index] player_meta:set_string("character_creator:" .. data_name, key) end save_data("skin") save_data("face") save_data("eyes") save_data("hair_style") save_data("hair") save_data("tshirt") save_data("pants") save_data("shoes") end local function get_texture(player) local player_meta = player:get_meta() if player_meta == nil then -- The player disconnected before this function was dispatched return "" end local indexes = skin_indexes[player] local texture = "" local gender = player_meta:get_string("character_creator:gender") local skin_key = skins_array.skin[indexes.skin] local skin = skins.skin[skin_key] texture = texture .. "(" .. skin .. ")" local face_key = skins_array.face[indexes.face] local face = skins.face[face_key][gender][skin_key] texture = texture .. "^(" .. face .. ")" local eyes_key = skins_array.eyes[indexes.eyes] local eyes = skins.eyes[eyes_key] texture = texture .. "^(" .. eyes .. ")" local hair_style = skins_array.hair_style[indexes.hair_style] local hair_key = skins_array.hair[indexes.hair] local hair = skins.hair[hair_key][gender][hair_style] texture = texture .. "^(" .. hair .. ")" local tshirt_key = skins_array.tshirt[indexes.tshirt] local tshirt = skins.tshirt[tshirt_key] texture = texture .. "^(" .. tshirt .. ")" local pants_key = skins_array.pants[indexes.pants] local pants = skins.pants[pants_key] texture = texture .. "^(" .. pants .. ")" local shoes_key = skins_array.shoes[indexes.shoes] local shoes = skins.shoes[shoes_key] texture = texture .. "^(" .. shoes .. ")" return texture end local function change_skin(player) local playername = player:get_player_name() if playername == nil then -- The player disconnected before this function was dispatched return end local skin_obj = player_api.registered_skins["character_creator:"..playername] skin_obj.textures = skin_obj.textures or {} skin_obj.textures[1] = get_texture(player) skin_obj.preview = nil --rebuild preview save_skin(player) end player_api.register_skin_modifier(function(textures, player, player_model, player_skin) local player_meta = player:get_meta() if player_skin:sub(1,18) == 'character_creator:' then player:set_properties({ visual_size = { x = player_meta:get_float("character_creator:width"), y = player_meta:get_float("character_creator:height") }, }) end end) local orig_init_on_joinplayer = player_api.init_on_joinplayer function player_api.init_on_joinplayer(player) local playername = player:get_player_name() local skinname = "character_creator:"..playername load_skin(player) player_api.register_skin(skinname, { playername = playername, description = "Character creator", license = "MIT / CC-BY-SA 3.0 Unported", in_inventory_list = false, }) change_skin(player) orig_init_on_joinplayer(player) end minetest.register_on_leaveplayer(function(player) local playername = player:get_player_name() player_api.registered_skins["character_creator:"..playername] = nil skin_indexes[player] = nil end) local skin_temp = {} minetest.register_on_player_receive_fields(function(player, formname, fields) if formname ~= "character_creator" then return end local player_meta = player:get_meta() local indexes = skin_indexes[player] if not skin_temp[player] then skin_temp[player] = { gender = player_meta:get_string("character_creator:gender"), width = player_meta:get_float("character_creator:width"), height = player_meta:get_float("character_creator:height"), indexes = table.copy(indexes) } end -- Gender do if fields.male then player_meta:set_string("character_creator:gender", "Male") player_meta:set_float("character_creator:width", 1) player_meta:set_float("character_creator:height", 1) end if fields.female then player_meta:set_string("character_creator:gender", "Female") player_meta:set_float("character_creator:width", 0.95) player_meta:set_float("character_creator:height", 1) end end -- Height do local height = tonumber(player_meta:get_float("character_creator:height")) if fields.taller and height < 1.25 then player_meta:set_float("character_creator:height", height + 0.05) end if fields.shorter and height > 0.75 then player_meta:set_float("character_creator:height", height - 0.05) end end -- Width do local width = tonumber(player_meta:get_float("character_creator:width")) if fields.wider and width < 1.25 then player_meta:set_float("character_creator:width", width + 0.05) end if fields.thinner and width > 0.75 then player_meta:set_float("character_creator:width", width - 0.05) end end -- Switch skin do local function switch_skin(data_name, next_index) if not indexes[data_name] or not skins_array[data_name] then return -- Supplied invalid data end local index = indexes[data_name] + next_index local max = #skins_array[data_name] if index == 0 then index = max elseif index == (max + 1) then index = 1 end indexes[data_name] = index end for field in pairs(fields) do if field:find("_back$") then local data_name = field:match("(.+)_back$") switch_skin(data_name, -1) elseif field:find("_next$") then local data_name = field:match("(.+)_next$") switch_skin(data_name, 1) end end end -- Close or update do local quit = false if fields.cancel then local temp = skin_temp[player] player_meta:set_string("character_creator:gender", temp.gender) player_meta:set_float("character_creator:width", temp.width) player_meta:set_float("character_creator:height", temp.height) skin_indexes[player] = table.copy(temp.indexes) skin_temp[player] = nil quit = true elseif fields.quit then skin_temp[player] = nil quit = true end change_skin(player) player_api.set_skin(player, "character_creator:"..player:get_player_name(), false, true) if not quit then show_formspec(player) end end end) minetest.register_chatcommand("character_creator", { func = function(name) local player = minetest.get_player_by_name(name) if player then show_formspec(player) end end }) if minetest.global_exists("unified_inventory") then unified_inventory.register_button("character_creator", { type = "image", image = "inventory_plus_character_creator.png", action = show_formspec }) elseif minetest.global_exists("inventory_plus") then minetest.register_on_joinplayer(function(player) inventory_plus.register_button(player, "character_creator", "Character Creator") end) minetest.register_on_player_receive_fields(function(player, _, fields) if fields.character_creator then show_formspec(player) end end) elseif minetest.get_modpath("sfinv_buttons") then sfinv_buttons.register_button("character_creator", { image = "inventory_plus_character_creator.png", title = "Character Creator", action = show_formspec, }) elseif minetest.global_exists("sfinv") and sfinv.enabled then local old_func = sfinv.pages["sfinv:crafting"].get sfinv.override_page("sfinv:crafting", { get = function(self, player, context) local fs = old_func(self, player, context) return fs .. "image_button[0,0;1,1;inventory_plus_character_creator.png;character_creator;]" end }) minetest.register_on_player_receive_fields(function(player, formname, fields) if fields.character_creator then show_formspec(player) return true end end) end