local storage = minetest.get_mod_storage() playersettings = {} playersettings.registered = {} playersettings.settingslist = {} playersettings.highlighted = {} playersettings.form = "formspec_version[4]".. "size[15,15]" .. "button[12.5,13.5;2,1;quit;Save and Exit]".. "textlist[0.5,0.5;7,14;settinglist;%s;%d;false]".. "label[8,2;%s]".. "label[8,3;%s]".. "%s" function playersettings.openform(player) if type(player) == "string" then player = minetest.get_player_by_name(player) end local name = player:get_player_name() if #playersettings.settingslist < 1 then local form = "formspec_version[4]size[12,3]label[0.5,1;No settings currently exist.]label[0.5,1.5;To use the settings menu, one or more mods that provide some settings must be installed.]button_exit[5,2;2,1;quit;Close]" minetest.show_formspec(name,"playersettings:nosettings",form) return end if not playersettings.highlighted[name] then playersettings.highlighted[name] = 1 end local settingslist = "" for _,setting in ipairs(playersettings.settingslist) do settingslist = settingslist..minetest.formspec_escape(playersettings.registered[setting].shortdesc).."," end settingslist = settingslist:sub(1,-2) local settingname = playersettings.settingslist[playersettings.highlighted[name]] local def = playersettings.registered[settingname] local extras = "" if def.type == "boolean" then extras = "checkbox[8,8;checkbox;Enabled;%s]" extras = extras:format(playersettings.get(name,settingname) and "true" or "false") elseif def.type == "string" then extras = "field[8,8;6,1;field;Enter value:;%s]label[8,9.5;Allowed values: %s]field_close_on_enter[field;false]" extras = extras:format(minetest.formspec_escape(playersettings.get(name,settingname)),minetest.formspec_escape(def.values or "any string")) elseif def.type == "number" then extras = "field[8,8;6,1;field;Enter value:;%s]label[8,9.5;Allowed values: %s]field_close_on_enter[field;false]" local allowed = "any number" if def.min and def.max then allowed = "numbers "..def.min.." to "..def.max elseif def.max then allowed = "numbers up to "..def.max elseif def.min then allowed = "numbers "..def.min.." and up" end if def.integer then allowed = allowed.." (whole numbers only)" end extras = extras:format(minetest.formspec_escape(playersettings.get(name,settingname)),allowed) elseif def.type == "enum" then extras = "dropdown[8,8;6,1;dropdown;%s;%d;false]" local choices = "" local current = playersettings.get(name,settingname) local selected = 1 for k,v in ipairs(def.values) do choices = choices..minetest.formspec_escape(v).."," if v == current then selected = k end end choices = choices:sub(1,-2) extras = extras:format(choices,selected) end local form = playersettings.form:format(settingslist,playersettings.highlighted[name],minetest.formspec_escape(def.shortdesc),minetest.formspec_escape(def.longdesc),extras) minetest.show_formspec(name,"playersettings:settings",form) end function playersettings.handleform(player,form,fields) if form ~= "playersettings:settings" then return end local name = player:get_player_name() local settingname = playersettings.settingslist[playersettings.highlighted[name]] local def = playersettings.registered[settingname] if def.type == "boolean" and fields.checkbox then playersettings.set(name,settingname,fields.checkbox == "true") elseif def.type == "enum" and fields.dropdown then playersettings.set(name,settingname,fields.dropdown) elseif def.type == "number" and fields.field then local value = tonumber(fields.field) if value and ((not def.max) or (value <= def.max)) and ((not def.min) or (value >= def.min)) then if def.integer then value = math.floor(value) end playersettings.set(name,settingname,value) end elseif def.type == "string" and fields.field then playersettings.set(name,settingname,fields.field) end if fields.settinglist then local exp = minetest.explode_textlist_event(fields.settinglist) if exp.type == "CHG" then playersettings.highlighted[name] = exp.index playersettings.openform(player) end end if fields.quit then minetest.close_formspec(name,"playersettings:settings") end end function playersettings.getdefault(setting) local def = playersettings.registered[setting] if def.default ~= nil then return def.default end if def.type == "boolean" then return false elseif def.type == "number" then return def.min or 0 elseif def.type == "string" then return "" end end function playersettings.get(name,setting) assert(type(name) == "string",string.format("Invalid player name (expected string, got %s)",type(name))) assert(type(setting) == "string",string.format("Invalid setting name (expected string, got %s)",type(setting))) assert(playersettings.registered[setting],"No such setting: "..setting) local value = minetest.deserialize(storage:get_string(string.format("%s|%s",name,setting))) if value then return value else return playersettings.getdefault(setting) end end function playersettings.set(name,setting,value) assert(type(name) == "string",string.format("Invalid player name (expected string, got %s)",type(name))) assert(type(setting) == "string",string.format("Invalid setting name (expected string, got %s)",type(setting))) assert(playersettings.registered[setting],"No such setting: "..setting) local old = playersettings.get(name,setting) local def = playersettings.registered[setting] if def.onchange then if not def.onchange(name,old,value) then return end end storage:set_string(string.format("%s|%s",name,setting),minetest.serialize(value)) if def.afterchange then def.afterchange(name,old,value) end end function playersettings.register(setting,def) assert(type(setting) == "string",string.format("Invalid setting name (expected string, got %s)",type(setting))) assert(not playersettings.registered[setting],string.format("Setting %s already defined",setting)) assert(type(def) == "table",string.format("Invalid setting definition (expected table, got %s)",type(def))) table.insert(playersettings.settingslist,setting) table.sort(playersettings.settingslist) playersettings.registered[setting] = def end function playersettings.onjoin(player) local name = player:get_player_name() for setting,def in pairs(playersettings.registered) do if type(def.onjoin) == "function" then def.onjoin(name,playersettings.get(name,setting)) end end end if minetest.get_modpath("unified_inventory") then unified_inventory.register_button("playersettings", { action = playersettings.openform, tooltip = "Settings", type = "image", image = "playersettings_settings_button.png" } ) end minetest.register_chatcommand("settings",{ description = "Open player settings menu", func = playersettings.openform, }) minetest.register_on_joinplayer(playersettings.onjoin) minetest.register_on_player_receive_fields(playersettings.handleform)