Version 0.41.3
[MineClone/MineClone2/MineClone2-Fixes.git] / mods / HUD / awards / api.lua
blob09f92917685cc3e20699818f897a9fe526cf9084
1 -- AWARDS
2 --
3 -- Copyright (C) 2013-2015 rubenwardy
4 -- This program is free software; you can redistribute it and/or modify
5 -- it under the terms of the GNU Lesser General Public License as published by
6 -- the Free Software Foundation; either version 2.1 of the License, or
7 -- (at your option) any later version.
8 -- This program is distributed in the hope that it will be useful,
9 -- but WITHOUT ANY WARRANTY; without even the implied warranty of
10 -- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
11 -- GNU Lesser General Public License for more details.
12 -- You should have received a copy of the GNU Lesser General Public License along
13 -- with this program; if not, write to the Free Software Foundation, Inc.,
14 -- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
17 -- The global award namespace
18 awards = {
19 show_mode = "hud"
21 dofile(minetest.get_modpath("awards").."/api_helpers.lua")
23 -- Table Save Load Functions
24 function awards.save()
25 local file = io.open(minetest.get_worldpath().."/awards.txt", "w")
26 if file then
27 file:write(minetest.serialize(awards.players))
28 file:close()
29 end
30 end
32 local S = function(s) return s end
33 function awards.set_intllib(locale)
34 S = locale
35 end
37 function awards.init()
38 awards.players = awards.load()
39 awards.def = {}
40 awards.trigger_types = {}
41 awards.on = {}
42 awards.on_unlock = {}
43 end
45 function awards.load()
46 local file = io.open(minetest.get_worldpath().."/awards.txt", "r")
47 if file then
48 local table = minetest.deserialize(file:read("*all"))
49 if type(table) == "table" then
50 return table
51 end
52 end
53 return {}
54 end
56 function awards.register_trigger(name, func)
57 awards.trigger_types[name] = func
58 awards.on[name] = {}
59 awards['register_on_'..name] = function(func)
60 table.insert(awards.on[name], func)
61 end
62 end
64 function awards.run_trigger_callbacks(player, data, trigger, table_func)
65 for i = 1, #awards.on[trigger] do
66 local res = nil
67 local entry = awards.on[trigger][i]
68 if type(entry) == "function" then
69 res = entry(player, data)
70 elseif type(entry) == "table" and entry.award then
71 res = table_func(entry)
72 end
74 if res then
75 awards.unlock(player:get_player_name(), res)
76 end
77 end
78 end
80 function awards.increment_item_counter(data, field, itemname, count)
81 local name_split = string.split(itemname, ":")
82 if #name_split ~= 2 then
83 return false
84 end
85 local mod = name_split[1]
86 local item = name_split[2]
88 if data and field and mod and item then
89 awards.assertPlayer(data)
90 awards.tbv(data, field)
91 awards.tbv(data[field], mod)
92 awards.tbv(data[field][mod], item, 0)
94 data[field][mod][item] = data[field][mod][item] + (count or 1)
95 return true
96 else
97 return false
98 end
99 end
101 function awards.get_item_count(data, field, itemname)
102 local name_split = string.split(itemname, ":")
103 if #name_split ~= 2 then
104 return false
106 local mod = name_split[1]
107 local item = name_split[2]
109 if data and field and mod and item then
110 awards.assertPlayer(data)
111 awards.tbv(data, field)
112 awards.tbv(data[field], mod)
113 awards.tbv(data[field][mod], item, 0)
114 return data[field][mod][item]
118 function awards.get_total_item_count(data, field)
119 local i = 0
120 if data and field then
121 awards.assertPlayer(data)
122 awards.tbv(data, field)
123 for mod,_ in pairs(data[field]) do
124 awards.tbv(data[field], mod)
125 for item,_ in pairs(data[field][mod]) do
126 awards.tbv(data[field][mod], item, 0)
127 i = i + data[field][mod][item]
131 return i
134 function awards.register_on_unlock(func)
135 table.insert(awards.on_unlock, func)
138 -- API Functions
139 function awards._additional_triggers(name, def)
140 -- Depreciated!
143 function awards.register_achievement(name, def)
144 def.name = name
146 -- Add Triggers
147 if def.trigger and def.trigger.type then
148 local func = awards.trigger_types[def.trigger.type]
150 if func then
151 func(def)
152 else
153 awards._additional_triggers(name, def)
157 -- Add Award
158 awards.def[name] = def
160 local tdef = awards.def[name]
161 if def.description == nil and tdef.getDefaultDescription then
162 def.description = tdef:getDefaultDescription()
166 function awards.enable(name)
167 local data = awards.player(name)
168 if data then
169 data.disabled = nil
173 function awards.disable(name)
174 local data = awards.player(name)
175 if data then
176 data.disabled = true
180 function awards.clear_player(name)
181 awards.players[name] = {}
184 -- This function is called whenever a target condition is met.
185 -- It checks if a player already has that achievement, and if they do not,
186 -- it gives it to them
187 ----------------------------------------------
188 --awards.unlock(name, award)
189 -- name - the name of the player
190 -- award - the name of the award to give
191 function awards.unlock(name, award)
192 -- Access Player Data
193 local data = awards.players[name]
194 local awdef = awards.def[award]
196 -- Perform checks
197 if not data then
198 return
200 if not awdef then
201 return
203 if data.disabled then
204 return
206 awards.tbv(data,"unlocked")
208 -- Don't give the achievement if it has already been given
209 if data.unlocked[award] and data.unlocked[award] == award then
210 return
213 -- Get award
214 minetest.log("action", name.." has gotten award "..name)
215 data.unlocked[award] = award
216 awards.save()
218 -- Give Prizes
219 if awdef and awdef.prizes then
220 for i = 1, #awdef.prizes do
221 local itemstack = ItemStack(awdef.prizes[i])
222 if not itemstack:is_empty() then
223 local receiverref = minetest.get_player_by_name(name)
224 if receiverref then
225 receiverref:get_inventory():add_item("main", itemstack)
231 -- Run callbacks
232 if awdef.on_unlock and awdef.on_unlock(name, awdef) then
233 return
235 for _, callback in pairs(awards.on_unlock) do
236 if callback(name, awdef) then
237 return
241 -- Get Notification Settings
242 local title = awdef.title or award
243 local desc = awdef.description or ""
244 local background = awdef.background or "awards_bg_default.png"
245 local icon = awdef.icon or "awards_unknown.png"
246 local sound = awdef.sound
247 if sound == nil then
248 -- Explicit check for nil because sound could be `false` to disable it
249 sound = {name="awards_got_generic", gain=0.25}
251 local custom_announce = awdef.custom_announce
252 if not custom_announce then
253 if awdef.secret then
254 custom_announce = S("Secret achievement gotten:")
255 else
256 custom_announce = S("Achievement gotten:")
260 -- Do Notification
261 if sound then
262 -- Enforce sound delay to prevent sound spamming
263 local lastsound = awards.players[name].lastsound
264 if lastsound == nil or os.difftime(os.time(), lastsound) >= 1 then
265 minetest.sound_play(sound, {to_player=name})
266 awards.players[name].lastsound = os.time()
270 if awards.show_mode == "formspec" then
271 -- use a formspec to send it
272 minetest.show_formspec(name, "achievements:unlocked", "size[4,2]"..
273 "image_button_exit[0,0;4,2;"..background..";close1; ]"..
274 "image_button_exit[0.2,0.8;1,1;"..icon..";close2; ]"..
275 "label[1.1,1;"..title.."]"..
276 "label[0.3,0.1;"..custom_announce.."]")
277 elseif awards.show_mode == "chat" then
278 local chat_announce
279 if awdef.secret == true then
280 chat_announce = S("Secret achievement gotten: %s")
281 else
282 chat_announce = S("Achievement gotten: %s")
284 -- use the chat console to send it
285 minetest.chat_send_player(name, string.format(chat_announce, title))
286 if desc~="" then
287 minetest.chat_send_player(name, desc)
289 else
290 local player = minetest.get_player_by_name(name)
291 local one = player:hud_add({
292 hud_elem_type = "image",
293 name = "award_bg",
294 scale = {x = 1, y = 1},
295 text = background,
296 position = {x = 0.5, y = 0},
297 offset = {x = 0, y = 138},
298 alignment = {x = 0, y = -1}
300 local hud_announce
301 if awdef.secret == true then
302 hud_announce = S("Secret achievement gotten!")
303 else
304 hud_announce = S("Achievement gotten!")
306 local two = player:hud_add({
307 hud_elem_type = "text",
308 name = "award_au",
309 number = 0xFFFF00,
310 scale = {x = 100, y = 20},
311 text = hud_announce,
312 position = {x = 0.5, y = 0},
313 offset = {x = 0, y = 40},
314 alignment = {x = 0, y = -1}
316 local three = player:hud_add({
317 hud_elem_type = "text",
318 name = "award_title",
319 number = 0xFFFFFF,
320 scale = {x = 100, y = 20},
321 text = title,
322 position = {x = 0.5, y = 0},
323 offset = {x = 30, y = 100},
324 alignment = {x = 0, y = -1}
326 --[[ We use a statbar instead of image here because statbar allows us to scale the image
327 properly. Note that number is 2, thus leading to a single full image.
328 Yes, it's a hack, but it works for all texture sizes and is needed because the image
329 type does NOT allow us a simple scaling. ]]
330 local four = player:hud_add({
331 hud_elem_type = "statbar",
332 name = "award_icon",
333 size = {x=64, y = 64},
334 number = 2,
335 text = icon,
336 position = {x = 0.5, y = 0},
337 offset = {x = -110, y = 62},
338 alignment = {x = 0, y = 0},
339 direction = 0,
341 minetest.after(3, function(name)
342 local player = minetest.get_player_by_name(name)
343 if not player then
344 return
346 player:hud_remove(one)
347 player:hud_remove(two)
348 player:hud_remove(three)
349 player:hud_remove(four)
350 end, player:get_player_name())
354 -- Backwards compatibility
355 awards.give_achievement = awards.unlock
357 --[[minetest.register_chatcommand("gawd", {
358 params = "award name",
359 description = "gawd: give award to self",
360 func = function(name, param)
361 awards.unlock(name,param)
363 })]]--
365 function awards.getFormspec(name, to, sid)
366 local formspec = ""
367 local listofawards = awards._order_awards(name)
368 local playerdata = awards.players[name]
370 if #listofawards == 0 then
371 formspec = formspec .. "label[3.9,1.5;"..minetest.formspec_escape(S("Error: No awards available.")).."]"
372 formspec = formspec .. "button_exit[4.2,2.3;3,1;close;"..minetest.formspec_escape(S("OK")).."]"
373 return formspec
376 -- Sidebar
377 if sid then
378 local item = listofawards[sid+0]
379 local def = awards.def[item.name]
381 if def and def.secret and not item.got then
382 formspec = formspec .. "label[1,2.75;"..minetest.formspec_escape(S("(Secret achievement)")).."]"..
383 "image[1,0;3,3;awards_unknown.png]"
384 if def and def.description then
385 formspec = formspec .. "textarea[0.25,3.25;4.8,1.7;;"..minetest.formspec_escape(S("Get this achievement to find out what it is."))..";]"
387 else
388 local title = item.name
389 if def and def.title then
390 title = def.title
392 local status = "%s"
393 if item.got then
394 status = S("%s (got)")
396 formspec = formspec .. "label[1,2.75;" ..
397 string.format(status, minetest.formspec_escape(title)) ..
399 if def and def.icon then
400 formspec = formspec .. "image[1,0;3,3;" .. def.icon .. "]"
402 local barwidth = 4.6
403 local perc = nil
404 local label = nil
405 if def.getProgress and playerdata then
406 local res = def:getProgress(playerdata)
407 perc = res.perc
408 label = res.label
410 if perc then
411 if perc > 1 then
412 perc = 1
414 formspec = formspec .. "background[0,4.80;" .. barwidth ..",0.25;awards_progress_gray.png;false]"
415 formspec = formspec .. "background[0,4.80;" .. (barwidth * perc) ..",0.25;awards_progress_green.png;false]"
416 if label then
417 formspec = formspec .. "label[1.75,4.63;" .. minetest.formspec_escape(label) .. "]"
420 if def and def.description then
421 formspec = formspec .. "textarea[0.25,3.25;4.8,1.7;;"..minetest.formspec_escape(def.description)..";]"
426 -- Create list box
427 formspec = formspec .. "textlist[4.75,0;6,5;awards;"
428 local first = true
429 for _,award in pairs(listofawards) do
430 local def = awards.def[award.name]
431 if def then
432 if not first then
433 formspec = formspec .. ","
435 first = false
437 if def.secret and not award.got then
438 formspec = formspec .. "#707070"..minetest.formspec_escape(S("(Secret Award)"))
439 else
440 local title = award.name
441 if def and def.title then
442 title = def.title
444 if award.got then
445 formspec = formspec .. minetest.formspec_escape(title)
446 else
447 formspec = formspec .. "#ACACAC".. minetest.formspec_escape(title)
452 return formspec .. ";"..sid.."]"
455 function awards.show_to(name, to, sid, text)
456 if name == "" or name == nil then
457 name = to
459 if name == to and awards.player(to).disabled then
460 minetest.chat_send_player(S("You've disabled awards. Type /awards enable to reenable."))
461 return
463 if text then
464 local listofawards = awards._order_awards(name)
465 if #listofawards == 0 then
466 minetest.chat_send_player(to, S("Error: No awards available."))
467 return
468 elseif not awards.players[name] or not awards.players[name].unlocked then
469 minetest.chat_send_player(to, S("You have not gotten any awards."))
470 return
472 minetest.chat_send_player(to, string.format(S("%s’s awards:"), name))
474 for _, str in pairs(awards.players[name].unlocked) do
475 local def = awards.def[str]
476 if def then
477 if def.title then
478 if def.description then
479 minetest.chat_send_player(to, string.format(S("%s: %s"), def.title, def.description))
480 else
481 minetest.chat_send_player(to, def.title)
483 else
484 minetest.chat_send_player(to, str)
488 else
489 if sid == nil or sid < 1 then
490 sid = 1
492 local deco = ""
493 if minetest.global_exists("default") then
494 deco = default.gui_bg .. default.gui_bg_img
496 -- Show formspec to user
497 minetest.show_formspec(to,"awards:awards",
498 "size[11,5]" .. deco ..
499 awards.getFormspec(name, to, sid))
502 awards.showto = awards.show_to
504 minetest.register_on_player_receive_fields(function(player, formname, fields)
505 if formname ~= "awards:awards" then
506 return false
508 if fields.quit then
509 return true
511 local name = player:get_player_name()
512 if fields.awards then
513 local event = minetest.explode_textlist_event(fields.awards)
514 if event.type == "CHG" then
515 awards.show_to(name, name, event.index, false)
519 return true
520 end)
522 awards.init()
524 minetest.register_on_newplayer(function(player)
525 local playern = player:get_player_name()
526 awards.assertPlayer(playern)
527 end)
529 minetest.register_on_shutdown(function()
530 awards.save()
531 end)