get_viewed_count returns nil for unknown player
[minetest_doc.git] / init.lua
blobf2a83567a51e0140383de4ee1a3758168fce1f19
1 doc = {}
3 doc.VERSION = {}
4 doc.VERSION.MAJOR = 0
5 doc.VERSION.MINOR = 1
6 doc.VERSION.PATCH = 0
7 doc.VERSION.STRING = doc.VERSION.MAJOR.."."..doc.VERSION.MINOR.."."..doc.VERSION.PATCH
10 doc.data = {}
11 doc.data.categories = {}
12 doc.data.category_order = {}
13 doc.data.players = {}
15 -- Space for additional APIs
16 doc.sub = {}
18 --[[ Core API functions ]]
20 -- Add a new category
21 function doc.new_category(id, def)
22 if doc.data.categories[id] == nil and id ~= nil then
23 doc.data.categories[id] = {}
24 doc.data.categories[id].entries = {}
25 doc.data.categories[id].entry_count = 0
26 doc.data.categories[id].def = def
27 doc.data.categories[id].entry_aliases = {}
28 table.insert(doc.data.category_order, id)
29 return true
30 else
31 return false
32 end
33 end
35 -- Add a new entry
36 function doc.new_entry(category_id, entry_id, def)
37 if doc.data.categories[category_id] ~= nil then
38 doc.data.categories[category_id].entries[entry_id] = def
39 doc.data.categories[category_id].entry_count = doc.data.categories[category_id].entry_count + 1
40 return true
41 else
42 return false
43 end
44 end
46 -- Marks a particular entry as viewed by a certain player
47 function doc.mark_entry_as_viewed(playername, category_id, entry_id)
48 if doc.data.players[playername].stored_data.viewed[category_id] == nil then
49 doc.data.players[playername].stored_data.viewed[category_id] = {}
50 doc.data.players[playername].stored_data.viewed_count[category_id] = 0
51 end
52 if doc.data.players[playername].stored_data.viewed[category_id][entry_id] ~= true then
53 doc.data.players[playername].stored_data.viewed[category_id][entry_id] = true
54 doc.data.players[playername].stored_data.viewed_count[category_id] = doc.data.players[playername].stored_data.viewed_count[category_id] + 1
55 -- Needed because viewed entries get a different color
56 doc.data.players[playername].entry_textlist_needs_updating = true
57 end
58 end
60 -- Returns true if the specified entry has been viewed by the player
61 function doc.entry_viewed(playername, category_id, entry_id)
62 if doc.data.players[playername].stored_data.viewed[category_id] == nil then
63 return false
64 else
65 return doc.data.players[playername].stored_data.viewed[category_id][entry_id] == true
66 end
67 end
69 -- Opens the main documentation formspec for the player
70 function doc.show_doc(playername)
71 local formspec = doc.formspec_core()..doc.formspec_main()
72 minetest.show_formspec(playername, "doc:main", formspec)
73 end
75 -- Opens the documentation formspec for the player at the specified category
76 function doc.show_category(playername, category_id)
77 doc.data.players[playername].catsel = nil
78 doc.data.players[playername].category = category_id
79 doc.data.players[playername].entry = nil
80 local formspec = doc.formspec_core(2)..doc.formspec_category(category_id, playername)
81 minetest.show_formspec(playername, "doc:category", formspec)
82 end
84 -- Opens the documentation formspec for the player showing the specified entry in a category
85 function doc.show_entry(playername, category_id, entry_id)
86 doc.data.players[playername].catsel = nil
87 doc.data.players[playername].category = category_id
88 doc.data.players[playername].entry = entry_id
89 doc.mark_entry_as_viewed(playername, category_id, entry_id)
90 local eids, catsel = doc.data.players[playername].entry_ids, doc.data.players[playername].catsel
91 local formspec = doc.formspec_core(3)..doc.formspec_entry(category_id, entry_id)
92 minetest.show_formspec(playername, "doc:entry", formspec)
93 end
95 -- Returns true if and only if:
96 -- * The specified category exists
97 -- * This category contains the specified entry
98 function doc.entry_exists(category_id, entry_id)
99 if doc.data.categories[category_id] ~= nil then
100 if doc.data.categories[category_id].entries[entry_id] ~= nil then
101 -- Entry exists
102 return true
103 else
104 -- Entry of this ID does not exist, so we check if there's an alis for it
105 return doc.data.categories[category_id].entry_aliases[entry_id] ~= nil
107 else
108 return false
112 -- Adds aliases for an entry. Attempting to open an entry by an alias name
113 -- results in opening the entry of the original name.
114 -- Aliases are true within one category only.
115 function doc.add_entry_aliases(category_id, entry_id, aliases)
116 for a=1,#aliases do
117 doc.data.categories[category_id].entry_aliases[aliases[a]] = entry_id
121 -- Same as above, but only adds one alias
122 function doc.add_entry_alias(category_id, entry_id, alias)
123 doc.data.categories[category_id].entry_aliases[alias] = entry_id
126 -- Returns number of categories
127 function doc.get_category_count()
128 return #doc.data.category_order
131 -- Returns number of entries in category
132 function doc.get_entry_count(category_id)
133 return doc.data.categories[category_id].entry_count
136 -- Returns how many entries have been viewed by the player
137 function doc.get_viewed_count(playername, category_id)
138 if doc.data.players[playername] == nil then
139 return nil
141 local count = doc.data.players[playername].stored_data.viewed_count[category_id]
142 if count == nil then
143 return 0
144 else
145 return count
149 --[[ Functions for internal use ]]
151 function doc.formspec_core(tab)
152 if tab == nil then tab = 1 else tab = tostring(tab) end
153 return "size[12,9]tabheader[0,0;doc_header;Main,Category,Entry;"..tab..";true;false]"
156 function doc.formspec_main()
157 local y = 1
158 local formstring = "label[0,0;Available help topics:]"
159 for c=1,#doc.data.category_order do
160 local id = doc.data.category_order[c]
161 local data = doc.data.categories[id]
162 local button = "button[0,"..y..";3,1;doc_button_category_"..id..";"..minetest.formspec_escape(data.def.name).."]"
163 formstring = formstring .. button
164 y = y + 1
166 return formstring
169 function doc.generate_entry_list(cid, playername)
170 local formstring
171 if doc.data.players[playername].entry_textlist == nil
172 or doc.data.players[playername].category ~= cid
173 or doc.data.players[playername].entry_textlist_needs_updating == true then
174 local entry_textlist = "textlist[0,1;11,7;doc_catlist;"
175 local counter = 0
176 doc.data.players[playername].entry_ids = {}
177 local entries = doc.get_sorted_entry_names(cid)
178 for i=1, #entries do
179 local eid = entries[i].eid
180 table.insert(doc.data.players[playername].entry_ids, eid)
181 -- Colorize entries based on viewed status
182 -- Not viewed: Cyan
183 local viewedprefix = "#00FFFF"
184 if doc.entry_viewed(playername, cid, eid) then
185 -- Viewed: White
186 viewedprefix = "#FFFFFF"
188 entry_textlist = entry_textlist .. viewedprefix .. minetest.formspec_escape(entries[i].name) .. ","
189 counter = counter + 1
191 if counter >= 1 then
192 entry_textlist = string.sub(entry_textlist, 1, #entry_textlist-1)
194 local catsel = doc.data.players[playername].catsel
195 if catsel then
196 entry_textlist = entry_textlist .. ";"..catsel
198 entry_textlist = entry_textlist .. "]"
199 doc.data.players[playername].entry_textlist = entry_textlist
200 formstring = entry_textlist
201 doc.data.players[playername].entry_testlist_needs_updating = false
202 else
203 formstring = doc.data.players[playername].entry_textlist
205 return formstring
208 function doc.get_sorted_entry_names(cid)
209 local sort_table = {}
210 local entry_table = {}
211 for eid,entry in pairs(doc.data.categories[cid].entries) do
212 local new_entry = table.copy(entry)
213 new_entry.eid = eid
214 table.insert(entry_table, new_entry)
215 table.insert(sort_table, entry.name)
217 table.sort(sort_table)
218 local reverse_sort_table = table.copy(sort_table)
219 for i=1, #sort_table do
220 reverse_sort_table[sort_table[i]] = i
222 local comp = function(e1, e2)
223 if reverse_sort_table[e1.name] < reverse_sort_table[e2.name] then return true else return false end
225 table.sort(entry_table, comp)
227 return entry_table
230 function doc.formspec_category(id, playername)
231 local formstring
232 if id == nil then
233 formstring = "label[0,0;You haven't selected a help topic yet. Please select one in the category list first.]"
234 formstring = formstring .. "button[0,1;3,1;doc_button_goto_main;Go to category list]"
235 else
236 formstring = "label[0,0;Current help topic: "..doc.data.categories[id].def.name.."]"
237 formstring = formstring .. "label[0,0.5;Available entries:]"
238 formstring = formstring .. doc.generate_entry_list(id, playername)
239 formstring = formstring .. "button[0,8;3,1;doc_button_goto_entry;Show entry]"
241 return formstring
244 function doc.formspec_entry(category_id, entry_id)
245 local formstring
246 if category_id == nil then
247 formstring = "label[0,0;You haven't selected a help topic yet. Please select one in the category list first.]"
248 formstring = formstring .. "button[0,1;3,1;doc_button_goto_main;Go to category list]"
249 elseif entry_id == nil then
250 formstring = "label[0,0;You haven't selected an help entry yet. Please select one in the list of entries first.]"
251 formstring = formstring .. "button[0,1;3,1;doc_button_goto_category;Go to entry list]"
252 else
254 local category = doc.data.categories[category_id]
255 local entry = category.entries[entry_id]
256 -- Check if entry has an alias
257 if entry == nil then
258 local resolved_alias = doc.data.categories[category_id].entry_aliases[entry_id]
259 if resolved_alias ~= nil then
260 entry = category.entries[resolved_alias]
264 formstring = "label[0,0;Help > "..category.def.name.." > "..entry.name.."]"
265 formstring = formstring .. category.def.build_formspec(entry.data)
267 return formstring
270 function doc.process_form(player,formname,fields)
271 local playername = player:get_player_name()
272 --[[ process clicks on the tab header ]]
273 if(formname == "doc:main" or formname == "doc:category" or formname == "doc:entry") then
274 if fields.doc_header ~= nil then
275 local tab = tonumber(fields.doc_header)
276 local formspec, subformname, contents
277 local cid, eid
278 cid = doc.data.players[playername].category
279 eid = doc.data.players[playername].entry
280 if(tab==1) then
281 contents = doc.formspec_main()
282 subformname = "main"
283 elseif(tab==2) then
284 contents = doc.formspec_category(cid, playername)
285 subformname = "category"
286 elseif(tab==3) then
287 contents = doc.formspec_entry(cid, eid)
288 if cid ~= nil and eid ~= nil then
289 doc.mark_entry_as_viewed(playername, cid, eid)
291 subformname = "entry"
293 formspec = doc.formspec_core(tab)..contents
294 minetest.show_formspec(playername, "doc:" .. subformname, formspec)
295 return
298 if(formname == "doc:main") then
299 for id,category in pairs(doc.data.categories) do
300 if fields["doc_button_category_"..id] then
301 local formspec = doc.formspec_core(2)..doc.formspec_category(id, playername)
302 doc.data.players[playername].catsel = nil
303 doc.data.players[playername].category = id
304 doc.data.players[playername].entry = nil
305 minetest.show_formspec(playername, "doc:category", formspec)
306 break
309 elseif(formname == "doc:category") then
310 if fields["doc_button_goto_entry"] then
311 local cid = doc.data.players[playername].category
312 if cid ~= nil then
313 local eid = nil
314 local eids, catsel = doc.data.players[playername].entry_ids, doc.data.players[playername].catsel
315 if eids ~= nil and catsel ~= nil then
316 eid = eids[catsel]
318 local formspec = doc.formspec_core(3)..doc.formspec_entry(cid, eid)
319 minetest.show_formspec(playername, "doc:entry", formspec)
320 doc.mark_entry_as_viewed(playername, cid, eid)
323 if fields["doc_button_goto_main"] then
324 local formspec = doc.formspec_core(1)..doc.formspec_main()
325 minetest.show_formspec(playername, "doc:main", formspec)
327 if fields["doc_catlist"] then
328 local event = minetest.explode_textlist_event(fields["doc_catlist"])
329 if event.type == "CHG" then
330 doc.data.players[playername].catsel = event.index
331 doc.data.players[playername].entry = doc.data.players[playername].entry_ids[event.index]
332 elseif event.type == "DCL" then
333 local cid = doc.data.players[playername].category
334 local eid = nil
335 local eids, catsel = doc.data.players[playername].entry_ids, event.index
336 if eids ~= nil and catsel ~= nil then
337 eid = eids[catsel]
339 local formspec = doc.formspec_core(3)..doc.formspec_entry(cid, eid)
340 minetest.show_formspec(playername, "doc:entry", formspec)
341 doc.mark_entry_as_viewed(playername, cid, eid)
344 elseif(formname == "doc:entry") then
345 if fields["doc_button_goto_main"] then
346 local formspec = doc.formspec_core(1)..doc.formspec_main()
347 minetest.show_formspec(playername, "doc:main", formspec)
348 elseif fields["doc_button_goto_category"] then
349 local formspec = doc.formspec_core(2)..doc.formspec_category(doc.data.players[playername].category, playername)
350 minetest.show_formspec(playername, "doc:category", formspec)
355 minetest.register_on_player_receive_fields(doc.process_form)
357 minetest.register_chatcommand("doc", {
358 params = "",
359 description = "Open documentation system.",
360 privs = {},
361 func = function(playername, param)
362 doc.show_doc(playername)
363 end,
367 minetest.register_on_joinplayer(function(player)
368 local playername = player:get_player_name()
369 doc.data.players[playername] = {}
370 -- Table for persistant data
371 doc.data.players[playername].stored_data = {}
372 -- Contains viewed entries
373 doc.data.players[playername].stored_data.viewed = {}
374 -- Count viewed entries
375 doc.data.players[playername].stored_data.viewed_count = {}
376 end)
378 minetest.register_on_leaveplayer(function(player)
379 doc.data.players[player:get_player_name()] = nil
380 end)
382 ---[[ Add buttons for inventory mods ]]
383 -- Unified Inventory
384 if minetest.get_modpath("unified_inventory") ~= nil then
385 unified_inventory.register_button("doc", {
386 type = "image",
387 image = "doc_button_icon_hires.png",
388 tooltip = "Documentation System",
389 action = function(player)
390 doc.show_doc(player:get_player_name())
391 end,