summaryrefslogtreecommitdiff
path: root/worldedit_gui/init.lua
blob: b88a82ec510af358e7b84013c6c34d0aee7fcd79 (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
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
worldedit = worldedit or {}

--[[
Example:

    worldedit.register_gui_function("worldedit_gui_hollow_cylinder", {
    	name = "Make Hollow Cylinder",
    	privs = {worldedit=true},
    	get_formspec = function(name) return "some formspec here" end,
    	on_select = function(name) print(name .. " clicked the button!") end,
    })

Use `nil` for the `options` parameter to unregister the function associated with the given identifier.

Use `nil` for the `get_formspec` field to denote that the function does not have its own screen.

The `privs` field may not be `nil`.

If the identifier is already registered to another function, it will be replaced by the new one.

The `on_select` function must not call `worldedit.show_page`
]]

worldedit.pages = {} --mapping of identifiers to options
local identifiers = {} --ordered list of identifiers
worldedit.register_gui_function = function(identifier, options)
	if options.privs == nil or next(options.privs) == nil then
		error("privs unset")
	end
	worldedit.pages[identifier] = options
	table.insert(identifiers, identifier)
end

--[[
Example:

    worldedit.register_gui_handler("worldedit_gui_hollow_cylinder", function(name, fields)
    	print(minetest.serialize(fields))
    end)
]]

worldedit.register_gui_handler = function(identifier, handler)
	local enabled = true
	minetest.register_on_player_receive_fields(function(player, formname, fields)
		if not enabled then return false end
		enabled = false
		minetest.after(0.2, function() enabled = true end)
		local name = player:get_player_name()

		--ensure the player has permission to perform the action
		local entry = worldedit.pages[identifier]
		if entry and minetest.check_player_privs(name, entry.privs) then
			return handler(name, fields)
		end
		return false
	end)
end

worldedit.get_formspec_header = function(identifier)
	local entry = worldedit.pages[identifier] or {}
	return "button[0,0;2,0.5;worldedit_gui;Back]" ..
		string.format("label[2,0;WorldEdit GUI > %s]", entry.name or "")
end

local get_formspec = function(name, identifier)
	if worldedit.pages[identifier] then
		return worldedit.pages[identifier].get_formspec(name)
	end
	return worldedit.pages["worldedit_gui"].get_formspec(name) --default to showing main page if an unknown page is given
end

--implement worldedit.show_page(name, page) in different ways depending on the available APIs
if rawget(_G, "unified_inventory") then --unified inventory installed
	local old_func = worldedit.register_gui_function
	worldedit.register_gui_function = function(identifier, options)
		old_func(identifier, options)
		unified_inventory.register_page(identifier, {get_formspec=function(player) return {formspec=options.get_formspec(player:get_player_name())} end})
	end

	unified_inventory.register_button("worldedit_gui", {
		type = "image",
		image = "inventory_plus_worldedit_gui.png",
	})

	minetest.register_on_player_receive_fields(function(player, formname, fields)
		local name = player:get_player_name()
		if fields.worldedit_gui then --main page
			worldedit.show_page(name, "worldedit_gui")
			return true
		elseif fields.worldedit_gui_exit then --return to original page
			local player = minetest.get_player_by_name(name)
			if player then
				unified_inventory.set_inventory_formspec(player, "craft")
			end
			return true
		end
		return false
	end)

	worldedit.show_page = function(name, page)
		local player = minetest.get_player_by_name(name)
		if player then
			player:set_inventory_formspec(get_formspec(name, page))
		end
	end
elseif rawget(_G, "inventory_plus") then --inventory++ installed
	minetest.register_on_joinplayer(function(player)
		local can_worldedit = minetest.check_player_privs(player:get_player_name(), {worldedit=true})
		if can_worldedit then
			inventory_plus.register_button(player, "worldedit_gui", "WorldEdit")
		end
	end)

	--show the form when the button is pressed and hide it when done
	local gui_player_formspecs = {}
	minetest.register_on_player_receive_fields(function(player, formname, fields)
		local name = player:get_player_name()
		if fields.worldedit_gui then --main page
			gui_player_formspecs[name] = player:get_inventory_formspec()
			worldedit.show_page(name, "worldedit_gui")
			return true
		elseif fields.worldedit_gui_exit then --return to original page
			if gui_player_formspecs[name] then
				inventory_plus.set_inventory_formspec(player, inventory_plus.get_formspec(player, "main"))
			end
			return true
		end
		return false
	end)

	worldedit.show_page = function(name, page)
		local player = minetest.get_player_by_name(name)
		if player then
			inventory_plus.set_inventory_formspec(player, get_formspec(name, page))
		end
	end
elseif rawget(_G, "smart_inventory") then -- smart_inventory installed
	-- redefinition: Update the code element on inventory page to show the we-page
	function worldedit.show_page(name, page)
		local state = smart_inventory.get_page_state("worldedit_gui", name)
		if state then
			state:get("code"):set_we_formspec(page)
			state.location.rootState:show() -- update inventory page
		end
	end

	-- smart_inventory page callback. Contains just a "custom code" element
	local function smart_worldedit_gui_callback(state)
		local codebox = state:element("code", { name = "code", code = "" })
		function codebox:set_we_formspec(we_page)
			local new_formspec = get_formspec(state.location.rootState.location.player, we_page)
			new_formspec = new_formspec:gsub('button_exit','button') --no inventory closing
			self.data.code = "container[1,1]".. new_formspec .. "container_end[]"
		end
		codebox:set_we_formspec("worldedit_gui")

		-- process input (the back button)
		state:onInput(function(state, fields, player)
			if fields.worldedit_gui then --main page
				state:get("code"):set_we_formspec("worldedit_gui")
			elseif fields.worldedit_gui_exit then --return to original page
				state:get("code"):set_we_formspec("worldedit_gui")
				state.location.parentState:get("crafting_button"):submit() -- switch to the crafting tab
			end
		end)
	end

	-- all handler should return false to force inventory UI update
	local orig_register_gui_handler = worldedit.register_gui_handler
	worldedit.register_gui_handler = function(identifier, handler)
		local wrapper = function(...)
			handler(...)
			return false
		end
		orig_register_gui_handler(identifier, wrapper)
	end

	-- register the inventory button
	smart_inventory.register_page({
		name = "worldedit_gui",
		tooltip = "Edit your World!",
		icon = "inventory_plus_worldedit_gui.png",
		smartfs_callback = smart_worldedit_gui_callback,
		sequence = 99
	})
elseif rawget(_G, "sfinv") then --sfinv installed (part of minetest_game since 0.4.15)
	assert(sfinv.enabled)
	local orig_get = sfinv.pages["sfinv:crafting"].get
	sfinv.override_page("sfinv:crafting", {
		get = function(self, player, context)
			local can_worldedit = minetest.check_player_privs(player, {worldedit=true})
			local fs = orig_get(self, player, context)
			return fs .. (can_worldedit and "image_button[0,0;1,1;inventory_plus_worldedit_gui.png;worldedit_gui;]" or "")
		end
	})

	--compatibility with pre-0.4.16 sfinv
	local set_page = sfinv.set_page or function(player, name)
		--assumptions: src pg has no leave callback, dst pg has no enter callback
		local ctx = {page=name}
		sfinv.contexts[player:get_player_name()] = ctx
		sfinv.set_player_inventory_formspec(player, ctx)
	end

	--show the form when the button is pressed and hide it when done
	minetest.register_on_player_receive_fields(function(player, formname, fields)
		if fields.worldedit_gui then --main page
			worldedit.show_page(player:get_player_name(), "worldedit_gui")
			return true
		elseif fields.worldedit_gui_exit then --return to original page
			set_page(player, "sfinv:crafting")
			return true
		end
		return false
	end)

	worldedit.show_page = function(name, page)
		local player = minetest.get_player_by_name(name)
		if player then
			player:set_inventory_formspec(get_formspec(name, page))
		end
	end
else
	error(
		"worldedit_gui requires a supported \"gui management\" mod to be installed\n"..
		"To use the GUI you need to either\n"..
		"* Use minetest_game (at least 0.4.15) or a subgame with compatible sfinv\n"..
		"* Install Unified Inventory or Inventory++\n"..
		"If you do not want to use worldedit_gui, disable it by editing world.mt or from the Main Menu"
	)
end

worldedit.register_gui_function("worldedit_gui", {
	name = "WorldEdit GUI",
	privs = {interact=true},
	get_formspec = function(name)
		--create a form with all the buttons arranged in a grid
		local buttons, x, y, index = {}, 0, 1, 0
		local width, height = 3, 0.8
		local columns = 5
		for i, identifier in pairs(identifiers) do
			if identifier ~= "worldedit_gui" then
				local entry = worldedit.pages[identifier]
				table.insert(buttons, string.format((entry.get_formspec and "button" or "button_exit") ..
					"[%g,%g;%g,%g;%s;%s]", x, y, width, height, identifier, minetest.formspec_escape(entry.name)))

				index, x = index + 1, x + width
				if index == columns then --row is full
					x, y = 0, y + height
					index = 0
				end
			end
		end
		if index == 0 then --empty row
			y = y - height
		end
		return string.format("size[%g,%g]", math.max(columns * width, 5), math.max(y + 0.5, 3)) ..
			"button[0,0;2,0.5;worldedit_gui_exit;Back]" ..
			"label[2,0;WorldEdit GUI]" ..
			table.concat(buttons)
	end,
})

worldedit.register_gui_handler("worldedit_gui", function(name, fields)
	for identifier, entry in pairs(worldedit.pages) do --check for WorldEdit GUI main formspec button selection
		if fields[identifier] and identifier ~= "worldedit_gui" then
			--ensure player has permission to perform action
			local has_privs, missing_privs = minetest.check_player_privs(name, entry.privs)
			if not has_privs then
				worldedit.player_notify(name, "you are not allowed to use this function (missing privileges: " .. table.concat(missing_privs, ", ") .. ")")
				return false
			end
			if entry.on_select then
				entry.on_select(name)
			end
			if entry.get_formspec then
				worldedit.show_page(name, identifier)
			end
			return true
		end
	end
	return false
end)

dofile(minetest.get_modpath(minetest.get_current_modname()) .. "/functionality.lua")