1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
|
local storage = minetest.get_mod_storage()
playersettings = {}
playersettings.registered = {}
playersettings.settingslist = {}
playersettings.highlighted = {}
playersettings.form = "formspec_version[4]"..
"size[15,15]" ..
"button[12.5,13.5;2,1;quit;Save and Exit]"..
"textlist[0.5,0.5;7,14;settinglist;%s;%d;false]"..
"label[8,2;%s]"..
"label[8,3;%s]"..
"%s"
function playersettings.openform(player)
if type(player) == "string" then player = minetest.get_player_by_name(player) end
local name = player:get_player_name()
if #playersettings.settingslist < 1 then
local form = "formspec_version[4]"..
"size[12,3.5]"..
"label[0.5,1;No settings currently exist.]"..
"label[0.5,1.5;To use the settings menu, one or more mods that provide some settings must be installed.]"..
"button_exit[5,2;2,1;quit;Close]"
minetest.show_formspec(name,"playersettings:nosettings",form)
return
end
if not playersettings.highlighted[name] then playersettings.highlighted[name] = 1 end
local settingslist = ""
for _,setting in ipairs(playersettings.settingslist) do
settingslist = settingslist..minetest.formspec_escape(playersettings.registered[setting].shortdesc)..","
end
settingslist = settingslist:sub(1,-2)
local settingname = playersettings.settingslist[playersettings.highlighted[name]]
local def = playersettings.registered[settingname]
local extras = ""
if def.type == "boolean" then
extras = "checkbox[8,8;checkbox;Enabled;%s]"
extras = extras:format(playersettings.get(name,settingname) and "true" or "false")
elseif def.type == "string" then
extras = "field[8,8;6,1;field;Enter value:;%s]label[8,9.5;Allowed values: %s]field_close_on_enter[field;false]"
extras = extras:format(minetest.formspec_escape(playersettings.get(name,settingname)),minetest.formspec_escape(def.values or "any string"))
elseif def.type == "number" then
extras = "field[8,8;6,1;field;Enter value:;%s]label[8,9.5;Allowed values: %s]field_close_on_enter[field;false]"
local allowed = "any number"
if def.min and def.max then
allowed = "numbers "..def.min.." to "..def.max
elseif def.max then
allowed = "numbers up to "..def.max
elseif def.min then
allowed = "numbers "..def.min.." and up"
end
if def.integer then
allowed = allowed.." (whole numbers only)"
end
extras = extras:format(minetest.formspec_escape(playersettings.get(name,settingname)),allowed)
elseif def.type == "enum" then
extras = "dropdown[8,8;6,1;dropdown;%s;%d;false]"
local choices = ""
local current = playersettings.get(name,settingname)
local selected = 1
for k,v in ipairs(def.values) do
choices = choices..minetest.formspec_escape(v)..","
if v == current then selected = k end
end
choices = choices:sub(1,-2)
extras = extras:format(choices,selected)
end
local highlighted = playersettings.highlighted[name]
local shortdesc = minetest.formspec_escape(def.shortdesc)
local longdesc = minetest.formspec_escape(def.longdesc)
local form = playersettings.form:format(settingslist,highlighted,shortdesc,longdesc,extras)
minetest.show_formspec(name,"playersettings:settings",form)
end
function playersettings.handleform(player,form,fields)
if form ~= "playersettings:settings" then return end
local name = player:get_player_name()
local settingname = playersettings.settingslist[playersettings.highlighted[name]]
local def = playersettings.registered[settingname]
if def.type == "boolean" and fields.checkbox then
playersettings.set(name,settingname,fields.checkbox == "true")
elseif def.type == "enum" and fields.dropdown then
playersettings.set(name,settingname,fields.dropdown)
elseif def.type == "number" and fields.field then
local value = tonumber(fields.field)
if value
and ((not def.max) or (value <= def.max))
and ((not def.min) or (value >= def.min)) then
if def.integer then value = math.floor(value) end
playersettings.set(name,settingname,value)
end
elseif def.type == "string" and fields.field then
playersettings.set(name,settingname,fields.field)
end
if fields.settinglist then
local exp = minetest.explode_textlist_event(fields.settinglist)
if exp.type == "CHG" then
playersettings.highlighted[name] = exp.index
playersettings.openform(player)
end
end
if fields.quit then
minetest.close_formspec(name,"playersettings:settings")
end
end
function playersettings.getdefault(setting)
local def = playersettings.registered[setting]
if def.default ~= nil then return def.default end
if def.type == "boolean" then
return false
elseif def.type == "number" then
return def.min or 0
elseif def.type == "string" then
return ""
end
end
function playersettings.get(name,setting)
assert(type(name) == "string",string.format("Invalid player name (expected string, got %s)",type(name)))
assert(type(setting) == "string",string.format("Invalid setting name (expected string, got %s)",type(setting)))
assert(playersettings.registered[setting],"No such setting: "..setting)
local value = minetest.deserialize(storage:get_string(string.format("%s|%s",name,setting)))
if value then
return value
else
return playersettings.getdefault(setting)
end
end
function playersettings.set(name,setting,value)
assert(type(name) == "string",string.format("Invalid player name (expected string, got %s)",type(name)))
assert(type(setting) == "string",string.format("Invalid setting name (expected string, got %s)",type(setting)))
assert(playersettings.registered[setting],"No such setting: "..setting)
local old = playersettings.get(name,setting)
local def = playersettings.registered[setting]
if def.onchange then
if not def.onchange(name,old,value) then return end
end
storage:set_string(string.format("%s|%s",name,setting),minetest.serialize(value))
if def.afterchange then def.afterchange(name,old,value) end
end
function playersettings.register(setting,def)
assert(type(setting) == "string",string.format("Invalid setting name (expected string, got %s)",type(setting)))
assert(not playersettings.registered[setting],string.format("Setting %s already defined",setting))
assert(type(def) == "table",string.format("Invalid setting definition (expected table, got %s)",type(def)))
table.insert(playersettings.settingslist,setting)
table.sort(playersettings.settingslist)
playersettings.registered[setting] = def
end
function playersettings.onjoin(player)
local name = player:get_player_name()
for setting,def in pairs(playersettings.registered) do
if type(def.onjoin) == "function" then
def.onjoin(name,playersettings.get(name,setting))
end
end
end
if minetest.global_exists("unified_inventory") then
unified_inventory.register_button("playersettings",
{
action = playersettings.openform,
tooltip = "Settings",
type = "image",
image = "playersettings_settings_button.png"
}
)
end
minetest.register_chatcommand("settings",{
description = "Open player settings menu",
func = playersettings.openform,
})
minetest.register_on_joinplayer(playersettings.onjoin)
minetest.register_on_player_receive_fields(playersettings.handleform)
|