Subversion Repositories fei

Rev

Rev 2 | Details | Compare with Previous | Last modification | View Log | RSS feed

Rev Author Line No. Line
2 mrkubax10 1
-- Finally Enough Items
2
-- Copyright (C) 2026 mrkubax10
3
 
4
-- This program is free software: you can redistribute it and/or modify
5
-- it under the terms of the GNU Affero General Public License as published by
6
-- the Free Software Foundation, either version 3 of the License, or
7
-- (at your option) any later version.
8
 
9
-- This program is distributed in the hope that it will be useful,
10
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
11
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
-- GNU Affero General Public License for more details.
13
 
14
-- You should have received a copy of the GNU Affero General Public License
15
-- along with this program. If not, see <https://www.gnu.org/licenses/>.
16
 
17
local prev_inventory_formspec_values={}
18
local update_elapsed = 0
19
 
20
-- Extracts next number from string from the beginning
21
local function scan_number(str)
22
	local begin_index = 0
23
	local end_index = 0
24
	local collect = false
25
	local index = 1
26
	for ch in string.gmatch(str, ".") do
27
		if collect then
28
			if not string.match(ch, "%d") and ch~="." then
29
				end_index = index
30
				break
31
			end
32
		else
33
			if string.match(ch, "%d") then
34
				begin_index = index
35
				collect = true
36
			end
37
		end
38
		index = index+1
39
	end
40
	if begin_index==end_index then
41
		return 0, str
42
	end
43
	return tonumber(string.sub(str, begin_index, end_index-1)), string.sub(str, end_index+1)
44
end
45
 
46
local function parse_formspec_information(formspec)
47
	local width = 0
48
	local height = 0
3 mrkubax10 49
	local _, size_end = string.find(formspec, "size%[")
2 mrkubax10 50
	if size_end then
51
		local size_pattern = string.sub(formspec, size_end)
52
		width, size_pattern = scan_number(size_pattern)
53
		height, size_pattern = scan_number(size_pattern)
54
	end
55
 
3 mrkubax10 56
	local version = 1
57
	local _, version_end = string.find(formspec, "formspec_version%[")
58
	if version_end then
59
		local version_pattern = string.sub(formspec, version_end)
60
		version, _ = scan_number(version_pattern)
61
	end
62
 
63
	local real_coordinates = false
64
	local _, real_coordinates_end = string.find(formspec, "real_coordinates%[")
65
	if real_coordinates_end then
66
		-- Check for true keyword
67
		local real_coordinates_pattern = string.sub(formspec, real_coordinates_end, real_coordinates_end+4)
68
		real_coordinates = real_coordinates_pattern=="true"
69
	end
70
 
2 mrkubax10 71
	return {
72
		width = width,
73
		height = height,
3 mrkubax10 74
		legacy_coordinates = version==1 and not real_coordinates
2 mrkubax10 75
	}
76
end
77
 
78
local function determine_anchor(window_info, inv_formspec_info)
79
	local element_spacing = inv_formspec_info.legacy_coordinates and 0.25 or 0
80
	local formspec_center_x = inv_formspec_info.width/2
81
	local formspec_center_y = inv_formspec_info.height/2
82
	local window_center_x = window_info.max_formspec_size.x/2
83
	local window_center_y = window_info.max_formspec_size.y/2
84
	return {
85
		x = formspec_center_x-element_spacing+window_center_x-window_center_x*element_spacing,
86
		y = formspec_center_y-element_spacing-window_center_y+window_center_y*element_spacing
87
	}
88
end
89
 
90
local function create_items_view(player, prev_inventory_formspec)
91
	local window_info = minetest.get_player_window_information(player:get_player_name())
92
	-- There is a brief moment when the player is still joining where get_player_window_information returns nil
93
	if not window_info then
94
		return ""
95
	end
96
	local inv_formspec_info = parse_formspec_information(player:get_formspec_prepend()..prev_inventory_formspec)
97
	local items_anchor = determine_anchor(window_info, inv_formspec_info)
98
	local formspec = {
99
		"style_type[label;noclip=true]",
100
		"style_type[image_button;noclip=true]",
101
		string.format("image_button[%f,%f;1,1;default_furnace_front.png;button1;]", items_anchor.x, items_anchor.y),
102
	}
103
	return table.concat(formspec, "")
104
end
105
 
106
local function update_if_needed(player, prev_inventory_formspec)
107
	player:set_inventory_formspec(prev_inventory_formspec..create_items_view(player, prev_inventory_formspec))
108
end
109
 
110
-- This is a kind of hacky way of registering in on_mods_loaded to make sure that our on_joinplayer handler is called last
111
minetest.register_on_mods_loaded(function()
112
	minetest.register_on_joinplayer(function(player)
113
		prev_inventory_formspec_values[player:get_player_name()] = player:get_inventory_formspec()
114
	end)
115
end)
116
 
117
minetest.register_on_leaveplayer(function(player)
118
	prev_inventory_formspec_values[player:get_player_name()] = nil
119
end)
120
 
121
minetest.register_on_player_receive_fields(function(player, formname, fields)
122
	-- If player inventory formspec was somehow updated, then change the previous formspec value too
123
	if formname~="" then
124
		return
125
	end
126
	prev_inventory_formspec_values[player:get_player_name()] = player:get_inventory_formspec()
127
	update_if_needed(player, player:get_inventory_formspec())
128
end)
129
 
130
minetest.register_globalstep(function(dtime)
131
	update_elapsed = update_elapsed+dtime
132
	-- TODO: Introduce option which will allow to adjust item list updating frequency
133
	if update_elapsed<0.5 then
134
		return
135
	end
136
	update_elapsed = 0
137
	local players = minetest.get_connected_players()
138
	for _,player in ipairs(players) do
139
		local prev_inventory_formspec = prev_inventory_formspec_values[player:get_player_name()]
140
		if prev_inventory_formspec then
141
			update_if_needed(player, prev_inventory_formspec)
142
		end
143
	end
144
end)
145
 
146
minetest.register_chatcommand("formspec",{
147
	func=function(name)
148
		local window_info = minetest.get_player_window_information(name)
149
		local formspec={
150
			--"formspec_version[2]",
151
			"size[5,5]",
152
			"style_type[button;noclip=true]",
153
			"button[0,0;1,1;button1;]",
154
			"button[1,0;1,1;button2;]",
155
			"button[2,0;1,1;button2;]",
156
			"button[2.25,1;1,1;button2;]"
157
			--string.format("button[%f,0;1,1;button1;]", window_info.max_formspec_size.x/2+2.5)
158
		}
159
		minetest.show_formspec(name, "fei:formspec", table.concat(formspec, ""))
160
	end
161
})