mirror of
https://github.com/luanti-org/luanti.git
synced 2025-07-22 17:18:39 +00:00
Merge branch 'master' into master
This commit is contained in:
commit
80c0410442
769 changed files with 137450 additions and 85683 deletions
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
-- Global menu data
|
||||
menudata = {}
|
||||
|
@ -34,7 +21,6 @@ function check_cache_age(key, max_age)
|
|||
end
|
||||
|
||||
function core.on_before_close()
|
||||
-- called before the menu is closed, either exit or to join a game
|
||||
cache_settings:write()
|
||||
end
|
||||
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2018-24 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2018-24 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
if not core.get_http_api then
|
||||
return
|
||||
|
@ -41,6 +28,7 @@ contentdb = {
|
|||
REASON_DEPENDENCY = "dependency",
|
||||
}
|
||||
|
||||
-- API documentation: https://content.luanti.org/help/api/
|
||||
|
||||
local function get_download_url(package, reason)
|
||||
local base_url = core.settings:get("contentdb_url")
|
||||
|
@ -182,14 +170,16 @@ function contentdb.get_package_by_id(id)
|
|||
end
|
||||
|
||||
|
||||
function contentdb.calculate_package_id(type, author, name)
|
||||
local id = author:lower() .. "/"
|
||||
local function strip_game_suffix(type, name)
|
||||
if (type == nil or type == "game") and #name > 5 and name:sub(#name - 4) == "_game" then
|
||||
id = id .. name:sub(1, #name - 5)
|
||||
return name:sub(1, #name - 5)
|
||||
else
|
||||
id = id .. name
|
||||
return name
|
||||
end
|
||||
return id
|
||||
end
|
||||
|
||||
function contentdb.calculate_package_id(type, author, name)
|
||||
return author:lower() .. "/" .. strip_game_suffix(type, name)
|
||||
end
|
||||
|
||||
|
||||
|
@ -398,7 +388,6 @@ local function fetch_pkgs()
|
|||
local url = base_url ..
|
||||
"/api/packages/?type=mod&type=game&type=txp&protocol_version=" ..
|
||||
core.get_max_supp_proto() .. "&engine_version=" .. core.urlencode(version.string)
|
||||
|
||||
for _, item in pairs(core.settings:get("contentdb_flag_blacklist"):split(",")) do
|
||||
item = item:trim()
|
||||
if item ~= "" then
|
||||
|
@ -406,19 +395,11 @@ local function fetch_pkgs()
|
|||
end
|
||||
end
|
||||
|
||||
local languages
|
||||
local current_language = core.get_language()
|
||||
if current_language ~= "" then
|
||||
languages = { current_language, "en;q=0.8" }
|
||||
else
|
||||
languages = { "en" }
|
||||
end
|
||||
|
||||
local http = core.get_http_api()
|
||||
local response = http.fetch_sync({
|
||||
url = url,
|
||||
extra_headers = {
|
||||
"Accept-Language: " .. table.concat(languages, ", ")
|
||||
core.get_http_accept_languages()
|
||||
},
|
||||
})
|
||||
if not response.succeeded then
|
||||
|
@ -448,7 +429,7 @@ function contentdb.set_packages_from_api(packages)
|
|||
-- We currently don't support name changing
|
||||
local suffix = "/" .. package.name
|
||||
if alias:sub(-#suffix) == suffix then
|
||||
contentdb.aliases[alias:lower()] = package.id
|
||||
contentdb.aliases[strip_game_suffix(packages.type, alias:lower())] = package.id
|
||||
end
|
||||
end
|
||||
end
|
||||
|
@ -596,57 +577,54 @@ function contentdb.filter_packages(query, by_type)
|
|||
end
|
||||
|
||||
|
||||
function contentdb.get_full_package_info(package, callback)
|
||||
assert(package)
|
||||
if package.full_info then
|
||||
callback(package.full_info)
|
||||
return
|
||||
end
|
||||
|
||||
local function fetch(params)
|
||||
local version = core.get_version()
|
||||
local base_url = core.settings:get("contentdb_url")
|
||||
|
||||
local languages
|
||||
local current_language = core.get_language()
|
||||
if current_language ~= "" then
|
||||
languages = { current_language, "en;q=0.8" }
|
||||
else
|
||||
languages = { "en" }
|
||||
local function get_package_info(key, path)
|
||||
return function(package, callback)
|
||||
assert(package)
|
||||
if package[key] then
|
||||
callback(package[key])
|
||||
return
|
||||
end
|
||||
|
||||
local url = base_url ..
|
||||
"/api/packages/" .. params.package.url_part .. "/for-client/?" ..
|
||||
"protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
|
||||
"&engine_version=" .. core.urlencode(version.string) ..
|
||||
"&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
|
||||
"&include_images=false"
|
||||
local http = core.get_http_api()
|
||||
local response = http.fetch_sync({
|
||||
url = url,
|
||||
extra_headers = {
|
||||
"Accept-Language: " .. table.concat(languages, ", ")
|
||||
},
|
||||
})
|
||||
if not response.succeeded then
|
||||
return nil
|
||||
local function fetch(params)
|
||||
local version = core.get_version()
|
||||
local base_url = core.settings:get("contentdb_url")
|
||||
local url = base_url ..
|
||||
"/api/packages/" .. params.package.url_part .. params.path .. "?" ..
|
||||
"protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
|
||||
"&engine_version=" .. core.urlencode(version.string) ..
|
||||
"&formspec_version=" .. core.urlencode(core.get_formspec_version()) ..
|
||||
"&include_images=false"
|
||||
local http = core.get_http_api()
|
||||
local response = http.fetch_sync({
|
||||
url = url,
|
||||
extra_headers = {
|
||||
core.get_http_accept_languages()
|
||||
},
|
||||
})
|
||||
if not response.succeeded then
|
||||
return nil
|
||||
end
|
||||
|
||||
return core.parse_json(response.data)
|
||||
end
|
||||
|
||||
return core.parse_json(response.data)
|
||||
end
|
||||
local function my_callback(value)
|
||||
package[key] = value
|
||||
callback(value)
|
||||
end
|
||||
|
||||
local function my_callback(value)
|
||||
package.full_info = value
|
||||
callback(value)
|
||||
end
|
||||
|
||||
if not core.handle_async(fetch, { package = package }, my_callback) then
|
||||
core.log("error", "ERROR: async event failed")
|
||||
callback(nil)
|
||||
if not core.handle_async(fetch, { package = package, path = path }, my_callback) then
|
||||
core.log("error", "ERROR: async event failed")
|
||||
callback(nil)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
contentdb.get_full_package_info = get_package_info("full_info", "/for-client/")
|
||||
contentdb.get_package_reviews = get_package_info("reviews", "/for-client/reviews/")
|
||||
|
||||
|
||||
function contentdb.get_formspec_padding()
|
||||
-- Padding is increased on Android to account for notches
|
||||
-- TODO: use Android API to determine size of cut outs
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2018-20 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2018-20 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
if not core.get_http_api then
|
||||
function create_contentdb_dlg()
|
||||
|
@ -323,9 +310,17 @@ local function get_formspec(dlgdata)
|
|||
})
|
||||
local img_w = cell_h * 3 / 2
|
||||
|
||||
-- Use as much of the available space as possible (so no padding on the
|
||||
-- right/bottom), but don't quite allow the text to touch the border.
|
||||
local text_w = cell_w - img_w - 0.25 - 0.025
|
||||
local text_h = cell_h - 0.25 - 0.025
|
||||
|
||||
local start_idx = (cur_page - 1) * num_per_page + 1
|
||||
for i=start_idx, math.min(#contentdb.packages, start_idx+num_per_page-1) do
|
||||
local package = contentdb.packages[i]
|
||||
local text = core.colorize(mt_color_green, package.title) ..
|
||||
core.colorize("#BFBFBF", " by " .. package.author) .. "\n" ..
|
||||
package.short_description
|
||||
|
||||
table.insert_all(formspec, {
|
||||
"container[",
|
||||
|
@ -340,13 +335,14 @@ local function get_formspec(dlgdata)
|
|||
"image[0,0;", img_w, ",", cell_h, ";",
|
||||
core.formspec_escape(get_screenshot(package, package.thumbnail, 2)), "]",
|
||||
|
||||
"label[", img_w + 0.25 + 0.05, ",0.5;",
|
||||
core.formspec_escape(
|
||||
core.colorize(mt_color_green, package.title) ..
|
||||
core.colorize("#BFBFBF", " by " .. package.author)), "]",
|
||||
"label[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
|
||||
core.formspec_escape(text), "]",
|
||||
|
||||
"textarea[", img_w + 0.25, ",0.75;", cell_w - img_w - 0.25, ",", cell_h - 0.75, ";;;",
|
||||
core.formspec_escape(package.short_description), "]",
|
||||
-- Add a tooltip in case the label overflows and the short description is cut off.
|
||||
"tooltip[", img_w + 0.25, ",0.25;", text_w, ",", text_h, ";",
|
||||
-- Text in tooltips doesn't wrap automatically, so we do it manually to
|
||||
-- avoid everything being one long line.
|
||||
core.formspec_escape(core.wrap_text(package.short_description, 80)), "]",
|
||||
|
||||
"style[view_", i, ";border=false]",
|
||||
"style[view_", i, ":hovered;bgimg=", core.formspec_escape(defaulttexturedir .. "button_hover_semitrans.png"), "]",
|
||||
|
@ -362,7 +358,7 @@ local function get_formspec(dlgdata)
|
|||
end
|
||||
|
||||
table.insert_all(formspec, {
|
||||
"container[", cell_w - 0.625,",", 0.25, "]",
|
||||
"container[", cell_w - 0.625,",", 0.125, "]",
|
||||
})
|
||||
|
||||
if package.downloading then
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2018-24 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2018-24 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
local function is_still_visible(dlg)
|
||||
local this = ui.find_by_name("install_dialog")
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2018-24 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2018-24 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
function get_formspec(data)
|
||||
local package = data.package
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2018-24 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2018-24 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
|
||||
local function get_info_formspec(size, padding, text)
|
||||
|
@ -32,6 +19,7 @@ end
|
|||
|
||||
|
||||
local function get_formspec(data)
|
||||
local package = data.package
|
||||
local window_padding = contentdb.get_formspec_padding()
|
||||
local size = contentdb.get_formspec_size()
|
||||
size.x = math.min(size.x, 20)
|
||||
|
@ -42,7 +30,7 @@ local function get_formspec(data)
|
|||
if not data.loading and not data.loading_error then
|
||||
data.loading = true
|
||||
|
||||
contentdb.get_full_package_info(data.package, function(info)
|
||||
contentdb.get_full_package_info(package, function(info)
|
||||
data.loading = false
|
||||
|
||||
if info == nil then
|
||||
|
@ -61,7 +49,7 @@ local function get_formspec(data)
|
|||
-- check to see if that happened
|
||||
if not data.info then
|
||||
if data.loading_error then
|
||||
return get_info_formspec(size, window_padding, fgettext("No packages could be retrieved"))
|
||||
return get_info_formspec(size, window_padding, fgettext("Error loading package information"))
|
||||
end
|
||||
return get_info_formspec(size, window_padding, fgettext("Loading..."))
|
||||
end
|
||||
|
@ -103,15 +91,15 @@ local function get_formspec(data)
|
|||
|
||||
local left_button_rect = "0,0;2.875,1"
|
||||
local right_button_rect = "3.125,0;2.875,1"
|
||||
if data.package.downloading then
|
||||
if package.downloading then
|
||||
formspec[#formspec + 1] = "animated_image[5,0;1,1;downloading;"
|
||||
formspec[#formspec + 1] = core.formspec_escape(defaulttexturedir)
|
||||
formspec[#formspec + 1] = "cdb_downloading.png;3;400;]"
|
||||
elseif data.package.queued then
|
||||
elseif package.queued then
|
||||
formspec[#formspec + 1] = "style[queued;border=false]"
|
||||
formspec[#formspec + 1] = "image_button[5,0;1,1;" .. core.formspec_escape(defaulttexturedir)
|
||||
formspec[#formspec + 1] = "cdb_queued.png;queued;]"
|
||||
elseif not data.package.path then
|
||||
elseif not package.path then
|
||||
formspec[#formspec + 1] = "style[install;bgcolor=green]"
|
||||
formspec[#formspec + 1] = "button["
|
||||
formspec[#formspec + 1] = right_button_rect
|
||||
|
@ -119,7 +107,7 @@ local function get_formspec(data)
|
|||
formspec[#formspec + 1] = fgettext("Install [$1]", info.download_size)
|
||||
formspec[#formspec + 1] = "]"
|
||||
else
|
||||
if data.package.installed_release < data.package.release then
|
||||
if package.installed_release < package.release then
|
||||
-- The install_ action also handles updating
|
||||
formspec[#formspec + 1] = "style[install;bgcolor=#28ccdf]"
|
||||
formspec[#formspec + 1] = "button["
|
||||
|
@ -137,10 +125,12 @@ local function get_formspec(data)
|
|||
formspec[#formspec + 1] = "]"
|
||||
end
|
||||
|
||||
local review_count = info.reviews.positive + info.reviews.neutral + info.reviews.negative
|
||||
local current_tab = data.current_tab or 1
|
||||
local tab_titles = {
|
||||
fgettext("Description"),
|
||||
fgettext("Information"),
|
||||
fgettext("Reviews") .. core.formspec_escape(" [" .. review_count .. "]"),
|
||||
}
|
||||
|
||||
local tab_body_height = bottom_buttons_y - 2.8
|
||||
|
@ -162,8 +152,8 @@ local function get_formspec(data)
|
|||
local winfo = core.get_window_info()
|
||||
local fs_to_px = winfo.size.x / winfo.max_formspec_size.x
|
||||
for i, ss in ipairs(info.screenshots) do
|
||||
local path = get_screenshot(data.package, ss.url, 2)
|
||||
hypertext = hypertext .. "<action name=\"ss_" .. i .. "\"><img name=\"" ..
|
||||
local path = get_screenshot(package, ss.url, 2)
|
||||
hypertext = hypertext .. "<action name=\"ss_".. i .. "\"><img name=\"" ..
|
||||
core.hypertext_escape(path) .. "\" width=" .. (3 * fs_to_px) ..
|
||||
" height=" .. (2 * fs_to_px) .. "></action>"
|
||||
if i ~= #info.screenshots then
|
||||
|
@ -194,22 +184,54 @@ local function get_formspec(data)
|
|||
|
||||
hypertext = hypertext .. "\n\n" .. info.long_description.body
|
||||
|
||||
-- Fix the path to blank.png. This is needed for bullet indentation.
|
||||
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
|
||||
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
|
||||
|
||||
table.insert_all(formspec, {
|
||||
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
|
||||
";desc;", core.formspec_escape(hypertext), "]",
|
||||
|
||||
})
|
||||
|
||||
elseif current_tab == 2 then
|
||||
local hypertext = info.info_hypertext.head .. info.info_hypertext.body
|
||||
|
||||
table.insert_all(formspec, {
|
||||
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
|
||||
";info;", core.formspec_escape(hypertext), "]",
|
||||
})
|
||||
elseif current_tab == 3 then
|
||||
if not package.reviews and not data.reviews_error and not data.reviews_loading then
|
||||
data.reviews_loading = true
|
||||
|
||||
contentdb.get_package_reviews(package, function(reviews)
|
||||
if not reviews then
|
||||
data.reviews_error = true
|
||||
end
|
||||
ui.update()
|
||||
end)
|
||||
end
|
||||
|
||||
if package.reviews then
|
||||
local hypertext = package.reviews.head .. package.reviews.body
|
||||
-- Provide correct path to blank.png image. This is needed for bullet indentation.
|
||||
hypertext = hypertext:gsub("<img name=\"?blank.png\"? ",
|
||||
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "blank.png\" ")
|
||||
-- Placeholders in reviews hypertext for icons
|
||||
hypertext = hypertext:gsub("<thumbsup>",
|
||||
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_thumb_up.png\" width=24>")
|
||||
hypertext = hypertext:gsub("<thumbsdown>",
|
||||
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_thumb_down.png\" width=24>")
|
||||
hypertext = hypertext:gsub("<neutral>",
|
||||
"<img name=\"" .. core.hypertext_escape(defaulttexturedir) .. "contentdb_neutral.png\" width=24>")
|
||||
table.insert_all(formspec, {
|
||||
"hypertext[0,0;", W, ",", tab_body_height - 0.375,
|
||||
";reviews;", core.formspec_escape(hypertext), "]",
|
||||
})
|
||||
elseif data.reviews_error then
|
||||
table.insert_all(formspec, {"label[2,2;", fgettext("Error loading reviews"), "]"} )
|
||||
else
|
||||
table.insert_all(formspec, {"label[2,2;", fgettext("Loading..."), "]"} )
|
||||
end
|
||||
else
|
||||
error("Unknown tab " .. current_tab)
|
||||
end
|
||||
|
@ -269,9 +291,10 @@ local function handle_submit(this, fields)
|
|||
end
|
||||
|
||||
if fields.open_contentdb then
|
||||
local url = ("%s/packages/%s/?protocol_version=%d"):format(
|
||||
core.settings:get("contentdb_url"), package.url_part,
|
||||
core.get_max_supp_proto())
|
||||
local version = core.get_version()
|
||||
local url = core.settings:get("contentdb_url") .. "/packages/" .. package.url_part ..
|
||||
"/?protocol_version=" .. core.urlencode(core.get_max_supp_proto()) ..
|
||||
"&engine_version=" .. core.urlencode(version.string)
|
||||
core.open_url(url)
|
||||
return true
|
||||
end
|
||||
|
@ -295,7 +318,8 @@ local function handle_submit(this, fields)
|
|||
end
|
||||
|
||||
if handle_hypertext_event(this, fields.desc, info.long_description) or
|
||||
handle_hypertext_event(this, fields.info, info.info_hypertext) then
|
||||
handle_hypertext_event(this, fields.info, info.info_hypertext) or
|
||||
(package.reviews and handle_hypertext_event(this, fields.reviews, package.reviews)) then
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2023 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2023 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
local path = core.get_mainmenu_path() .. DIR_DELIM .. "content"
|
||||
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2013 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2013 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
local function get_last_folder(text,count)
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2023-24 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2023-24 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
|
||||
-- Screenshot
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2022 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2022 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
local mods_dir = "/tmp/.minetest/mods"
|
||||
local games_dir = "/tmp/.minetest/games"
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2023 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2023 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
|
||||
update_detector = {}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
{
|
||||
"#": "https://github.com/orgs/minetest/teams/engine/members",
|
||||
"#": "https://github.com/orgs/luanti-org/teams/engine/members",
|
||||
"core_developers": [
|
||||
"Perttu Ahola (celeron55) <celeron55@gmail.com> [Project founder]",
|
||||
"sfan5 <sfan5@live.de>",
|
||||
|
@ -15,7 +15,8 @@
|
|||
"Gregor Parzefall (grorp)",
|
||||
"Lars Müller (luatic)",
|
||||
"cx384",
|
||||
"sfence"
|
||||
"sfence",
|
||||
"y5nw"
|
||||
],
|
||||
"previous_core_developers": [
|
||||
"BlockMen",
|
||||
|
@ -38,7 +39,7 @@
|
|||
"Hugues Ross <hugues.ross@gmail.com>",
|
||||
"Dmitry Kostenko (x2048) <codeforsmile@gmail.com>"
|
||||
],
|
||||
"#": "Currently only https://github.com/orgs/minetest/teams/triagers/members",
|
||||
"#": "Currently only https://github.com/orgs/luanti-org/teams/triagers/members",
|
||||
"core_team": [
|
||||
"Zughy [Issue triager]",
|
||||
"wsor [Issue triager]",
|
||||
|
@ -46,25 +47,20 @@
|
|||
],
|
||||
"#": "For updating active/previous contributors, see the script in ./util/gather_git_credits.py",
|
||||
"contributors": [
|
||||
"JosiahWI",
|
||||
"1F616EMO",
|
||||
"y5nw",
|
||||
"Erich Schubert",
|
||||
"numzero",
|
||||
"red-001 <red-001@outlook.ie>",
|
||||
"David Heidelberg",
|
||||
"Wuzzy",
|
||||
"paradust7",
|
||||
"HybridDog",
|
||||
"Zemtzov7",
|
||||
"kromka-chleba",
|
||||
"AFCMS",
|
||||
"chmodsayshello",
|
||||
"OgelGames"
|
||||
"wrrrzr",
|
||||
"siliconsniffer",
|
||||
"JosiahWI",
|
||||
"veprogames",
|
||||
"Miguel P.L",
|
||||
"AFCMS"
|
||||
],
|
||||
"previous_contributors": [
|
||||
"Ælla Chiana Moskopp (erle) <erle@dieweltistgarnichtso.net> [Logo]",
|
||||
"numzero",
|
||||
"red-001 <red-001@outlook.ie>",
|
||||
"Giuseppe Bilotta",
|
||||
"HybridDog",
|
||||
"ClobberXD",
|
||||
"Dániel Juhász (juhdanad) <juhdanad@gmail.com>",
|
||||
"MirceaKitsune <mirceakitsune@gmail.com>",
|
||||
|
@ -77,6 +73,7 @@
|
|||
"stujones11",
|
||||
"Rogier <rogier777@gmail.com>",
|
||||
"Gregory Currie (gregorycu)",
|
||||
"paradust7",
|
||||
"JacobF",
|
||||
"Jeija <jeija@mesecons.net>"
|
||||
]
|
||||
|
|
|
@ -21,7 +21,7 @@ local function clients_list_formspec(dialogdata)
|
|||
"size[6,9.5]",
|
||||
TOUCH_GUI and "padding[0.01,0.01]" or "",
|
||||
"hypertext[0,0;6,1.5;;<global margin=5 halign=center valign=middle>",
|
||||
fgettext("This is the list of clients connected to\n$1",
|
||||
fgettext("Players connected to\n$1",
|
||||
"<b>" .. core.hypertext_escape(servername) .. "</b>") .. "]",
|
||||
"textlist[0.5,1.5;5,6.8;;" .. fmt_formspec_list(clients_list) .. "]",
|
||||
"button[1.5,8.5;3,0.8;quit;OK]"
|
||||
|
@ -32,7 +32,7 @@ end
|
|||
|
||||
local function clients_list_buttonhandler(this, fields)
|
||||
if fields.quit then
|
||||
this:delete()
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2013 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2013 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
@ -299,7 +286,7 @@ local function handle_buttons(this, fields)
|
|||
worldfile:set("load_mod_" .. mod.name, mod.virtual_path)
|
||||
was_set[mod.name] = true
|
||||
elseif not was_set[mod.name] then
|
||||
worldfile:set("load_mod_" .. mod.name, "false")
|
||||
worldfile:remove("load_mod_" .. mod.name)
|
||||
end
|
||||
elseif mod.enabled then
|
||||
gamedata.errormessage = fgettext_ne("Failed to enable mo" ..
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
local function table_to_flags(ftable)
|
||||
-- Convert e.g. { jungles = true, caves = false } to "jungles,nocaves"
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
|
||||
local function delete_world_formspec(dialogdata)
|
||||
|
|
108
builtin/mainmenu/dlg_rebind_keys.lua
Normal file
108
builtin/mainmenu/dlg_rebind_keys.lua
Normal file
|
@ -0,0 +1,108 @@
|
|||
-- Luanti
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
-- Modified based on dlg_reinstall_mtg.lua
|
||||
-- Note that this is only needed for migrating from <5.11 to 5.12.
|
||||
|
||||
local doc_url = "https://docs.luanti.org/for-players/controls/"
|
||||
local SETTING_NAME = "no_keycode_migration_warning"
|
||||
|
||||
local function get_formspec(dialogdata)
|
||||
local markup = table.concat({
|
||||
"<big>" .. hgettext("Keybindings changed") .. "</big>",
|
||||
hgettext("The input handling system was reworked in Luanti 5.12.0."),
|
||||
hgettext("As a result, your keybindings may have been changed."),
|
||||
hgettext("Check out the key settings or refer to the documentation:"),
|
||||
("<action name='doc_url'><style color='cyan' hovercolor='orangered'>%s</style></action>"):format(doc_url),
|
||||
}, "\n")
|
||||
|
||||
return table.concat({
|
||||
"formspec_version[6]",
|
||||
"size[12,7]",
|
||||
"hypertext[0.5,0.5;11,4.7;text;", core.formspec_escape(markup), "]",
|
||||
"container[0.5,5.7]",
|
||||
"button[0,0;4,0.8;dismiss;", fgettext("Close"), "]",
|
||||
"button[4.5,0;6.5,0.8;reconfigure;", fgettext("Open settings"), "]",
|
||||
"container_end[]",
|
||||
})
|
||||
end
|
||||
|
||||
local function close_dialog(this)
|
||||
cache_settings:set_bool(SETTING_NAME, true)
|
||||
this:delete()
|
||||
end
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
if fields.reconfigure then
|
||||
close_dialog(this)
|
||||
|
||||
local maintab = ui.find_by_name("maintab")
|
||||
|
||||
local dlg = create_settings_dlg("controls_keyboard_and_mouse")
|
||||
dlg:set_parent(maintab)
|
||||
maintab:hide()
|
||||
dlg:show()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.dismiss then
|
||||
close_dialog(this)
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.text == "action:doc_url" then
|
||||
core.open_url(doc_url)
|
||||
end
|
||||
end
|
||||
|
||||
local function eventhandler(event)
|
||||
if event == "DialogShow" then
|
||||
mm_game_theme.set_engine()
|
||||
return true
|
||||
elseif event == "MenuQuit" then
|
||||
-- Don't allow closing the dialog with ESC, but still allow exiting
|
||||
-- Luanti
|
||||
core.close()
|
||||
return true
|
||||
end
|
||||
return false
|
||||
end
|
||||
|
||||
local function create_rebind_keys_dlg()
|
||||
local dlg = dialog_create("dlg_rebind_keys", get_formspec,
|
||||
buttonhandler, eventhandler)
|
||||
return dlg
|
||||
end
|
||||
|
||||
function migrate_keybindings()
|
||||
-- Show migration dialog if the user upgraded from an earlier version
|
||||
-- and this has not yet been shown before, *or* if keys settings had to be changed
|
||||
if core.is_first_run then
|
||||
cache_settings:set_bool(SETTING_NAME, true)
|
||||
end
|
||||
local has_migration = not cache_settings:get_bool(SETTING_NAME)
|
||||
|
||||
-- normalize all existing key settings, this converts them from KEY_KEY_C to SYSTEM_SCANCODE_6
|
||||
local settings = core.settings:to_table()
|
||||
for name, value in pairs(settings) do
|
||||
if name:match("^keymap_") then
|
||||
local normalized = core.normalize_keycode(value)
|
||||
if value ~= normalized then
|
||||
has_migration = true
|
||||
core.settings:set(name, normalized)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if not has_migration then
|
||||
return
|
||||
end
|
||||
|
||||
local maintab = ui.find_by_name("maintab")
|
||||
|
||||
local dlg = create_rebind_keys_dlg()
|
||||
dlg:set_parent(maintab)
|
||||
maintab:hide()
|
||||
dlg:show()
|
||||
ui.update()
|
||||
end
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2022 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2022 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
|
|
@ -1,25 +1,12 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2023 Gregor Parzefall
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2023 Gregor Parzefall
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
---- IMPORTANT ----
|
||||
-- This whole file can be removed after a while.
|
||||
-- It was only directly useful for upgrades from 5.7.0 to 5.8.0, but
|
||||
-- maybe some odd fellow directly upgrades from 5.6.1 to 5.9.0 in the future...
|
||||
-- see <https://github.com/minetest/minetest/pull/13850> in case it's not obvious
|
||||
-- see <https://github.com/luanti-org/luanti/pull/13850> in case it's not obvious
|
||||
---- ----
|
||||
|
||||
local SETTING_NAME = "no_mtg_notification"
|
||||
|
@ -67,10 +54,10 @@ end
|
|||
|
||||
local function get_formspec(dialogdata)
|
||||
local markup = table.concat({
|
||||
"<big>", fgettext("Minetest Game is no longer installed by default"), "</big>\n",
|
||||
fgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
|
||||
"<big>", hgettext("Minetest Game is no longer installed by default"), "</big>\n",
|
||||
hgettext("For a long time, Luanti shipped with a default game called \"Minetest Game\". " ..
|
||||
"Since version 5.8.0, Luanti ships without a default game."), "\n",
|
||||
fgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
|
||||
hgettext("If you want to continue playing in your Minetest Game worlds, you need to reinstall Minetest Game."),
|
||||
})
|
||||
|
||||
return table.concat({
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
|
|
105
builtin/mainmenu/dlg_server_list_mods.lua
Normal file
105
builtin/mainmenu/dlg_server_list_mods.lua
Normal file
|
@ -0,0 +1,105 @@
|
|||
-- Luanti
|
||||
-- Copyright (C) 2024 cx384
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
local function get_formspec(dialogdata)
|
||||
local TOUCH_GUI = core.settings:get_bool("touch_gui")
|
||||
local server = dialogdata.server
|
||||
local group_by_prefix = dialogdata.group_by_prefix
|
||||
local expand_all = dialogdata.expand_all
|
||||
|
||||
-- A wrongly behaving server may send ill formed mod names
|
||||
table.sort(server.mods)
|
||||
|
||||
local cells = {}
|
||||
if group_by_prefix then
|
||||
local function get_prefix(mod)
|
||||
return mod:match("[^_]*")
|
||||
end
|
||||
local count = {}
|
||||
for _, mod in ipairs(server.mods) do
|
||||
local prefix = get_prefix(mod)
|
||||
count[prefix] = (count[prefix] or 0) + 1
|
||||
end
|
||||
local last_prefix
|
||||
local function add_row(depth, mod)
|
||||
table.insert(cells, ("%d"):format(depth))
|
||||
table.insert(cells, mod)
|
||||
end
|
||||
for i, mod in ipairs(server.mods) do
|
||||
local prefix = get_prefix(mod)
|
||||
if last_prefix == prefix then
|
||||
add_row(1, mod)
|
||||
elseif count[prefix] > 1 then
|
||||
add_row(0, prefix)
|
||||
add_row(1, mod)
|
||||
else
|
||||
add_row(0, mod)
|
||||
end
|
||||
last_prefix = prefix
|
||||
end
|
||||
else
|
||||
cells = table.copy(server.mods)
|
||||
end
|
||||
|
||||
for i, cell in ipairs(cells) do
|
||||
cells[i] = core.formspec_escape(cell)
|
||||
end
|
||||
cells = table.concat(cells, ",")
|
||||
|
||||
local heading
|
||||
if server.gameid then
|
||||
heading = fgettext("The $1 server uses a game called $2 and the following mods:",
|
||||
"<b>" .. core.hypertext_escape(server.name) .. "</b>",
|
||||
"<style font=mono>" .. core.hypertext_escape(server.gameid) .. "</style>")
|
||||
else
|
||||
heading = fgettext("The $1 server uses the following mods:",
|
||||
"<b>" .. core.hypertext_escape(server.name) .. "</b>")
|
||||
end
|
||||
|
||||
local formspec = {
|
||||
"formspec_version[8]",
|
||||
"size[8,9.5]",
|
||||
TOUCH_GUI and "padding[0.01,0.01]" or "",
|
||||
"hypertext[0,0;8,1.5;;<global margin=5 halign=center valign=middle>", heading, "]",
|
||||
"tablecolumns[", group_by_prefix and
|
||||
(expand_all and "indent;text" or "tree;text") or "text", "]",
|
||||
"table[0.5,1.5;7,6.8;mods;", cells, "]",
|
||||
"checkbox[0.5,8.7;group_by_prefix;", fgettext("Group by prefix"), ";",
|
||||
group_by_prefix and "true" or "false", "]",
|
||||
group_by_prefix and ("checkbox[0.5,9.15;expand_all;" .. fgettext("Expand all") .. ";" ..
|
||||
(expand_all and "true" or "false") .. "]") or "",
|
||||
"button[5.5,8.5;2,0.8;quit;OK]"
|
||||
}
|
||||
return table.concat(formspec, "")
|
||||
end
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
if fields.quit then
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.group_by_prefix then
|
||||
this.data.group_by_prefix = core.is_yes(fields.group_by_prefix)
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.expand_all then
|
||||
this.data.expand_all = core.is_yes(fields.expand_all)
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
function create_server_list_mods_dialog(server)
|
||||
local retval = dialog_create("dlg_server_list_mods",
|
||||
get_formspec,
|
||||
buttonhandler,
|
||||
nil)
|
||||
retval.data.group_by_prefix = false
|
||||
retval.data.expand_all = false
|
||||
retval.data.server = server
|
||||
return retval
|
||||
end
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2013 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2013 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
|
||||
mm_game_theme = {}
|
||||
|
|
|
@ -1,27 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
mt_color_grey = "#AAAAAA"
|
||||
mt_color_blue = "#6389FF"
|
||||
mt_color_lightblue = "#99CCFF"
|
||||
mt_color_green = "#72FF63"
|
||||
mt_color_dark_green = "#25C191"
|
||||
mt_color_orange = "#FF8800"
|
||||
mt_color_red = "#FF3300"
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
MAIN_TAB_W = 15.5
|
||||
MAIN_TAB_H = 7.1
|
||||
|
@ -35,6 +14,7 @@ local basepath = core.get_builtin_path()
|
|||
defaulttexturedir = core.get_texturepath_share() .. DIR_DELIM .. "base" ..
|
||||
DIR_DELIM .. "pack" .. DIR_DELIM
|
||||
|
||||
dofile(basepath .. "common" .. DIR_DELIM .. "menu.lua")
|
||||
dofile(basepath .. "common" .. DIR_DELIM .. "filterlist.lua")
|
||||
dofile(basepath .. "fstk" .. DIR_DELIM .. "buttonbar.lua")
|
||||
dofile(basepath .. "fstk" .. DIR_DELIM .. "dialog.lua")
|
||||
|
@ -47,7 +27,7 @@ dofile(menupath .. DIR_DELIM .. "game_theme.lua")
|
|||
dofile(menupath .. DIR_DELIM .. "content" .. DIR_DELIM .. "init.lua")
|
||||
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_config_world.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
|
||||
dofile(basepath .. "common" .. DIR_DELIM .. "settings" .. DIR_DELIM .. "init.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_create_world.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_delete_content.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_delete_world.lua")
|
||||
|
@ -55,7 +35,9 @@ dofile(menupath .. DIR_DELIM .. "dlg_register.lua")
|
|||
dofile(menupath .. DIR_DELIM .. "dlg_rename_modpack.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_version_info.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_reinstall_mtg.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_rebind_keys.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_clients_list.lua")
|
||||
dofile(menupath .. DIR_DELIM .. "dlg_server_list_mods.lua")
|
||||
|
||||
local tabs = {
|
||||
content = dofile(menupath .. DIR_DELIM .. "tab_content.lua"),
|
||||
|
@ -131,6 +113,7 @@ local function init_globals()
|
|||
ui.update()
|
||||
|
||||
check_reinstall_mtg()
|
||||
migrate_keybindings()
|
||||
check_new_version()
|
||||
end
|
||||
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2020 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2020 rubenwardy
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
serverlistmgr = {
|
||||
-- continent code we detected for ourselves
|
||||
|
|
|
@ -1,435 +0,0 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2022 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
local make = {}
|
||||
|
||||
|
||||
-- This file defines various component constructors, of the form:
|
||||
--
|
||||
-- make.component(setting)
|
||||
--
|
||||
-- `setting` is a table representing the settingtype.
|
||||
--
|
||||
-- A component is a table with the following:
|
||||
--
|
||||
-- * `full_width`: (Optional) true if the component shouldn't reserve space for info / reset.
|
||||
-- * `info_text`: (Optional) string, informational text shown in an info icon.
|
||||
-- * `setting`: (Optional) the setting.
|
||||
-- * `max_w`: (Optional) maximum width, `avail_w` will never exceed this.
|
||||
-- * `resettable`: (Optional) if this is true, a reset button is shown.
|
||||
-- * `get_formspec = function(self, avail_w)`:
|
||||
-- * `avail_w` is the available width for the component.
|
||||
-- * Returns `fs, used_height`.
|
||||
-- * `fs` is a string for the formspec.
|
||||
-- Components should be relative to `0,0`, and not exceed `avail_w` or the returned `used_height`.
|
||||
-- * `used_height` is the space used by components in `fs`.
|
||||
-- * `on_submit = function(self, fields, parent)`:
|
||||
-- * `fields`: submitted formspec fields
|
||||
-- * `parent`: the fstk element for the settings UI, use to show dialogs
|
||||
-- * Return true if the event was handled, to prevent future components receiving it.
|
||||
|
||||
|
||||
local function get_label(setting)
|
||||
local show_technical_names = core.settings:get_bool("show_technical_names")
|
||||
if not show_technical_names and setting.readable_name then
|
||||
return fgettext(setting.readable_name)
|
||||
end
|
||||
return setting.name
|
||||
end
|
||||
|
||||
|
||||
local function is_valid_number(value)
|
||||
return type(value) == "number" and not (value ~= value or value >= math.huge or value <= -math.huge)
|
||||
end
|
||||
|
||||
|
||||
function make.heading(text)
|
||||
return {
|
||||
full_width = true,
|
||||
get_formspec = function(self, avail_w)
|
||||
return ("label[0,0.6;%s]box[0,0.9;%f,0.05;#ccc6]"):format(core.formspec_escape(text), avail_w), 1.2
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function make.note(text)
|
||||
return {
|
||||
full_width = true,
|
||||
get_formspec = function(self, avail_w)
|
||||
-- Assuming label height 0.4:
|
||||
-- Position at y=0 to eat 0.2 of the padding above, leave 0.05.
|
||||
-- The returned used_height doesn't include padding.
|
||||
return ("label[0,0;%s]"):format(core.colorize("#bbb", core.formspec_escape(text))), 0.2
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
--- Used for string and numeric style fields
|
||||
---
|
||||
--- @param converter Function to coerce values from strings.
|
||||
--- @param validator Validator function, optional. Returns true when valid.
|
||||
--- @param stringifier Function to convert values to strings, optional.
|
||||
local function make_field(converter, validator, stringifier)
|
||||
return function(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = core.settings:get(setting.name) or setting.default
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
|
||||
avail_w - 1.5, setting.name, get_label(setting), core.formspec_escape(value))
|
||||
fs = fs .. ("field_enter_after_edit[%s;true]"):format(setting.name)
|
||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
|
||||
|
||||
return fs, 1.1
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
|
||||
local value = converter(fields[setting.name])
|
||||
if value == nil or (validator and not validator(value)) then
|
||||
return true
|
||||
end
|
||||
|
||||
if setting.min then
|
||||
value = math.max(value, setting.min)
|
||||
end
|
||||
if setting.max then
|
||||
value = math.min(value, setting.max)
|
||||
end
|
||||
core.settings:set(setting.name, (stringifier or tostring)(value))
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
make.float = make_field(tonumber, is_valid_number, function(x)
|
||||
local str = tostring(x)
|
||||
if str:match("^[+-]?%d+$") then
|
||||
str = str .. ".0"
|
||||
end
|
||||
return str
|
||||
end)
|
||||
make.int = make_field(function(x)
|
||||
local value = tonumber(x)
|
||||
return value and math.floor(value)
|
||||
end, is_valid_number)
|
||||
make.string = make_field(tostring, nil)
|
||||
|
||||
|
||||
function make.bool(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = core.settings:get_bool(setting.name, core.is_yes(setting.default))
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local fs = ("checkbox[0,0.25;%s;%s;%s]"):format(
|
||||
setting.name, get_label(setting), tostring(value))
|
||||
return fs, 0.5
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
if fields[setting.name] == nil then
|
||||
return false
|
||||
end
|
||||
|
||||
core.settings:set_bool(setting.name, core.is_yes(fields[setting.name]))
|
||||
return true
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function make.enum(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
max_w = 4.5,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = core.settings:get(setting.name) or setting.default
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local labels = setting.option_labels or {}
|
||||
|
||||
local items = {}
|
||||
for i, option in ipairs(setting.values) do
|
||||
items[i] = core.formspec_escape(labels[option] or option)
|
||||
end
|
||||
|
||||
local selected_idx = table.indexof(setting.values, value)
|
||||
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
|
||||
|
||||
fs = fs .. ("dropdown[0,0.3;%f,0.8;%s;%s;%d;true]"):format(
|
||||
avail_w, setting.name, table.concat(items, ","), selected_idx, value)
|
||||
|
||||
return fs, 1.1
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
local old_value = core.settings:get(setting.name) or setting.default
|
||||
local idx = tonumber(fields[setting.name]) or 0
|
||||
local value = setting.values[idx]
|
||||
if value == nil or value == old_value then
|
||||
return false
|
||||
end
|
||||
|
||||
core.settings:set(setting.name, value)
|
||||
return true
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function make_path(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = core.settings:get(setting.name) or setting.default
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local fs = ("field[0,0.3;%f,0.8;%s;%s;%s]"):format(
|
||||
avail_w - 3, setting.name, get_label(setting), core.formspec_escape(value))
|
||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 3, "pick_" .. setting.name, fgettext("Browse"))
|
||||
fs = fs .. ("button[%f,0.3;1.5,0.8;%s;%s]"):format(avail_w - 1.5, "set_" .. setting.name, fgettext("Set"))
|
||||
|
||||
return fs, 1.1
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
local dialog_name = "dlg_path_" .. setting.name
|
||||
if fields["pick_" .. setting.name] then
|
||||
local is_file = setting.type ~= "path"
|
||||
core.show_path_select_dialog(dialog_name,
|
||||
is_file and fgettext_ne("Select file") or fgettext_ne("Select directory"), is_file)
|
||||
return true
|
||||
end
|
||||
if fields[dialog_name .. "_accepted"] then
|
||||
local value = fields[dialog_name .. "_accepted"]
|
||||
if value ~= nil then
|
||||
core.settings:set(setting.name, value)
|
||||
end
|
||||
return true
|
||||
end
|
||||
if fields["set_" .. setting.name] or fields.key_enter_field == setting.name then
|
||||
local value = fields[setting.name]
|
||||
if value ~= nil then
|
||||
core.settings:set(setting.name, value)
|
||||
end
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
if PLATFORM == "Android" then
|
||||
-- The Irrlicht file picker doesn't work on Android.
|
||||
make.path = make.string
|
||||
make.filepath = make.string
|
||||
else
|
||||
make.path = make_path
|
||||
make.filepath = make_path
|
||||
end
|
||||
|
||||
|
||||
function make.v3f(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local value = vector.from_string(core.settings:get(setting.name) or setting.default)
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
-- Allocate space for "Set" button
|
||||
avail_w = avail_w - 1
|
||||
|
||||
local fs = "label[0,0.1;" .. get_label(setting) .. "]"
|
||||
|
||||
local field_width = (avail_w - 3*0.25) / 3
|
||||
|
||||
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
|
||||
0, field_width, setting.name .. "_x", "X", value.x)
|
||||
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
|
||||
field_width + 0.25, field_width, setting.name .. "_y", "Y", value.y)
|
||||
fs = fs .. ("field[%f,0.6;%f,0.8;%s;%s;%s]"):format(
|
||||
2 * (field_width + 0.25), field_width, setting.name .. "_z", "Z", value.z)
|
||||
|
||||
fs = fs .. ("button[%f,0.6;1,0.8;%s;%s]"):format(avail_w, "set_" .. setting.name, fgettext("Set"))
|
||||
|
||||
return fs, 1.4
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
if fields["set_" .. setting.name] or
|
||||
fields.key_enter_field == setting.name .. "_x" or
|
||||
fields.key_enter_field == setting.name .. "_y" or
|
||||
fields.key_enter_field == setting.name .. "_z" then
|
||||
local x = tonumber(fields[setting.name .. "_x"])
|
||||
local y = tonumber(fields[setting.name .. "_y"])
|
||||
local z = tonumber(fields[setting.name .. "_z"])
|
||||
if is_valid_number(x) and is_valid_number(y) and is_valid_number(z) then
|
||||
core.settings:set(setting.name, vector.new(x, y, z):to_string())
|
||||
else
|
||||
core.log("error", "Invalid vector: " .. dump({x, y, z}))
|
||||
end
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
function make.flags(setting)
|
||||
local checkboxes = {}
|
||||
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
local fs = {
|
||||
"label[0,0.1;" .. get_label(setting) .. "]",
|
||||
}
|
||||
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
checkboxes = {}
|
||||
for _, name in ipairs(setting.possible) do
|
||||
checkboxes[name] = false
|
||||
end
|
||||
local function apply_flags(flag_string, what)
|
||||
local prefixed_flags = {}
|
||||
for _, name in ipairs(flag_string:split(",")) do
|
||||
prefixed_flags[name:trim()] = true
|
||||
end
|
||||
for _, name in ipairs(setting.possible) do
|
||||
local enabled = prefixed_flags[name]
|
||||
local disabled = prefixed_flags["no" .. name]
|
||||
if enabled and disabled then
|
||||
core.log("warning", "Flag " .. name .. " in " .. what .. " " ..
|
||||
setting.name .. " both enabled and disabled, ignoring")
|
||||
elseif enabled then
|
||||
checkboxes[name] = true
|
||||
elseif disabled then
|
||||
checkboxes[name] = false
|
||||
end
|
||||
end
|
||||
end
|
||||
-- First apply the default, which is necessary since flags
|
||||
-- which are not overridden may be missing from the value.
|
||||
apply_flags(setting.default, "default for setting")
|
||||
local value = core.settings:get(setting.name)
|
||||
if value then
|
||||
apply_flags(value, "setting")
|
||||
end
|
||||
|
||||
local columns = math.max(math.floor(avail_w / 2.5), 1)
|
||||
local column_width = avail_w / columns
|
||||
local x = 0
|
||||
local y = 0.55
|
||||
|
||||
for _, possible in ipairs(setting.possible) do
|
||||
if x >= avail_w then
|
||||
x = 0
|
||||
y = y + 0.5
|
||||
end
|
||||
|
||||
local is_checked = checkboxes[possible]
|
||||
fs[#fs + 1] = ("checkbox[%f,%f;%s;%s;%s]"):format(
|
||||
x, y, setting.name .. "_" .. possible,
|
||||
core.formspec_escape(possible), tostring(is_checked))
|
||||
x = x + column_width
|
||||
end
|
||||
|
||||
return table.concat(fs, ""), y + 0.25
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields)
|
||||
local changed = false
|
||||
for name, _ in pairs(checkboxes) do
|
||||
local value = fields[setting.name .. "_" .. name]
|
||||
if value ~= nil then
|
||||
checkboxes[name] = core.is_yes(value)
|
||||
changed = true
|
||||
end
|
||||
end
|
||||
|
||||
if changed then
|
||||
local values = {}
|
||||
for _, name in ipairs(setting.possible) do
|
||||
if checkboxes[name] then
|
||||
table.insert(values, name)
|
||||
else
|
||||
table.insert(values, "no" .. name)
|
||||
end
|
||||
end
|
||||
|
||||
core.settings:set(setting.name, table.concat(values, ","))
|
||||
end
|
||||
return changed
|
||||
end
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function make_noise_params(setting)
|
||||
return {
|
||||
info_text = setting.comment,
|
||||
setting = setting,
|
||||
|
||||
get_formspec = function(self, avail_w)
|
||||
-- The "defaults" noise parameter flag doesn't reset a noise
|
||||
-- setting to its default value, so we offer a regular reset button.
|
||||
self.resettable = core.settings:has(setting.name)
|
||||
|
||||
local fs = "label[0,0.4;" .. get_label(setting) .. "]" ..
|
||||
("button[%f,0;2.5,0.8;%s;%s]"):format(avail_w - 2.5, "edit_" .. setting.name, fgettext("Edit"))
|
||||
return fs, 0.8
|
||||
end,
|
||||
|
||||
on_submit = function(self, fields, tabview)
|
||||
if fields["edit_" .. setting.name] then
|
||||
local dlg = create_change_mapgen_flags_dlg(setting)
|
||||
dlg:set_parent(tabview)
|
||||
tabview:hide()
|
||||
dlg:show()
|
||||
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
||||
end
|
||||
|
||||
make.noise_params_2d = make_noise_params
|
||||
make.noise_params_3d = make_noise_params
|
||||
|
||||
|
||||
return make
|
|
@ -1,252 +0,0 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2015 PilzAdam
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
local checkboxes = {}
|
||||
|
||||
local function flags_to_table(flags)
|
||||
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
|
||||
end
|
||||
|
||||
local function get_current_np_group(setting)
|
||||
local value = core.settings:get_np_group(setting.name)
|
||||
if value == nil then
|
||||
return setting.values
|
||||
end
|
||||
local p = "%g"
|
||||
return {
|
||||
p:format(value.offset),
|
||||
p:format(value.scale),
|
||||
p:format(value.spread.x),
|
||||
p:format(value.spread.y),
|
||||
p:format(value.spread.z),
|
||||
p:format(value.seed),
|
||||
p:format(value.octaves),
|
||||
p:format(value.persistence),
|
||||
p:format(value.lacunarity),
|
||||
value.flags
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
local function get_formspec(dialogdata)
|
||||
local setting = dialogdata.setting
|
||||
|
||||
-- Final formspec will be created at the end of this function
|
||||
-- Default values below, may be changed depending on setting type
|
||||
local width = 10
|
||||
local height = 2
|
||||
local description_height = 1.5
|
||||
|
||||
local t = get_current_np_group(setting)
|
||||
local dimension = 3
|
||||
if setting.type == "noise_params_2d" then
|
||||
dimension = 2
|
||||
end
|
||||
|
||||
local fields = {}
|
||||
local function add_field(x, name, label, value)
|
||||
fields[#fields + 1] = ("field[%f,%f;3.3,1;%s;%s;%s]"):format(
|
||||
x, height, name, label, core.formspec_escape(value or "")
|
||||
)
|
||||
end
|
||||
-- First row
|
||||
height = height + 0.3
|
||||
add_field(0.3, "te_offset", fgettext("Offset"), t[1])
|
||||
add_field(3.6, "te_scale", fgettext("Scale"), t[2])
|
||||
add_field(6.9, "te_seed", fgettext("Seed"), t[6])
|
||||
height = height + 1.1
|
||||
|
||||
-- Second row
|
||||
add_field(0.3, "te_spreadx", fgettext("X spread"), t[3])
|
||||
if dimension == 3 then
|
||||
add_field(3.6, "te_spready", fgettext("Y spread"), t[4])
|
||||
else
|
||||
fields[#fields + 1] = "label[4," .. height - 0.2 .. ";" ..
|
||||
fgettext("2D Noise") .. "]"
|
||||
end
|
||||
add_field(6.9, "te_spreadz", fgettext("Z spread"), t[5])
|
||||
height = height + 1.1
|
||||
|
||||
-- Third row
|
||||
add_field(0.3, "te_octaves", fgettext("Octaves"), t[7])
|
||||
add_field(3.6, "te_persist", fgettext("Persistence"), t[8])
|
||||
add_field(6.9, "te_lacun", fgettext("Lacunarity"), t[9])
|
||||
height = height + 1.1
|
||||
|
||||
|
||||
local enabled_flags = flags_to_table(t[10])
|
||||
local flags = {}
|
||||
for _, name in ipairs(enabled_flags) do
|
||||
-- Index by name, to avoid iterating over all enabled_flags for every possible flag.
|
||||
flags[name] = true
|
||||
end
|
||||
for _, name in ipairs(setting.flags) do
|
||||
local checkbox_name = "cb_" .. name
|
||||
local is_enabled = flags[name] == true -- to get false if nil
|
||||
checkboxes[checkbox_name] = is_enabled
|
||||
end
|
||||
|
||||
local formspec = table.concat(fields)
|
||||
.. "checkbox[0.5," .. height - 0.6 .. ";cb_defaults;"
|
||||
--[[~ "defaults" is a noise parameter flag.
|
||||
It describes the default processing options
|
||||
for noise settings in the settings menu. ]]
|
||||
.. fgettext("defaults") .. ";" -- defaults
|
||||
.. tostring(flags["defaults"] == true) .. "]" -- to get false if nil
|
||||
.. "checkbox[5," .. height - 0.6 .. ";cb_eased;"
|
||||
--[[~ "eased" is a noise parameter flag.
|
||||
It is used to make the map smoother and
|
||||
can be enabled in noise settings in
|
||||
the settings menu. ]]
|
||||
.. fgettext("eased") .. ";" -- eased
|
||||
.. tostring(flags["eased"] == true) .. "]"
|
||||
.. "checkbox[5," .. height - 0.15 .. ";cb_absvalue;"
|
||||
--[[~ "absvalue" is a noise parameter flag.
|
||||
It is short for "absolute value".
|
||||
It can be enabled in noise settings in
|
||||
the settings menu. ]]
|
||||
.. fgettext("absvalue") .. ";" -- absvalue
|
||||
.. tostring(flags["absvalue"] == true) .. "]"
|
||||
|
||||
height = height + 1
|
||||
|
||||
-- Box good, textarea bad. Calculate textarea size from box.
|
||||
local function create_textfield(size, label, text, bg_color)
|
||||
local textarea = {
|
||||
x = size.x + 0.3,
|
||||
y = size.y,
|
||||
w = size.w + 0.25,
|
||||
h = size.h * 1.16 + 0.12
|
||||
}
|
||||
return ("box[%f,%f;%f,%f;%s]textarea[%f,%f;%f,%f;;%s;%s]"):format(
|
||||
size.x, size.y, size.w, size.h, bg_color or "#000",
|
||||
textarea.x, textarea.y, textarea.w, textarea.h,
|
||||
core.formspec_escape(label), core.formspec_escape(text)
|
||||
)
|
||||
|
||||
end
|
||||
|
||||
-- When there's an error: Shrink description textarea and add error below
|
||||
if dialogdata.error_message then
|
||||
local error_box = {
|
||||
x = 0,
|
||||
y = description_height - 0.4,
|
||||
w = width - 0.25,
|
||||
h = 0.5
|
||||
}
|
||||
formspec = formspec ..
|
||||
create_textfield(error_box, "", dialogdata.error_message, "#600")
|
||||
description_height = description_height - 0.75
|
||||
end
|
||||
|
||||
-- Get description field
|
||||
local description_box = {
|
||||
x = 0,
|
||||
y = 0.2,
|
||||
w = width - 0.25,
|
||||
h = description_height
|
||||
}
|
||||
|
||||
local setting_name = setting.name
|
||||
if setting.readable_name then
|
||||
setting_name = fgettext_ne(setting.readable_name) ..
|
||||
" (" .. setting.name .. ")"
|
||||
end
|
||||
|
||||
local comment_text
|
||||
if setting.comment == "" then
|
||||
comment_text = fgettext_ne("(No description of setting given)")
|
||||
else
|
||||
comment_text = fgettext_ne(setting.comment)
|
||||
end
|
||||
|
||||
return (
|
||||
"size[" .. width .. "," .. height + 0.25 .. ",true]" ..
|
||||
create_textfield(description_box, setting_name, comment_text) ..
|
||||
formspec ..
|
||||
"button[" .. width / 2 - 2.5 .. "," .. height - 0.4 .. ";2.5,1;btn_done;" ..
|
||||
fgettext("Save") .. "]" ..
|
||||
"button[" .. width / 2 .. "," .. height - 0.4 .. ";2.5,1;btn_cancel;" ..
|
||||
fgettext("Cancel") .. "]"
|
||||
)
|
||||
end
|
||||
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
local setting = this.data.setting
|
||||
if fields["btn_done"] or fields["key_enter"] then
|
||||
local np_flags = {}
|
||||
for _, name in ipairs(setting.flags) do
|
||||
if checkboxes["cb_" .. name] then
|
||||
table.insert(np_flags, name)
|
||||
end
|
||||
end
|
||||
|
||||
checkboxes = {}
|
||||
|
||||
if setting.type == "noise_params_2d" then
|
||||
fields["te_spready"] = fields["te_spreadz"]
|
||||
end
|
||||
local new_value = {
|
||||
offset = fields["te_offset"],
|
||||
scale = fields["te_scale"],
|
||||
spread = {
|
||||
x = fields["te_spreadx"],
|
||||
y = fields["te_spready"],
|
||||
z = fields["te_spreadz"]
|
||||
},
|
||||
seed = fields["te_seed"],
|
||||
octaves = fields["te_octaves"],
|
||||
persistence = fields["te_persist"],
|
||||
lacunarity = fields["te_lacun"],
|
||||
flags = table.concat(np_flags, ", ")
|
||||
}
|
||||
core.settings:set_np_group(setting.name, new_value)
|
||||
|
||||
core.settings:write()
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
|
||||
if fields["btn_cancel"] then
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
|
||||
for name, value in pairs(fields) do
|
||||
if name:sub(1, 3) == "cb_" then
|
||||
checkboxes[name] = core.is_yes(value)
|
||||
return false -- Don't update the formspec!
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function create_change_mapgen_flags_dlg(setting)
|
||||
assert(type(setting) == "table")
|
||||
|
||||
local retval = dialog_create("dlg_change_mapgen_flags",
|
||||
get_formspec,
|
||||
buttonhandler,
|
||||
nil)
|
||||
|
||||
retval.data.setting = setting
|
||||
return retval
|
||||
end
|
|
@ -1,775 +0,0 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2022 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
local component_funcs = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
|
||||
"settings" .. DIR_DELIM .. "components.lua")
|
||||
|
||||
local shadows_component = dofile(core.get_mainmenu_path() .. DIR_DELIM ..
|
||||
"settings" .. DIR_DELIM .. "shadows_component.lua")
|
||||
|
||||
local loaded = false
|
||||
local full_settings
|
||||
local info_icon_path = core.formspec_escape(defaulttexturedir .. "settings_info.png")
|
||||
local reset_icon_path = core.formspec_escape(defaulttexturedir .. "settings_reset.png")
|
||||
local all_pages = {}
|
||||
local page_by_id = {}
|
||||
local filtered_pages = all_pages
|
||||
local filtered_page_by_id = page_by_id
|
||||
|
||||
|
||||
local function get_setting_info(name)
|
||||
for _, entry in ipairs(full_settings) do
|
||||
if entry.type ~= "category" and entry.name == name then
|
||||
return entry
|
||||
end
|
||||
end
|
||||
|
||||
return nil
|
||||
end
|
||||
|
||||
|
||||
local function add_page(page)
|
||||
assert(type(page.id) == "string")
|
||||
assert(type(page.title) == "string")
|
||||
assert(page.section == nil or type(page.section) == "string")
|
||||
assert(type(page.content) == "table")
|
||||
|
||||
assert(not page_by_id[page.id], "Page " .. page.id .. " already registered")
|
||||
|
||||
all_pages[#all_pages + 1] = page
|
||||
page_by_id[page.id] = page
|
||||
return page
|
||||
end
|
||||
|
||||
|
||||
local function load_settingtypes()
|
||||
local page = nil
|
||||
local section = nil
|
||||
local function ensure_page_started()
|
||||
if not page then
|
||||
page = add_page({
|
||||
id = (section or "general"):lower():gsub(" ", "_"),
|
||||
title = section or fgettext_ne("General"),
|
||||
section = section,
|
||||
content = {},
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
for _, entry in ipairs(full_settings) do
|
||||
if entry.type == "category" then
|
||||
if entry.level == 0 then
|
||||
section = entry.name
|
||||
page = nil
|
||||
elseif entry.level == 1 then
|
||||
page = {
|
||||
id = ((section and section .. "_" or "") .. entry.name):lower():gsub(" ", "_"),
|
||||
title = entry.readable_name or entry.name,
|
||||
section = section,
|
||||
content = {},
|
||||
}
|
||||
|
||||
page = add_page(page)
|
||||
elseif entry.level == 2 then
|
||||
ensure_page_started()
|
||||
page.content[#page.content + 1] = {
|
||||
heading = fgettext_ne(entry.readable_name or entry.name),
|
||||
}
|
||||
end
|
||||
else
|
||||
ensure_page_started()
|
||||
page.content[#page.content + 1] = entry.name
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
local function load()
|
||||
if loaded then
|
||||
return
|
||||
end
|
||||
loaded = true
|
||||
|
||||
full_settings = settingtypes.parse_config_file(false, true)
|
||||
|
||||
local change_keys = {
|
||||
query_text = "Controls",
|
||||
requires = {
|
||||
touch_controls = false,
|
||||
},
|
||||
get_formspec = function(self, avail_w)
|
||||
local btn_w = math.min(avail_w, 3)
|
||||
return ("button[0,0;%f,0.8;btn_change_keys;%s]"):format(btn_w, fgettext("Controls")), 0.8
|
||||
end,
|
||||
on_submit = function(self, fields)
|
||||
if fields.btn_change_keys then
|
||||
core.show_keys_menu()
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
local touchscreen_layout = {
|
||||
query_text = "Touchscreen layout",
|
||||
requires = {
|
||||
touchscreen = true,
|
||||
},
|
||||
get_formspec = function(self, avail_w)
|
||||
local btn_w = math.min(avail_w, 6)
|
||||
return ("button[0,0;%f,0.8;btn_touch_layout;%s]"):format(btn_w, fgettext("Touchscreen layout")), 0.8
|
||||
end,
|
||||
on_submit = function(self, fields)
|
||||
if fields.btn_touch_layout then
|
||||
core.show_touchscreen_layout()
|
||||
end
|
||||
end,
|
||||
}
|
||||
|
||||
add_page({
|
||||
id = "accessibility",
|
||||
title = fgettext_ne("Accessibility"),
|
||||
content = {
|
||||
"language",
|
||||
{ heading = fgettext_ne("General") },
|
||||
"font_size",
|
||||
"chat_font_size",
|
||||
"gui_scaling",
|
||||
"hud_scaling",
|
||||
"show_nametag_backgrounds",
|
||||
{ heading = fgettext_ne("Chat") },
|
||||
"console_height",
|
||||
"console_alpha",
|
||||
"console_color",
|
||||
{ heading = fgettext_ne("Controls") },
|
||||
"autojump",
|
||||
"safe_dig_and_place",
|
||||
{ heading = fgettext_ne("Movement") },
|
||||
"arm_inertia",
|
||||
"view_bobbing_amount",
|
||||
"fall_bobbing_amount",
|
||||
},
|
||||
})
|
||||
|
||||
load_settingtypes()
|
||||
|
||||
table.insert(page_by_id.controls_keyboard_and_mouse.content, 1, change_keys)
|
||||
-- insert after "touch_controls"
|
||||
table.insert(page_by_id.controls_touchscreen.content, 2, touchscreen_layout)
|
||||
do
|
||||
local content = page_by_id.graphics_and_audio_effects.content
|
||||
local idx = table.indexof(content, "enable_dynamic_shadows")
|
||||
table.insert(content, idx, shadows_component)
|
||||
|
||||
idx = table.indexof(content, "enable_auto_exposure") + 1
|
||||
local note = component_funcs.note(fgettext_ne("(The game will need to enable automatic exposure as well)"))
|
||||
note.requires = get_setting_info("enable_auto_exposure").requires
|
||||
table.insert(content, idx, note)
|
||||
|
||||
idx = table.indexof(content, "enable_bloom") + 1
|
||||
note = component_funcs.note(fgettext_ne("(The game will need to enable bloom as well)"))
|
||||
note.requires = get_setting_info("enable_bloom").requires
|
||||
table.insert(content, idx, note)
|
||||
|
||||
idx = table.indexof(content, "enable_volumetric_lighting") + 1
|
||||
note = component_funcs.note(fgettext_ne("(The game will need to enable volumetric lighting as well)"))
|
||||
note.requires = get_setting_info("enable_volumetric_lighting").requires
|
||||
table.insert(content, idx, note)
|
||||
end
|
||||
|
||||
-- These must not be translated, as they need to show in the local
|
||||
-- language no matter the user's current language.
|
||||
-- This list must be kept in sync with src/unsupported_language_list.txt.
|
||||
get_setting_info("language").option_labels = {
|
||||
[""] = fgettext_ne("(Use system language)"),
|
||||
--ar = " [ar]", blacklisted
|
||||
be = "Беларуская [be]",
|
||||
bg = "Български [bg]",
|
||||
ca = "Català [ca]",
|
||||
cs = "Česky [cs]",
|
||||
cy = "Cymraeg [cy]",
|
||||
da = "Dansk [da]",
|
||||
de = "Deutsch [de]",
|
||||
--dv = " [dv]", blacklisted
|
||||
el = "Ελληνικά [el]",
|
||||
en = "English [en]",
|
||||
eo = "Esperanto [eo]",
|
||||
es = "Español [es]",
|
||||
et = "Eesti [et]",
|
||||
eu = "Euskara [eu]",
|
||||
fi = "Suomi [fi]",
|
||||
fil = "Wikang Filipino [fil]",
|
||||
fr = "Français [fr]",
|
||||
gd = "Gàidhlig [gd]",
|
||||
gl = "Galego [gl]",
|
||||
--he = " [he]", blacklisted
|
||||
--hi = " [hi]", blacklisted
|
||||
hu = "Magyar [hu]",
|
||||
id = "Bahasa Indonesia [id]",
|
||||
it = "Italiano [it]",
|
||||
ja = "日本語 [ja]",
|
||||
jbo = "Lojban [jbo]",
|
||||
kk = "Қазақша [kk]",
|
||||
--kn = " [kn]", blacklisted
|
||||
ko = "한국어 [ko]",
|
||||
ky = "Kırgızca / Кыргызча [ky]",
|
||||
lt = "Lietuvių [lt]",
|
||||
lv = "Latviešu [lv]",
|
||||
mn = "Монгол [mn]",
|
||||
mr = "मराठी [mr]",
|
||||
ms = "Bahasa Melayu [ms]",
|
||||
--ms_Arab = " [ms_Arab]", blacklisted
|
||||
nb = "Norsk Bokmål [nb]",
|
||||
nl = "Nederlands [nl]",
|
||||
nn = "Norsk Nynorsk [nn]",
|
||||
oc = "Occitan [oc]",
|
||||
pl = "Polski [pl]",
|
||||
pt = "Português [pt]",
|
||||
pt_BR = "Português do Brasil [pt_BR]",
|
||||
ro = "Română [ro]",
|
||||
ru = "Русский [ru]",
|
||||
sk = "Slovenčina [sk]",
|
||||
sl = "Slovenščina [sl]",
|
||||
sr_Cyrl = "Српски [sr_Cyrl]",
|
||||
sr_Latn = "Srpski (Latinica) [sr_Latn]",
|
||||
sv = "Svenska [sv]",
|
||||
sw = "Kiswahili [sw]",
|
||||
--th = " [th]", blacklisted
|
||||
tr = "Türkçe [tr]",
|
||||
tt = "Tatarça [tt]",
|
||||
uk = "Українська [uk]",
|
||||
vi = "Tiếng Việt [vi]",
|
||||
zh_CN = "中文 (简体) [zh_CN]",
|
||||
zh_TW = "正體中文 (繁體) [zh_TW]",
|
||||
}
|
||||
|
||||
get_setting_info("touch_controls").option_labels = {
|
||||
["auto"] = fgettext_ne("Auto"),
|
||||
["true"] = fgettext_ne("Enabled"),
|
||||
["false"] = fgettext_ne("Disabled"),
|
||||
}
|
||||
end
|
||||
|
||||
|
||||
-- See if setting matches keywords
|
||||
local function get_setting_match_weight(entry, query_keywords)
|
||||
local setting_score = 0
|
||||
for _, keyword in ipairs(query_keywords) do
|
||||
if string.find(entry.name:lower(), keyword, 1, true) then
|
||||
setting_score = setting_score + 1
|
||||
end
|
||||
|
||||
if entry.readable_name and
|
||||
string.find(fgettext(entry.readable_name):lower(), keyword, 1, true) then
|
||||
setting_score = setting_score + 1
|
||||
end
|
||||
|
||||
if entry.comment and
|
||||
string.find(fgettext_ne(entry.comment):lower(), keyword, 1, true) then
|
||||
setting_score = setting_score + 1
|
||||
end
|
||||
end
|
||||
|
||||
return setting_score
|
||||
end
|
||||
|
||||
|
||||
local function filter_page_content(page, query_keywords)
|
||||
if #query_keywords == 0 then
|
||||
return page.content, 0
|
||||
end
|
||||
|
||||
local retval = {}
|
||||
local i = 1
|
||||
local max_weight = 0
|
||||
for _, content in ipairs(page.content) do
|
||||
if type(content) == "string" then
|
||||
local setting = get_setting_info(content)
|
||||
assert(setting, "Unknown setting: " .. content)
|
||||
|
||||
local weight = get_setting_match_weight(setting, query_keywords)
|
||||
if weight > 0 then
|
||||
max_weight = math.max(max_weight, weight)
|
||||
retval[i] = content
|
||||
i = i + 1
|
||||
end
|
||||
elseif type(content) == "table" and content.query_text then
|
||||
for _, keyword in ipairs(query_keywords) do
|
||||
if string.find(fgettext(content.query_text), keyword, 1, true) then
|
||||
max_weight = math.max(max_weight, 1)
|
||||
retval[i] = content
|
||||
i = i + 1
|
||||
break
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
return retval, max_weight
|
||||
end
|
||||
|
||||
|
||||
local function update_filtered_pages(query)
|
||||
filtered_pages = {}
|
||||
filtered_page_by_id = {}
|
||||
|
||||
local query_keywords = {}
|
||||
for word in query:lower():gmatch("%S+") do
|
||||
table.insert(query_keywords, word)
|
||||
end
|
||||
|
||||
local best_page = nil
|
||||
local best_page_weight = -1
|
||||
|
||||
for _, page in ipairs(all_pages) do
|
||||
local content, page_weight = filter_page_content(page, query_keywords)
|
||||
if page_has_contents(page, content) then
|
||||
local new_page = table.copy(page)
|
||||
new_page.content = content
|
||||
|
||||
filtered_pages[#filtered_pages + 1] = new_page
|
||||
filtered_page_by_id[new_page.id] = new_page
|
||||
|
||||
if page_weight > best_page_weight then
|
||||
best_page = new_page
|
||||
best_page_weight = page_weight
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return best_page and best_page.id or nil
|
||||
end
|
||||
|
||||
|
||||
local function check_requirements(name, requires)
|
||||
if requires == nil then
|
||||
return true
|
||||
end
|
||||
|
||||
local video_driver = core.get_active_driver()
|
||||
local touch_support = core.irrlicht_device_supports_touch()
|
||||
local touch_controls = core.settings:get("touch_controls")
|
||||
local special = {
|
||||
android = PLATFORM == "Android",
|
||||
desktop = PLATFORM ~= "Android",
|
||||
touch_support = touch_support,
|
||||
-- When touch_controls is "auto", we don't know which input method will
|
||||
-- be used, so we show settings for both.
|
||||
touchscreen = touch_support and (touch_controls == "auto" or core.is_yes(touch_controls)),
|
||||
keyboard_mouse = not touch_support or (touch_controls == "auto" or not core.is_yes(touch_controls)),
|
||||
opengl = (video_driver == "opengl" or video_driver == "opengl3"),
|
||||
gles = video_driver:sub(1, 5) == "ogles",
|
||||
}
|
||||
|
||||
for req_key, req_value in pairs(requires) do
|
||||
if special[req_key] == nil then
|
||||
local required_setting = get_setting_info(req_key)
|
||||
if required_setting == nil then
|
||||
core.log("warning", "Unknown setting " .. req_key .. " required by " .. (name or "???"))
|
||||
end
|
||||
local actual_value = core.settings:get_bool(req_key,
|
||||
required_setting and core.is_yes(required_setting.default))
|
||||
if actual_value ~= req_value then
|
||||
return false
|
||||
end
|
||||
elseif special[req_key] ~= req_value then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
|
||||
function page_has_contents(page, actual_content)
|
||||
local is_advanced =
|
||||
page.id:sub(1, #"client_and_server") == "client_and_server" or
|
||||
page.id:sub(1, #"mapgen") == "mapgen" or
|
||||
page.id:sub(1, #"advanced") == "advanced"
|
||||
local show_advanced = core.settings:get_bool("show_advanced")
|
||||
if is_advanced and not show_advanced then
|
||||
return false
|
||||
end
|
||||
|
||||
for _, item in ipairs(actual_content) do
|
||||
if item == false or item.heading then --luacheck: ignore
|
||||
-- skip
|
||||
elseif type(item) == "string" then
|
||||
local setting = get_setting_info(item)
|
||||
assert(setting, "Unknown setting: " .. item)
|
||||
if check_requirements(setting.name, setting.requires) then
|
||||
return true
|
||||
end
|
||||
elseif item.get_formspec then
|
||||
if check_requirements(item.id, item.requires) then
|
||||
return true
|
||||
end
|
||||
else
|
||||
error("Unknown content in page: " .. dump(item))
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local function build_page_components(page)
|
||||
-- Filter settings based on requirements
|
||||
local content = {}
|
||||
local last_heading
|
||||
for _, item in ipairs(page.content) do
|
||||
if item == false then --luacheck: ignore
|
||||
-- skip
|
||||
elseif item.heading then
|
||||
last_heading = item
|
||||
else
|
||||
local name, requires
|
||||
if type(item) == "string" then
|
||||
local setting = get_setting_info(item)
|
||||
assert(setting, "Unknown setting: " .. item)
|
||||
name = setting.name
|
||||
requires = setting.requires
|
||||
elseif item.get_formspec then
|
||||
name = item.id
|
||||
requires = item.requires
|
||||
else
|
||||
error("Unknown content in page: " .. dump(item))
|
||||
end
|
||||
|
||||
if check_requirements(name, requires) then
|
||||
if last_heading then
|
||||
content[#content + 1] = last_heading
|
||||
last_heading = nil
|
||||
end
|
||||
content[#content + 1] = item
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Create components
|
||||
local retval = {}
|
||||
for i, item in ipairs(content) do
|
||||
if type(item) == "string" then
|
||||
local setting = get_setting_info(item)
|
||||
local component_func = component_funcs[setting.type]
|
||||
assert(component_func, "Unknown setting type: " .. setting.type)
|
||||
retval[i] = component_func(setting)
|
||||
elseif item.get_formspec then
|
||||
retval[i] = item
|
||||
elseif item.heading then
|
||||
retval[i] = component_funcs.heading(item.heading)
|
||||
end
|
||||
end
|
||||
return retval
|
||||
end
|
||||
|
||||
|
||||
local formspec_show_hack = false
|
||||
|
||||
|
||||
local function get_formspec(dialogdata)
|
||||
local page_id = dialogdata.page_id or "accessibility"
|
||||
local page = filtered_page_by_id[page_id]
|
||||
|
||||
local extra_h = 1 -- not included in tabsize.height
|
||||
local tabsize = {
|
||||
width = core.settings:get_bool("touch_gui") and 16.5 or 15.5,
|
||||
height = core.settings:get_bool("touch_gui") and (10 - extra_h) or 12,
|
||||
}
|
||||
|
||||
local scrollbar_w = core.settings:get_bool("touch_gui") and 0.6 or 0.4
|
||||
|
||||
local left_pane_width = core.settings:get_bool("touch_gui") and 4.5 or 4.25
|
||||
local left_pane_padding = 0.25
|
||||
local search_width = left_pane_width + scrollbar_w - (0.75 * 2)
|
||||
|
||||
local back_w = 3
|
||||
local checkbox_w = (tabsize.width - back_w - 2*0.2) / 2
|
||||
local show_technical_names = core.settings:get_bool("show_technical_names")
|
||||
local show_advanced = core.settings:get_bool("show_advanced")
|
||||
|
||||
formspec_show_hack = not formspec_show_hack
|
||||
|
||||
local fs = {
|
||||
"formspec_version[6]",
|
||||
"size[", tostring(tabsize.width), ",", tostring(tabsize.height + extra_h), "]",
|
||||
core.settings:get_bool("touch_gui") and "padding[0.01,0.01]" or "",
|
||||
"bgcolor[#0000]",
|
||||
|
||||
-- HACK: this is needed to allow resubmitting the same formspec
|
||||
formspec_show_hack and " " or "",
|
||||
|
||||
"box[0,0;", tostring(tabsize.width), ",", tostring(tabsize.height), ";#0000008C]",
|
||||
|
||||
("button[0,%f;%f,0.8;back;%s]"):format(
|
||||
tabsize.height + 0.2, back_w, fgettext("Back")),
|
||||
|
||||
("box[%f,%f;%f,0.8;#0000008C]"):format(
|
||||
back_w + 0.2, tabsize.height + 0.2, checkbox_w),
|
||||
("checkbox[%f,%f;show_technical_names;%s;%s]"):format(
|
||||
back_w + 2*0.2, tabsize.height + 0.6,
|
||||
fgettext("Show technical names"), tostring(show_technical_names)),
|
||||
|
||||
("box[%f,%f;%f,0.8;#0000008C]"):format(
|
||||
back_w + 2*0.2 + checkbox_w, tabsize.height + 0.2, checkbox_w),
|
||||
("checkbox[%f,%f;show_advanced;%s;%s]"):format(
|
||||
back_w + 3*0.2 + checkbox_w, tabsize.height + 0.6,
|
||||
fgettext("Show advanced settings"), tostring(show_advanced)),
|
||||
|
||||
"field[0.25,0.25;", tostring(search_width), ",0.75;search_query;;",
|
||||
core.formspec_escape(dialogdata.query or ""), "]",
|
||||
"field_enter_after_edit[search_query;true]",
|
||||
"container[", tostring(search_width + 0.25), ", 0.25]",
|
||||
"image_button[0,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "search.png"), ";search;]",
|
||||
"image_button[0.75,0;0.75,0.75;", core.formspec_escape(defaulttexturedir .. "clear.png"), ";search_clear;]",
|
||||
"tooltip[search;", fgettext("Search"), "]",
|
||||
"tooltip[search_clear;", fgettext("Clear"), "]",
|
||||
"container_end[]",
|
||||
("scroll_container[0.25,1.25;%f,%f;leftscroll;vertical;0.1;0]"):format(
|
||||
left_pane_width, tabsize.height - 1.5),
|
||||
"style_type[button;border=false;bgcolor=#3333]",
|
||||
"style_type[button:hover;border=false;bgcolor=#6663]",
|
||||
}
|
||||
|
||||
local y = 0
|
||||
local last_section = nil
|
||||
for _, other_page in ipairs(filtered_pages) do
|
||||
if other_page.section ~= last_section then
|
||||
fs[#fs + 1] = ("label[0.1,%f;%s]"):format(
|
||||
y + 0.41, core.colorize("#ff0", fgettext(other_page.section)))
|
||||
last_section = other_page.section
|
||||
y = y + 0.82
|
||||
end
|
||||
fs[#fs + 1] = ("box[0,%f;%f,0.8;%s]"):format(
|
||||
y, left_pane_width-left_pane_padding, other_page.id == page_id and "#467832FF" or "#3339")
|
||||
fs[#fs + 1] = ("button[0,%f;%f,0.8;page_%s;%s]")
|
||||
:format(y, left_pane_width-left_pane_padding, other_page.id, fgettext(other_page.title))
|
||||
y = y + 0.82
|
||||
end
|
||||
|
||||
if #filtered_pages == 0 then
|
||||
fs[#fs + 1] = "label[0.1,0.41;"
|
||||
fs[#fs + 1] = fgettext("No results")
|
||||
fs[#fs + 1] = "]"
|
||||
end
|
||||
|
||||
fs[#fs + 1] = "scroll_container_end[]"
|
||||
|
||||
if y >= tabsize.height - 1.25 then
|
||||
fs[#fs + 1] = ("scrollbar[%f,1.25;%f,%f;vertical;leftscroll;%f]"):format(
|
||||
left_pane_width + 0.25, scrollbar_w, tabsize.height - 1.5, dialogdata.leftscroll or 0)
|
||||
end
|
||||
|
||||
fs[#fs + 1] = "style_type[button;border=;bgcolor=]"
|
||||
|
||||
if not dialogdata.components then
|
||||
dialogdata.components = page and build_page_components(page) or {}
|
||||
end
|
||||
|
||||
local right_pane_width = tabsize.width - left_pane_width - 0.375 - 2*scrollbar_w - 0.25
|
||||
fs[#fs + 1] = ("scroll_container[%f,0;%f,%f;rightscroll;vertical;0.1;0.25]"):format(
|
||||
tabsize.width - right_pane_width - scrollbar_w, right_pane_width, tabsize.height)
|
||||
|
||||
y = 0.25
|
||||
for i, comp in ipairs(dialogdata.components) do
|
||||
fs[#fs + 1] = ("container[0,%f]"):format(y)
|
||||
|
||||
local avail_w = right_pane_width - 0.25
|
||||
if not comp.full_width then
|
||||
avail_w = avail_w - 1.4
|
||||
end
|
||||
if comp.max_w then
|
||||
avail_w = math.min(avail_w, comp.max_w)
|
||||
end
|
||||
|
||||
local comp_fs, used_h = comp:get_formspec(avail_w)
|
||||
fs[#fs + 1] = comp_fs
|
||||
|
||||
fs[#fs + 1] = "style_type[image_button;border=false;padding=]"
|
||||
|
||||
local show_reset = comp.resettable and comp.setting
|
||||
local show_info = comp.info_text and comp.info_text ~= ""
|
||||
if show_reset or show_info then
|
||||
-- ensure there's enough space for reset/info
|
||||
used_h = math.max(used_h, 0.5)
|
||||
end
|
||||
local info_reset_y = used_h / 2 - 0.25
|
||||
|
||||
if show_reset then
|
||||
local default = comp.setting.default
|
||||
local reset_tooltip = default and
|
||||
fgettext("Reset setting to default ($1)", tostring(default)) or
|
||||
fgettext("Reset setting to default")
|
||||
fs[#fs + 1] = ("image_button[%f,%f;0.5,0.5;%s;%s;]"):format(
|
||||
right_pane_width - 1.4, info_reset_y, reset_icon_path, "reset_" .. i)
|
||||
fs[#fs + 1] = ("tooltip[%s;%s]"):format("reset_" .. i, reset_tooltip)
|
||||
end
|
||||
|
||||
if show_info then
|
||||
local info_x = right_pane_width - 0.75
|
||||
fs[#fs + 1] = ("image[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, info_icon_path)
|
||||
fs[#fs + 1] = ("tooltip[%f,%f;0.5,0.5;%s]"):format(info_x, info_reset_y, fgettext(comp.info_text))
|
||||
end
|
||||
|
||||
fs[#fs + 1] = "style_type[image_button;border=;padding=]"
|
||||
|
||||
fs[#fs + 1] = "container_end[]"
|
||||
|
||||
if used_h > 0 then
|
||||
y = y + used_h + 0.25
|
||||
end
|
||||
end
|
||||
|
||||
fs[#fs + 1] = "scroll_container_end[]"
|
||||
|
||||
if y >= tabsize.height then
|
||||
fs[#fs + 1] = ("scrollbar[%f,0;%f,%f;vertical;rightscroll;%f]"):format(
|
||||
tabsize.width - scrollbar_w, scrollbar_w, tabsize.height, dialogdata.rightscroll or 0)
|
||||
end
|
||||
|
||||
return table.concat(fs, "")
|
||||
end
|
||||
|
||||
|
||||
-- On Android, closing the app via the "Recents screen" won't result in a clean
|
||||
-- exit, discarding any setting changes made by the user.
|
||||
-- To avoid that, we write the settings file in more cases on Android.
|
||||
function write_settings_early()
|
||||
if PLATFORM == "Android" then
|
||||
core.settings:write()
|
||||
end
|
||||
end
|
||||
|
||||
local function regenerate_page_list(dialogdata)
|
||||
local suggested_page_id = update_filtered_pages(dialogdata.query)
|
||||
|
||||
dialogdata.components = nil
|
||||
|
||||
if not filtered_page_by_id[dialogdata.page_id] then
|
||||
dialogdata.leftscroll = 0
|
||||
dialogdata.rightscroll = 0
|
||||
|
||||
dialogdata.page_id = suggested_page_id
|
||||
end
|
||||
end
|
||||
|
||||
local function buttonhandler(this, fields)
|
||||
local dialogdata = this.data
|
||||
dialogdata.leftscroll = core.explode_scrollbar_event(fields.leftscroll).value or dialogdata.leftscroll
|
||||
dialogdata.rightscroll = core.explode_scrollbar_event(fields.rightscroll).value or dialogdata.rightscroll
|
||||
dialogdata.query = fields.search_query
|
||||
|
||||
if fields.back then
|
||||
this:delete()
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.show_technical_names ~= nil then
|
||||
local value = core.is_yes(fields.show_technical_names)
|
||||
core.settings:set_bool("show_technical_names", value)
|
||||
write_settings_early()
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.show_advanced ~= nil then
|
||||
local value = core.is_yes(fields.show_advanced)
|
||||
core.settings:set_bool("show_advanced", value)
|
||||
write_settings_early()
|
||||
regenerate_page_list(dialogdata)
|
||||
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.search or fields.key_enter_field == "search_query" then
|
||||
dialogdata.components = nil
|
||||
dialogdata.leftscroll = 0
|
||||
dialogdata.rightscroll = 0
|
||||
|
||||
dialogdata.page_id = update_filtered_pages(dialogdata.query)
|
||||
|
||||
return true
|
||||
end
|
||||
if fields.search_clear then
|
||||
dialogdata.query = ""
|
||||
dialogdata.components = nil
|
||||
dialogdata.leftscroll = 0
|
||||
dialogdata.rightscroll = 0
|
||||
|
||||
dialogdata.page_id = update_filtered_pages("")
|
||||
return true
|
||||
end
|
||||
|
||||
for _, page in ipairs(all_pages) do
|
||||
if fields["page_" .. page.id] then
|
||||
dialogdata.page_id = page.id
|
||||
dialogdata.components = nil
|
||||
dialogdata.rightscroll = 0
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
local function after_setting_change(comp)
|
||||
write_settings_early()
|
||||
if comp.setting and comp.setting.name == "touch_controls" then
|
||||
-- Changing the "touch_controls" setting may result in a different
|
||||
-- page list.
|
||||
regenerate_page_list(dialogdata)
|
||||
else
|
||||
-- Clear components so they regenerate
|
||||
dialogdata.components = nil
|
||||
end
|
||||
end
|
||||
|
||||
for i, comp in ipairs(dialogdata.components) do
|
||||
if comp.on_submit and comp:on_submit(fields, this) then
|
||||
after_setting_change(comp)
|
||||
return true
|
||||
end
|
||||
if comp.setting and fields["reset_" .. i] then
|
||||
core.settings:remove(comp.setting.name)
|
||||
after_setting_change(comp)
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
local function eventhandler(event)
|
||||
if event == "DialogShow" then
|
||||
-- Don't show the header image behind the dialog.
|
||||
mm_game_theme.set_engine(true)
|
||||
return true
|
||||
end
|
||||
if event == "FullscreenChange" then
|
||||
-- Refresh the formspec to keep the fullscreen checkbox up to date.
|
||||
ui.update()
|
||||
return true
|
||||
end
|
||||
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
function create_settings_dlg()
|
||||
load()
|
||||
local dlg = dialog_create("dlg_settings", get_formspec, buttonhandler, eventhandler)
|
||||
|
||||
dlg.data.page_id = update_filtered_pages("")
|
||||
|
||||
return dlg
|
||||
end
|
|
@ -1,139 +0,0 @@
|
|||
local concat = table.concat
|
||||
local insert = table.insert
|
||||
local sprintf = string.format
|
||||
local rep = string.rep
|
||||
|
||||
local minetest_example_header = [[
|
||||
# This file contains a list of all available settings and their default value for minetest.conf
|
||||
|
||||
# By default, all the settings are commented and not functional.
|
||||
# Uncomment settings by removing the preceding #.
|
||||
|
||||
# minetest.conf is read by default from:
|
||||
# ../minetest.conf
|
||||
# ../../minetest.conf
|
||||
# Any other path can be chosen by passing the path as a parameter
|
||||
# to the program, eg. "luanti.exe --config ../minetest.conf.example".
|
||||
|
||||
# Further documentation:
|
||||
# https://wiki.luanti.org/
|
||||
|
||||
]]
|
||||
|
||||
local group_format_template = [[
|
||||
# %s = {
|
||||
# offset = %s,
|
||||
# scale = %s,
|
||||
# spread = (%s, %s, %s),
|
||||
# seed = %s,
|
||||
# octaves = %s,
|
||||
# persistence = %s,
|
||||
# lacunarity = %s,
|
||||
# flags =%s
|
||||
# }
|
||||
|
||||
]]
|
||||
|
||||
local function create_minetest_conf_example(settings)
|
||||
local result = { minetest_example_header }
|
||||
for _, entry in ipairs(settings) do
|
||||
if entry.type == "category" then
|
||||
if entry.level == 0 then
|
||||
insert(result, "#\n# " .. entry.name .. "\n#\n\n")
|
||||
else
|
||||
insert(result, rep("#", entry.level))
|
||||
insert(result, "# " .. entry.name .. "\n\n")
|
||||
end
|
||||
else
|
||||
local group_format = false
|
||||
if entry.noise_params and entry.values then
|
||||
if entry.type == "noise_params_2d" or entry.type == "noise_params_3d" then
|
||||
group_format = true
|
||||
end
|
||||
end
|
||||
if entry.comment ~= "" then
|
||||
for _, comment_line in ipairs(entry.comment:split("\n", true)) do
|
||||
if comment_line == "" then
|
||||
insert(result, "#\n")
|
||||
else
|
||||
insert(result, "# " .. comment_line .. "\n")
|
||||
end
|
||||
end
|
||||
end
|
||||
if entry.type == "key" then
|
||||
local line = "See https://github.com/minetest/irrlicht/blob/master/include/Keycodes.h"
|
||||
insert(result, "# " .. line .. "\n")
|
||||
end
|
||||
insert(result, "# type: " .. entry.type)
|
||||
if entry.min then
|
||||
insert(result, " min: " .. entry.min)
|
||||
end
|
||||
if entry.max then
|
||||
insert(result, " max: " .. entry.max)
|
||||
end
|
||||
if entry.values and entry.noise_params == nil then
|
||||
insert(result, " values: " .. concat(entry.values, ", "))
|
||||
end
|
||||
if entry.possible then
|
||||
insert(result, " possible values: " .. concat(entry.possible, ", "))
|
||||
end
|
||||
insert(result, "\n")
|
||||
if group_format == true then
|
||||
local flags = entry.values[10]
|
||||
if flags ~= "" then
|
||||
flags = " "..flags
|
||||
end
|
||||
insert(result, sprintf(group_format_template, entry.name, entry.values[1],
|
||||
entry.values[2], entry.values[3], entry.values[4], entry.values[5],
|
||||
entry.values[6], entry.values[7], entry.values[8], entry.values[9],
|
||||
flags))
|
||||
else
|
||||
local append
|
||||
if entry.default ~= "" then
|
||||
append = " " .. entry.default
|
||||
end
|
||||
insert(result, sprintf("# %s =%s\n\n", entry.name, append or ""))
|
||||
end
|
||||
end
|
||||
end
|
||||
return concat(result)
|
||||
end
|
||||
|
||||
local translation_file_header = [[
|
||||
// This file is automatically generated
|
||||
// It contains a bunch of fake gettext calls, to tell xgettext about the strings in config files
|
||||
// To update it, refer to the bottom of builtin/mainmenu/dlg_settings_advanced.lua
|
||||
|
||||
fake_function() {]]
|
||||
|
||||
local function create_translation_file(settings)
|
||||
local result = { translation_file_header }
|
||||
for _, entry in ipairs(settings) do
|
||||
if entry.type == "category" then
|
||||
insert(result, sprintf("\tgettext(%q);", entry.name))
|
||||
else
|
||||
if entry.readable_name then
|
||||
insert(result, sprintf("\tgettext(%q);", entry.readable_name))
|
||||
end
|
||||
if entry.comment ~= "" then
|
||||
local comment_escaped = entry.comment:gsub("\n", "\\n")
|
||||
comment_escaped = comment_escaped:gsub("\"", "\\\"")
|
||||
insert(result, "\tgettext(\"" .. comment_escaped .. "\");")
|
||||
end
|
||||
end
|
||||
end
|
||||
insert(result, "}\n")
|
||||
return concat(result, "\n")
|
||||
end
|
||||
|
||||
local file = assert(io.open("minetest.conf.example", "w"))
|
||||
file:write(create_minetest_conf_example(settingtypes.parse_config_file(true, false)))
|
||||
file:close()
|
||||
|
||||
file = assert(io.open("src/settings_translation_file.cpp", "w"))
|
||||
-- If 'minetest.conf.example' appears in the 'bin' folder, the line below may have to be
|
||||
-- used instead. The file will also appear in the 'bin' folder.
|
||||
--file = assert(io.open("settings_translation_file.cpp", "w"))
|
||||
-- We don't want hidden settings to be translated, so we set read_all to false.
|
||||
file:write(create_translation_file(settingtypes.parse_config_file(false, false)))
|
||||
file:close()
|
|
@ -1,28 +0,0 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2022 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
local path = core.get_mainmenu_path() .. DIR_DELIM .. "settings"
|
||||
|
||||
dofile(path .. DIR_DELIM .. "settingtypes.lua")
|
||||
dofile(path .. DIR_DELIM .. "dlg_change_mapgen_flags.lua")
|
||||
dofile(path .. DIR_DELIM .. "dlg_settings.lua")
|
||||
|
||||
-- Uncomment to generate 'minetest.conf.example' and 'settings_translation_file.cpp'.
|
||||
-- For RUN_IN_PLACE the generated files may appear in the 'bin' folder.
|
||||
-- See comment and alternative line at the end of 'generate_from_settingtypes.lua'.
|
||||
|
||||
-- dofile(path .. DIR_DELIM .. "generate_from_settingtypes.lua")
|
|
@ -1,507 +0,0 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2015 PilzAdam
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
settingtypes = {}
|
||||
|
||||
-- A Setting type is a table with the following keys:
|
||||
--
|
||||
-- name: Identifier
|
||||
-- readable_name: Readable title
|
||||
-- type: Category
|
||||
--
|
||||
-- name = mod.name,
|
||||
-- readable_name = mod.title,
|
||||
-- level = 1,
|
||||
-- type = "category", "int", "string", ""
|
||||
-- }
|
||||
|
||||
|
||||
local FILENAME = "settingtypes.txt"
|
||||
|
||||
local CHAR_CLASSES = {
|
||||
SPACE = "[%s]",
|
||||
VARIABLE = "[%w_%-%.]",
|
||||
INTEGER = "[+-]?[%d]",
|
||||
FLOAT = "[+-]?[%d%.]",
|
||||
FLAGS = "[%w_%-%.,]",
|
||||
}
|
||||
|
||||
local function flags_to_table(flags)
|
||||
return flags:gsub("%s+", ""):split(",", true) -- Remove all spaces and split
|
||||
end
|
||||
|
||||
-- returns error message, or nil
|
||||
local function parse_setting_line(settings, line, read_all, base_level, allow_secure)
|
||||
|
||||
-- strip carriage returns (CR, /r)
|
||||
line = line:gsub("\r", "")
|
||||
|
||||
-- comment
|
||||
local comment_match = line:match("^#" .. CHAR_CLASSES.SPACE .. "*(.*)$")
|
||||
if comment_match then
|
||||
settings.current_comment[#settings.current_comment + 1] = comment_match
|
||||
return
|
||||
end
|
||||
|
||||
-- clear current_comment so only comments directly above a setting are bound to it
|
||||
-- but keep a local reference to it for variables in the current line
|
||||
local current_comment = settings.current_comment
|
||||
settings.current_comment = {}
|
||||
|
||||
-- empty lines
|
||||
if line:match("^" .. CHAR_CLASSES.SPACE .. "*$") then
|
||||
return
|
||||
end
|
||||
|
||||
-- category
|
||||
local stars, category = line:match("^%[([%*]*)([^%]]+)%]$")
|
||||
if category then
|
||||
local category_level = stars:len() + base_level
|
||||
|
||||
if settings.current_hide_level then
|
||||
if settings.current_hide_level < category_level then
|
||||
-- Skip this category, it's inside a hidden category.
|
||||
return
|
||||
else
|
||||
-- The start of this category marks the end of a hidden category.
|
||||
settings.current_hide_level = nil
|
||||
end
|
||||
end
|
||||
|
||||
if not read_all and category:sub(1, 5) == "Hide:" then
|
||||
-- This category is hidden.
|
||||
settings.current_hide_level = category_level
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = category,
|
||||
level = category_level,
|
||||
type = "category",
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if settings.current_hide_level then
|
||||
-- Ignore this line, we're inside a hidden category.
|
||||
return
|
||||
end
|
||||
|
||||
-- settings
|
||||
local first_part, name, readable_name, setting_type = line:match("^"
|
||||
-- this first capture group matches the whole first part,
|
||||
-- so we can later strip it from the rest of the line
|
||||
.. "("
|
||||
.. "([" .. CHAR_CLASSES.VARIABLE .. "+)" -- variable name
|
||||
.. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "%(([^%)]*)%)" -- readable name
|
||||
.. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.VARIABLE .. "+)" -- type
|
||||
.. CHAR_CLASSES.SPACE .. "*"
|
||||
.. ")")
|
||||
|
||||
if not first_part then
|
||||
return "Invalid line"
|
||||
end
|
||||
|
||||
if name:match("secure%.[.]*") and not allow_secure then
|
||||
return "Tried to add \"secure.\" setting"
|
||||
end
|
||||
|
||||
local requires = {}
|
||||
local last_line = #current_comment > 0 and current_comment[#current_comment]:trim()
|
||||
if last_line and last_line:lower():sub(1, 9) == "requires:" then
|
||||
local parts = last_line:sub(10):split(",")
|
||||
current_comment[#current_comment] = nil
|
||||
|
||||
for _, part in ipairs(parts) do
|
||||
part = part:trim()
|
||||
|
||||
local value = true
|
||||
if part:sub(1, 1) == "!" then
|
||||
value = false
|
||||
part = part:sub(2):trim()
|
||||
end
|
||||
|
||||
requires[part] = value
|
||||
end
|
||||
end
|
||||
|
||||
if readable_name == "" then
|
||||
readable_name = nil
|
||||
end
|
||||
local remaining_line = line:sub(first_part:len() + 1)
|
||||
|
||||
local comment = table.concat(current_comment, "\n"):trim()
|
||||
|
||||
if setting_type == "int" then
|
||||
local default, min, max = remaining_line:match("^"
|
||||
-- first int is required, the last 2 are optional
|
||||
.. "(" .. CHAR_CLASSES.INTEGER .. "+)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.INTEGER .. "*)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.INTEGER .. "*)"
|
||||
.. "$")
|
||||
|
||||
if not default or not tonumber(default) then
|
||||
return "Invalid integer setting"
|
||||
end
|
||||
|
||||
min = tonumber(min)
|
||||
max = tonumber(max)
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "int",
|
||||
default = default,
|
||||
min = min,
|
||||
max = max,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "string"
|
||||
or setting_type == "key" or setting_type == "v3f" then
|
||||
local default = remaining_line:match("^(.*)$")
|
||||
|
||||
if not default then
|
||||
return "Invalid string setting"
|
||||
end
|
||||
if setting_type == "key" and not read_all then
|
||||
-- ignore key type if read_all is false
|
||||
return
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = setting_type,
|
||||
default = default,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "noise_params_2d"
|
||||
or setting_type == "noise_params_3d" then
|
||||
local default = remaining_line:match("^(.*)$")
|
||||
|
||||
if not default then
|
||||
return "Invalid string setting"
|
||||
end
|
||||
|
||||
local values = {}
|
||||
local ti = 1
|
||||
local index = 1
|
||||
for match in default:gmatch("[+-]?[%d.-e]+") do -- All numeric characters
|
||||
index = default:find("[+-]?[%d.-e]+", index) + match:len()
|
||||
table.insert(values, match)
|
||||
ti = ti + 1
|
||||
if ti > 9 then
|
||||
break
|
||||
end
|
||||
end
|
||||
index = default:find("[^, ]", index)
|
||||
local flags = ""
|
||||
if index then
|
||||
flags = default:sub(index)
|
||||
end
|
||||
table.insert(values, flags)
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = setting_type,
|
||||
default = default,
|
||||
default_table = {
|
||||
offset = values[1],
|
||||
scale = values[2],
|
||||
spread = {
|
||||
x = values[3],
|
||||
y = values[4],
|
||||
z = values[5]
|
||||
},
|
||||
seed = values[6],
|
||||
octaves = values[7],
|
||||
persistence = values[8],
|
||||
lacunarity = values[9],
|
||||
flags = values[10]
|
||||
},
|
||||
values = values,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
noise_params = true,
|
||||
flags = flags_to_table("defaults,eased,absvalue")
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "bool" then
|
||||
if remaining_line ~= "false" and remaining_line ~= "true" then
|
||||
return "Invalid boolean setting"
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "bool",
|
||||
default = remaining_line,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "float" then
|
||||
local default, min, max = remaining_line:match("^"
|
||||
-- first float is required, the last 2 are optional
|
||||
.. "(" .. CHAR_CLASSES.FLOAT .. "+)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.FLOAT .. "*)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.FLOAT .. "*)"
|
||||
.."$")
|
||||
|
||||
if not default or not tonumber(default) then
|
||||
return "Invalid float setting"
|
||||
end
|
||||
|
||||
min = tonumber(min)
|
||||
max = tonumber(max)
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "float",
|
||||
default = default,
|
||||
min = min,
|
||||
max = max,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "enum" then
|
||||
local default, values = remaining_line:match("^"
|
||||
-- first value (default) may be empty (i.e. is optional)
|
||||
.. "(" .. CHAR_CLASSES.VARIABLE .. "*)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.FLAGS .. "+)"
|
||||
.. "$")
|
||||
|
||||
if not default or values == "" then
|
||||
return "Invalid enum setting"
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "enum",
|
||||
default = default,
|
||||
values = values:split(",", true),
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "path" or setting_type == "filepath" then
|
||||
local default = remaining_line:match("^(.*)$")
|
||||
|
||||
if not default then
|
||||
return "Invalid path setting"
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = setting_type,
|
||||
default = default,
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
if setting_type == "flags" then
|
||||
local default, possible = remaining_line:match("^"
|
||||
-- first value (default) may be empty (i.e. is optional)
|
||||
-- this is implemented by making the last value optional, and
|
||||
-- swapping them around if it turns out empty.
|
||||
.. "(" .. CHAR_CLASSES.FLAGS .. "+)" .. CHAR_CLASSES.SPACE .. "*"
|
||||
.. "(" .. CHAR_CLASSES.FLAGS .. "*)"
|
||||
.. "$")
|
||||
|
||||
if not default or not possible then
|
||||
return "Invalid flags setting"
|
||||
end
|
||||
|
||||
if possible == "" then
|
||||
possible = default
|
||||
default = ""
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = name,
|
||||
readable_name = readable_name,
|
||||
type = "flags",
|
||||
default = default,
|
||||
possible = flags_to_table(possible),
|
||||
requires = requires,
|
||||
comment = comment,
|
||||
})
|
||||
return
|
||||
end
|
||||
|
||||
return "Invalid setting type \"" .. setting_type .. "\""
|
||||
end
|
||||
|
||||
local function parse_single_file(file, filepath, read_all, result, base_level, allow_secure)
|
||||
-- store this helper variable in the table so it's easier to pass to parse_setting_line()
|
||||
result.current_comment = {}
|
||||
result.current_hide_level = nil
|
||||
|
||||
local line = file:read("*line")
|
||||
while line do
|
||||
local error_msg = parse_setting_line(result, line, read_all, base_level, allow_secure)
|
||||
if error_msg then
|
||||
core.log("error", error_msg .. " in " .. filepath .. " \"" .. line .. "\"")
|
||||
end
|
||||
line = file:read("*line")
|
||||
end
|
||||
|
||||
result.current_comment = nil
|
||||
result.current_hide_level = nil
|
||||
end
|
||||
|
||||
|
||||
--- Returns table of setting types
|
||||
--
|
||||
-- @param read_all Whether to ignore certain setting types for GUI or not
|
||||
-- @parse_mods Whether to parse settingtypes.txt in mods and games
|
||||
function settingtypes.parse_config_file(read_all, parse_mods)
|
||||
local settings = {}
|
||||
|
||||
do
|
||||
local builtin_path = core.get_builtin_path() .. FILENAME
|
||||
local file = io.open(builtin_path, "r")
|
||||
if not file then
|
||||
core.log("error", "Can't load " .. FILENAME)
|
||||
return settings
|
||||
end
|
||||
|
||||
parse_single_file(file, builtin_path, read_all, settings, 0, true)
|
||||
|
||||
file:close()
|
||||
end
|
||||
|
||||
if parse_mods then
|
||||
-- Parse games
|
||||
local games_category_initialized = false
|
||||
for _, game in ipairs(pkgmgr.games) do
|
||||
local path = game.path .. DIR_DELIM .. FILENAME
|
||||
local file = io.open(path, "r")
|
||||
if file then
|
||||
if not games_category_initialized then
|
||||
fgettext_ne("Content: Games") -- not used, but needed for xgettext
|
||||
table.insert(settings, {
|
||||
name = "Content: Games",
|
||||
level = 0,
|
||||
type = "category",
|
||||
})
|
||||
games_category_initialized = true
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = game.path,
|
||||
readable_name = game.title,
|
||||
level = 1,
|
||||
type = "category",
|
||||
})
|
||||
|
||||
parse_single_file(file, path, read_all, settings, 2, false)
|
||||
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse mods
|
||||
pkgmgr.load_all()
|
||||
local mods_category_initialized = false
|
||||
local mods = pkgmgr.global_mods:get_list()
|
||||
table.sort(mods, function(a, b) return a.name < b.name end)
|
||||
|
||||
for _, mod in ipairs(mods) do
|
||||
local path = mod.path .. DIR_DELIM .. FILENAME
|
||||
local file = io.open(path, "r")
|
||||
if file then
|
||||
if not mods_category_initialized then
|
||||
fgettext_ne("Content: Mods") -- not used, but needed for xgettext
|
||||
table.insert(settings, {
|
||||
name = "Content: Mods",
|
||||
level = 0,
|
||||
type = "category",
|
||||
})
|
||||
mods_category_initialized = true
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = mod.path,
|
||||
readable_name = mod.title or mod.name,
|
||||
level = 1,
|
||||
type = "category",
|
||||
})
|
||||
|
||||
parse_single_file(file, path, read_all, settings, 2, false)
|
||||
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
|
||||
-- Parse client mods
|
||||
local clientmods_category_initialized = false
|
||||
local clientmods = {}
|
||||
pkgmgr.get_mods(core.get_clientmodpath(), "clientmods", clientmods)
|
||||
for _, mod in ipairs(clientmods) do
|
||||
local path = mod.path .. DIR_DELIM .. FILENAME
|
||||
local file = io.open(path, "r")
|
||||
if file then
|
||||
if not clientmods_category_initialized then
|
||||
fgettext_ne("Client Mods") -- not used, but needed for xgettext
|
||||
table.insert(settings, {
|
||||
name = "Client Mods",
|
||||
level = 0,
|
||||
type = "category",
|
||||
})
|
||||
clientmods_category_initialized = true
|
||||
end
|
||||
|
||||
table.insert(settings, {
|
||||
name = mod.path,
|
||||
readable_name = mod.title or mod.name,
|
||||
level = 1,
|
||||
type = "category",
|
||||
})
|
||||
|
||||
parse_single_file(file, path, read_all, settings, 2, false)
|
||||
|
||||
file:close()
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
return settings
|
||||
end
|
|
@ -1,120 +0,0 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2021-2 x2048
|
||||
--Copyright (C) 2022-3 rubenwardy
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
|
||||
|
||||
local shadow_levels_labels = {
|
||||
fgettext("Disabled"),
|
||||
fgettext("Very Low"),
|
||||
fgettext("Low"),
|
||||
fgettext("Medium"),
|
||||
fgettext("High"),
|
||||
fgettext("Very High"),
|
||||
fgettext("Custom"),
|
||||
}
|
||||
local PRESET_DISABLED = 1
|
||||
local PRESET_CUSTOM = #shadow_levels_labels
|
||||
|
||||
|
||||
-- max distance, texture size, texture_32bit, filters, map color
|
||||
local shadow_presets = {
|
||||
[2] = { 62, 512, true, 0, false },
|
||||
[3] = { 93, 1024, true, 0, false },
|
||||
[4] = { 140, 2048, true, 1, false },
|
||||
[5] = { 210, 4096, true, 2, true },
|
||||
[6] = { 300, 8192, true, 2, true },
|
||||
}
|
||||
|
||||
|
||||
local function detect_mapping_idx()
|
||||
if not core.settings:get_bool("enable_dynamic_shadows", false) then
|
||||
return PRESET_DISABLED
|
||||
end
|
||||
|
||||
local shadow_map_max_distance = tonumber(core.settings:get("shadow_map_max_distance"))
|
||||
local shadow_map_texture_size = tonumber(core.settings:get("shadow_map_texture_size"))
|
||||
local shadow_map_texture_32bit = core.settings:get_bool("shadow_map_texture_32bit", false)
|
||||
local shadow_filters = tonumber(core.settings:get("shadow_filters"))
|
||||
local shadow_map_color = core.settings:get_bool("shadow_map_color", false)
|
||||
|
||||
for i = 2, 6 do
|
||||
local preset = shadow_presets[i]
|
||||
if preset[1] == shadow_map_max_distance and
|
||||
preset[2] == shadow_map_texture_size and
|
||||
preset[3] == shadow_map_texture_32bit and
|
||||
preset[4] == shadow_filters and
|
||||
preset[5] == shadow_map_color then
|
||||
return i
|
||||
end
|
||||
end
|
||||
return PRESET_CUSTOM
|
||||
end
|
||||
|
||||
|
||||
local function apply_preset(preset)
|
||||
if preset then
|
||||
core.settings:set_bool("enable_dynamic_shadows", true)
|
||||
core.settings:set("shadow_map_max_distance", preset[1])
|
||||
core.settings:set("shadow_map_texture_size", preset[2])
|
||||
core.settings:set_bool("shadow_map_texture_32bit", preset[3])
|
||||
core.settings:set("shadow_filters", preset[4])
|
||||
core.settings:set_bool("shadow_map_color", preset[5])
|
||||
else
|
||||
core.settings:set_bool("enable_dynamic_shadows", false)
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
return {
|
||||
query_text = "Shadows",
|
||||
requires = {
|
||||
opengl = true,
|
||||
},
|
||||
get_formspec = function(self, avail_w)
|
||||
local labels = table.copy(shadow_levels_labels)
|
||||
local idx = detect_mapping_idx()
|
||||
|
||||
-- Remove "custom" if not already selected
|
||||
if idx ~= PRESET_CUSTOM then
|
||||
table.remove(labels, PRESET_CUSTOM)
|
||||
end
|
||||
|
||||
local fs =
|
||||
"label[0,0.2;" .. fgettext("Dynamic shadows") .. "]" ..
|
||||
"dropdown[0,0.4;3,0.8;dd_shadows;" .. table.concat(labels, ",") .. ";" .. idx .. ";true]" ..
|
||||
"label[0,1.5;" .. core.colorize("#bbb", fgettext("(The game will need to enable shadows as well)")) .. "]"
|
||||
return fs, 1.8
|
||||
end,
|
||||
on_submit = function(self, fields)
|
||||
if fields.dd_shadows then
|
||||
local old_shadow_level_idx = detect_mapping_idx()
|
||||
local shadow_level_idx = tonumber(fields.dd_shadows)
|
||||
if shadow_level_idx == nil or shadow_level_idx == old_shadow_level_idx then
|
||||
return false
|
||||
end
|
||||
|
||||
if shadow_level_idx == PRESET_CUSTOM then
|
||||
core.settings:set_bool("enable_dynamic_shadows", true)
|
||||
return true
|
||||
end
|
||||
|
||||
local preset = shadow_presets[shadow_level_idx]
|
||||
apply_preset(preset)
|
||||
return true
|
||||
end
|
||||
end,
|
||||
}
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2013 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2013 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
|
||||
local function prepare_credits(dest, source)
|
||||
|
|
|
@ -1,20 +1,7 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--Copyright (C) 2018 rubenwardy <rw@rubenwardy.com>
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- Copyright (C) 2018 rubenwardy <rw@rubenwardy.com>
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
|
||||
local function get_content_icons(packages_with_updates)
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
|
||||
local current_game, singleplayer_refresh_gamebar
|
||||
|
|
|
@ -1,19 +1,6 @@
|
|||
--Luanti
|
||||
--Copyright (C) 2014 sapier
|
||||
--
|
||||
--This program is free software; you can redistribute it and/or modify
|
||||
--it under the terms of the GNU Lesser General Public License as published by
|
||||
--the Free Software Foundation; either version 2.1 of the License, or
|
||||
--(at your option) any later version.
|
||||
--
|
||||
--This program is distributed in the hope that it will be useful,
|
||||
--but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
--MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
--GNU Lesser General Public License for more details.
|
||||
--
|
||||
--You should have received a copy of the GNU Lesser General Public License along
|
||||
--with this program; if not, write to the Free Software Foundation, Inc.,
|
||||
--51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||||
-- Luanti
|
||||
-- Copyright (C) 2014 sapier
|
||||
-- SPDX-License-Identifier: LGPL-2.1-or-later
|
||||
|
||||
local function get_sorted_servers()
|
||||
local servers = {
|
||||
|
@ -129,6 +116,7 @@ local function get_formspec(tabview, name, tabdata)
|
|||
local retval =
|
||||
-- Search
|
||||
"field[0.25,0.25;7,0.75;te_search;;" .. core.formspec_escape(tabdata.search_for) .. "]" ..
|
||||
"tooltip[te_search;" .. fgettext("Possible filters\ngame:<name>\nmod:<name>\nplayer:<name>") .. "]" ..
|
||||
"field_enter_after_edit[te_search;true]" ..
|
||||
"container[7.25,0.25]" ..
|
||||
"image_button[0,0;0.75,0.75;" .. core.formspec_escape(defaulttexturedir .. "search.png") .. ";btn_mp_search;]" ..
|
||||
|
@ -177,6 +165,26 @@ local function get_formspec(tabview, name, tabdata)
|
|||
core.formspec_escape(gamedata.serverdescription) .. "]"
|
||||
end
|
||||
|
||||
-- Mods button
|
||||
local mods = selected_server.mods
|
||||
if mods and #mods > 0 then
|
||||
local tooltip = ""
|
||||
if selected_server.gameid then
|
||||
tooltip = fgettext("Game: $1", selected_server.gameid) .. "\n"
|
||||
end
|
||||
tooltip = tooltip .. fgettext("Number of mods: $1", #mods)
|
||||
|
||||
retval = retval ..
|
||||
"tooltip[btn_view_mods;" .. tooltip .. "]" ..
|
||||
"style[btn_view_mods;padding=6]" ..
|
||||
"image_button[4,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir ..
|
||||
"server_view_mods.png") .. ";btn_view_mods;]"
|
||||
else
|
||||
retval = retval .. "image[4.1,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
|
||||
"server_view_mods_unavailable.png") .. "]"
|
||||
end
|
||||
|
||||
-- Clients list button
|
||||
local clients_list = selected_server.clients_list
|
||||
local can_view_clients_list = clients_list and #clients_list > 0
|
||||
if can_view_clients_list then
|
||||
|
@ -186,23 +194,31 @@ local function get_formspec(tabview, name, tabdata)
|
|||
local max_clients = 5
|
||||
if #clients_list > max_clients then
|
||||
retval = retval .. "tooltip[btn_view_clients;" ..
|
||||
fgettext("Clients:\n$1", table.concat(clients_list, "\n", 1, max_clients)) .. "\n..." .. "]"
|
||||
fgettext("Players:\n$1", table.concat(clients_list, "\n", 1, max_clients)) .. "\n..." .. "]"
|
||||
else
|
||||
retval = retval .. "tooltip[btn_view_clients;" ..
|
||||
fgettext("Clients:\n$1", table.concat(clients_list, "\n")) .. "]"
|
||||
fgettext("Players:\n$1", table.concat(clients_list, "\n")) .. "]"
|
||||
end
|
||||
retval = retval .. "style[btn_view_clients;padding=6]"
|
||||
retval = retval .. "image_button[4.5,1.3;0.5,0.5;" .. core.formspec_escape(defaulttexturedir ..
|
||||
"server_view_clients.png") .. ";btn_view_clients;]"
|
||||
else
|
||||
retval = retval .. "image[4.6,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
|
||||
"server_view_clients_unavailable.png") .. "]"
|
||||
end
|
||||
|
||||
-- URL button
|
||||
if selected_server.url then
|
||||
retval = retval .. "tooltip[btn_server_url;" .. fgettext("Open server website") .. "]"
|
||||
retval = retval .. "style[btn_server_url;padding=6]"
|
||||
retval = retval .. "image_button[" .. (can_view_clients_list and "4" or "4.5") .. ",1.3;0.5,0.5;" ..
|
||||
retval = retval .. "image_button[3.5,1.3;0.5,0.5;" ..
|
||||
core.formspec_escape(defaulttexturedir .. "server_url.png") .. ";btn_server_url;]"
|
||||
else
|
||||
retval = retval .. "image[3.6,1.4;0.3,0.3;" .. core.formspec_escape(defaulttexturedir ..
|
||||
"server_url_unavailable.png") .. "]"
|
||||
end
|
||||
|
||||
-- Favorites toggle button
|
||||
if is_selected_fav() then
|
||||
retval = retval .. "tooltip[btn_delete_favorite;" .. fgettext("Remove favorite") .. "]"
|
||||
retval = retval .. "style[btn_delete_favorite;padding=6]"
|
||||
|
@ -289,19 +305,109 @@ end
|
|||
|
||||
--------------------------------------------------------------------------------
|
||||
|
||||
local function search_server_list(input)
|
||||
local function parse_search_input(input)
|
||||
if not input:find("%S") then
|
||||
return -- Return nil if nothing to search for
|
||||
end
|
||||
|
||||
-- Search is not case sensitive
|
||||
input = input:lower()
|
||||
|
||||
local query = {keywords = {}, mods = {}, players = {}}
|
||||
|
||||
-- Process quotation enclosed parts
|
||||
input = input:gsub('(%S?)"([^"]*)"(%S?)', function(before, match, after)
|
||||
if before == "" and after == "" then -- Also have be separated by spaces
|
||||
table.insert(query.keywords, match)
|
||||
return " "
|
||||
end
|
||||
return before..'"'..match..'"'..after
|
||||
end)
|
||||
|
||||
-- Separate by space characters and handle special prefixes
|
||||
-- (words with special prefixes need an exact match and none of them can contain spaces)
|
||||
for word in input:gmatch("%S+") do
|
||||
local mod = word:match("^mod:(.*)")
|
||||
table.insert(query.mods, mod)
|
||||
local player = word:match("^player:(.*)")
|
||||
table.insert(query.players, player)
|
||||
local game = word:match("^game:(.*)")
|
||||
query.game = query.game or game
|
||||
if not (mod or player or game) then
|
||||
table.insert(query.keywords, word)
|
||||
end
|
||||
end
|
||||
|
||||
return query
|
||||
end
|
||||
|
||||
-- Prepares the server to be used for searching
|
||||
local function uncapitalize_server(server)
|
||||
local function table_lower(t)
|
||||
local r = {}
|
||||
for i, s in ipairs(t or {}) do
|
||||
r[i] = s:lower()
|
||||
end
|
||||
return r
|
||||
end
|
||||
|
||||
return {
|
||||
name = (server.name or ""):lower(),
|
||||
description = (server.description or ""):lower(),
|
||||
gameid = (server.gameid or ""):lower(),
|
||||
mods = table_lower(server.mods),
|
||||
clients_list = table_lower(server.clients_list),
|
||||
}
|
||||
end
|
||||
|
||||
-- Returns false if the query does not match
|
||||
-- otherwise returns a number to adjust the sorting priority
|
||||
local function matches_query(server, query)
|
||||
-- Search is not case sensitive
|
||||
server = uncapitalize_server(server)
|
||||
|
||||
-- Check if mods found
|
||||
for _, mod in ipairs(query.mods) do
|
||||
if table.indexof(server.mods, mod) < 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if players found
|
||||
for _, player in ipairs(query.players) do
|
||||
if table.indexof(server.clients_list, player) < 0 then
|
||||
return false
|
||||
end
|
||||
end
|
||||
|
||||
-- Check if game matches
|
||||
if query.game and query.game ~= server.gameid then
|
||||
return false
|
||||
end
|
||||
|
||||
-- Check if keyword found
|
||||
local name_matches = true
|
||||
local description_matches = true
|
||||
for _, keyword in ipairs(query.keywords) do
|
||||
name_matches = name_matches and server.name:find(keyword, 1, true)
|
||||
description_matches = description_matches and server.description:find(keyword, 1, true)
|
||||
end
|
||||
|
||||
return name_matches and 50 or description_matches and 0
|
||||
end
|
||||
|
||||
local function search_server_list(input, tabdata)
|
||||
menudata.search_result = nil
|
||||
if #serverlistmgr.servers < 2 then
|
||||
return
|
||||
end
|
||||
|
||||
-- setup the keyword list
|
||||
local keywords = {}
|
||||
for word in input:gmatch("%S+") do
|
||||
table.insert(keywords, word:lower())
|
||||
end
|
||||
|
||||
if #keywords == 0 then
|
||||
tabdata.pre_search_selection = tabdata.pre_search_selection or find_selected_server()
|
||||
|
||||
-- setup the search query
|
||||
local query = parse_search_input(input)
|
||||
if not query then
|
||||
return
|
||||
end
|
||||
|
||||
|
@ -310,16 +416,9 @@ local function search_server_list(input)
|
|||
-- Search the serverlist
|
||||
local search_result = {}
|
||||
for i, server in ipairs(serverlistmgr.servers) do
|
||||
local name_matches, description_matches = true, true
|
||||
for _, keyword in ipairs(keywords) do
|
||||
name_matches = name_matches and not not
|
||||
(server.name or ""):lower():find(keyword, 1, true)
|
||||
description_matches = description_matches and not not
|
||||
(server.description or ""):lower():find(keyword, 1, true)
|
||||
end
|
||||
if name_matches or description_matches then
|
||||
server.points = #serverlistmgr.servers - i
|
||||
+ (name_matches and 50 or 0)
|
||||
local match = matches_query(server, query)
|
||||
if match then
|
||||
server.points = #serverlistmgr.servers - i + match
|
||||
table.insert(search_result, server)
|
||||
end
|
||||
end
|
||||
|
@ -328,10 +427,32 @@ local function search_server_list(input)
|
|||
return
|
||||
end
|
||||
|
||||
local current_server = find_selected_server()
|
||||
|
||||
table.sort(search_result, function(a, b)
|
||||
return a.points > b.points
|
||||
end)
|
||||
menudata.search_result = search_result
|
||||
|
||||
-- Keep current selection if it's in search results
|
||||
if current_server then
|
||||
for _, server in ipairs(search_result) do
|
||||
if server.address == current_server.address and
|
||||
server.port == current_server.port then
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
-- Find first compatible server (favorite or public)
|
||||
for _, server in ipairs(search_result) do
|
||||
if is_server_protocol_compat(server.proto_min, server.proto_max) then
|
||||
set_selected_server(server)
|
||||
return
|
||||
end
|
||||
end
|
||||
-- If no compatible server found, clear selection
|
||||
set_selected_server(nil)
|
||||
end
|
||||
|
||||
local function main_button_handler(tabview, fields, name, tabdata)
|
||||
|
@ -371,6 +492,7 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
|||
end
|
||||
if event.type == "CHG" then
|
||||
set_selected_server(server)
|
||||
tabdata.pre_search_selection = nil
|
||||
return true
|
||||
end
|
||||
end
|
||||
|
@ -384,11 +506,9 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
|||
if fields.btn_delete_favorite then
|
||||
local idx = core.get_table_index("servers")
|
||||
if not idx then return end
|
||||
local server = tabdata.lookup[idx]
|
||||
if not server then return end
|
||||
|
||||
serverlistmgr.delete_favorite(server)
|
||||
set_selected_server(server)
|
||||
serverlistmgr.delete_favorite(tabdata.lookup[idx])
|
||||
set_selected_server(tabdata.lookup[idx+1])
|
||||
return true
|
||||
end
|
||||
|
||||
|
@ -405,20 +525,27 @@ local function main_button_handler(tabview, fields, name, tabdata)
|
|||
return true
|
||||
end
|
||||
|
||||
if fields.btn_view_mods then
|
||||
local dlg = create_server_list_mods_dialog(find_selected_server())
|
||||
dlg:set_parent(tabview)
|
||||
tabview:hide()
|
||||
dlg:show()
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.btn_mp_clear then
|
||||
tabdata.search_for = ""
|
||||
menudata.search_result = nil
|
||||
if tabdata.pre_search_selection then
|
||||
set_selected_server(tabdata.pre_search_selection)
|
||||
tabdata.pre_search_selection = nil
|
||||
end
|
||||
return true
|
||||
end
|
||||
|
||||
if fields.btn_mp_search or fields.key_enter_field == "te_search" then
|
||||
tabdata.search_for = fields.te_search
|
||||
search_server_list(fields.te_search:lower())
|
||||
if menudata.search_result then
|
||||
-- Note: This clears the selection if there are no results
|
||||
set_selected_server(menudata.search_result[1])
|
||||
end
|
||||
|
||||
search_server_list(fields.te_search, tabdata)
|
||||
return true
|
||||
end
|
||||
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue