summaryrefslogtreecommitdiff
path: root/init.lua
blob: c0c3c5f6bdb9b1f0548980e8af10e0f1793a3195 (plain)
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)