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
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")
27 file
:write(minetest
.serialize(awards
.players
))
32 local S
= minetest
.get_translator("awards")
34 function awards
.init()
35 awards
.players
= awards
.load()
37 awards
.trigger_types
= {}
42 function awards
.load()
43 local file
= io
.open(minetest
.get_worldpath().."/awards.txt", "r")
45 local table = minetest
.deserialize(file
:read("*all"))
46 if type(table) == "table" then
53 function awards
.register_trigger(name
, func
)
54 awards
.trigger_types
[name
] = func
56 awards
['register_on_'..name
] = function(func
)
57 table.insert(awards
.on
[name
], func
)
61 function awards
.run_trigger_callbacks(player
, data
, trigger
, table_func
)
62 for i
= 1, #awards
.on
[trigger
] do
64 local entry
= awards
.on
[trigger
][i
]
65 if type(entry
) == "function" then
66 res
= entry(player
, data
)
67 elseif type(entry
) == "table" and entry
.award
then
68 res
= table_func(entry
)
72 awards
.unlock(player
:get_player_name(), res
)
77 function awards
.increment_item_counter(data
, field
, itemname
, count
)
78 local name_split
= string.split(itemname
, ":")
79 if #name_split
~= 2 then
82 local mod = name_split
[1]
83 local item
= name_split
[2]
85 if data
and field
and mod and item
then
86 awards
.assertPlayer(data
)
87 awards
.tbv(data
, field
)
88 awards
.tbv(data
[field
], mod)
89 awards
.tbv(data
[field
][mod], item
, 0)
91 data
[field
][mod][item
] = data
[field
][mod][item
] + (count
or 1)
98 function awards
.get_item_count(data
, field
, itemname
)
99 local name_split
= string.split(itemname
, ":")
100 if #name_split
~= 2 then
103 local mod = name_split
[1]
104 local item
= name_split
[2]
106 if data
and field
and mod and item
then
107 awards
.assertPlayer(data
)
108 awards
.tbv(data
, field
)
109 awards
.tbv(data
[field
], mod)
110 awards
.tbv(data
[field
][mod], item
, 0)
111 return data
[field
][mod][item
]
115 function awards
.get_total_item_count(data
, field
)
117 if data
and field
then
118 awards
.assertPlayer(data
)
119 awards
.tbv(data
, field
)
120 for mod,_
in pairs(data
[field
]) do
121 awards
.tbv(data
[field
], mod)
122 for item
,_
in pairs(data
[field
][mod]) do
123 awards
.tbv(data
[field
][mod], item
, 0)
124 i
= i
+ data
[field
][mod][item
]
131 function awards
.register_on_unlock(func
)
132 table.insert(awards
.on_unlock
, func
)
136 function awards
._additional_triggers(name
, def
)
140 function awards
.register_achievement(name
, def
)
144 if def
.trigger
and def
.trigger
.type then
145 local func
= awards
.trigger_types
[def
.trigger
.type]
150 awards
._additional_triggers(name
, def
)
155 awards
.def
[name
] = def
157 local tdef
= awards
.def
[name
]
158 if def
.description
== nil and tdef
.getDefaultDescription
then
159 def
.description
= tdef
:getDefaultDescription()
163 function awards
.enable(name
)
164 local data
= awards
.player(name
)
170 function awards
.disable(name
)
171 local data
= awards
.player(name
)
177 function awards
.clear_player(name
)
178 awards
.players
[name
] = {}
181 -- Returns true if award exists, false otherwise
182 function awards
.exists(award
)
183 return awards
.def
[award
] ~= nil
186 -- This function is called whenever a target condition is met.
187 -- It checks if a player already has that achievement, and if they do not,
188 -- it gives it to them
189 ----------------------------------------------
190 --awards.unlock(name, award)
191 -- name - the name of the player
192 -- award - the name of the award to give
193 function awards
.unlock(name
, award
)
194 -- Access Player Data
195 local data
= awards
.players
[name
]
196 local awdef
= awards
.def
[award
]
205 if data
.disabled
then
208 awards
.tbv(data
,"unlocked")
210 -- Don't give the achievement if it has already been given
211 if data
.unlocked
[award
] and data
.unlocked
[award
] == award
then
216 minetest
.log("action", name
.." has gotten award "..name
)
217 data
.unlocked
[award
] = award
221 if awdef
and awdef
.prizes
then
222 for i
= 1, #awdef
.prizes
do
223 local itemstack
= ItemStack(awdef
.prizes
[i
])
224 if not itemstack
:is_empty() then
225 local receiverref
= minetest
.get_player_by_name(name
)
227 receiverref
:get_inventory():add_item("main", itemstack
)
234 if awdef
.on_unlock
and awdef
.on_unlock(name
, awdef
) then
237 for _
, callback
in pairs(awards
.on_unlock
) do
238 if callback(name
, awdef
) then
243 -- Get Notification Settings
244 local title
= awdef
.title
or award
245 local desc
= awdef
.description
or ""
246 local background
= awdef
.background
or "awards_bg_default.png"
247 local icon
= awdef
.icon
or "awards_unknown.png"
248 local sound
= awdef
.sound
250 -- Explicit check for nil because sound could be `false` to disable it
251 sound
= {name
="awards_got_generic", gain
=0.25}
253 local custom_announce
= awdef
.custom_announce
254 if not custom_announce
then
256 custom_announce
= S("Secret achievement gotten:")
258 custom_announce
= S("Achievement gotten:")
264 -- Enforce sound delay to prevent sound spamming
265 local lastsound
= awards
.players
[name
].lastsound
266 if lastsound
== nil or os
.difftime(os
.time(), lastsound
) >= 1 then
267 minetest
.sound_play(sound
, {to_player
=name
})
268 awards
.players
[name
].lastsound
= os
.time()
272 if awards
.show_mode
== "formspec" then
273 -- use a formspec to send it
274 minetest
.show_formspec(name
, "achievements:unlocked", "size[4,2]"..
275 "image_button_exit[0,0;4,2;"..background
..";close1; ]"..
276 "image_button_exit[0.2,0.8;1,1;"..icon
..";close2; ]"..
277 "label[1.1,1;"..title
.."]"..
278 "label[0.3,0.1;"..custom_announce
.."]")
279 elseif awards
.show_mode
== "chat" then
281 if awdef
.secret
== true then
282 chat_announce
= S("Secret achievement gotten: @1")
284 chat_announce
= S("Achievement gotten: @1")
286 -- use the chat console to send it
287 minetest
.chat_send_player(name
, string.format(chat_announce
, title
))
289 minetest
.chat_send_player(name
, desc
)
292 local player
= minetest
.get_player_by_name(name
)
293 local one
= player
:hud_add({
294 hud_elem_type
= "image",
296 scale
= {x
= 1, y
= 1},
298 position
= {x
= 0.5, y
= 0},
299 offset
= {x
= 0, y
= 138},
300 alignment
= {x
= 0, y
= -1}
303 if awdef
.secret
== true then
304 hud_announce
= S("Secret achievement gotten!")
306 hud_announce
= S("Achievement gotten!")
308 local two
= player
:hud_add({
309 hud_elem_type
= "text",
312 scale
= {x
= 100, y
= 20},
314 position
= {x
= 0.5, y
= 0},
315 offset
= {x
= 0, y
= 40},
316 alignment
= {x
= 0, y
= -1}
318 local three
= player
:hud_add({
319 hud_elem_type
= "text",
320 name
= "award_title",
322 scale
= {x
= 100, y
= 20},
324 position
= {x
= 0.5, y
= 0},
325 offset
= {x
= 30, y
= 100},
326 alignment
= {x
= 0, y
= -1}
328 --[[ We use a statbar instead of image here because statbar allows us to scale the image
329 properly. Note that number is 2, thus leading to a single full image.
330 Yes, it's a hack, but it works for all texture sizes and is needed because the image
331 type does NOT allow us a simple scaling. ]]
332 local four
= player
:hud_add({
333 hud_elem_type
= "statbar",
335 size
= {x
=64, y
= 64},
338 position
= {x
= 0.5, y
= 0},
339 offset
= {x
= -110, y
= 62},
340 alignment
= {x
= 0, y
= 0},
343 minetest
.after(3, function(name
)
344 local player
= minetest
.get_player_by_name(name
)
348 player
:hud_remove(one
)
349 player
:hud_remove(two
)
350 player
:hud_remove(three
)
351 player
:hud_remove(four
)
352 end, player
:get_player_name())
356 -- Backwards compatibility
357 awards
.give_achievement
= awards
.unlock
359 --[[minetest.register_chatcommand("gawd", {
360 params = "award name",
361 description = "gawd: give award to self",
362 func = function(name, param)
363 awards.unlock(name,param)
367 function awards
.getFormspec(name
, to
, sid
)
369 local listofawards
= awards
._order_awards(name
)
370 local playerdata
= awards
.players
[name
]
372 if #listofawards
== 0 then
373 formspec
= formspec
.. "label[3.9,1.5;"..minetest
.formspec_escape(S("Error: No awards available.")).."]"
374 formspec
= formspec
.. "button_exit[4.2,2.3;3,1;close;"..minetest
.formspec_escape(S("OK")).."]"
380 local item
= listofawards
[sid
+0]
381 local def
= awards
.def
[item
.name
]
383 if def
and def
.secret
and not item
.got
then
384 formspec
= formspec
.. "label[1,2.75;"..minetest
.formspec_escape(S("(Secret achievement)")).."]"..
385 "image[1,0;3,3;awards_unknown.png]"
386 if def
and def
.description
then
387 formspec
= formspec
.. "textarea[0.25,3.25;4.8,1.7;;"..minetest
.formspec_escape(S("Get this achievement to find out what it is."))..";]"
390 local title
= item
.name
391 if def
and def
.title
then
396 status
= S("@1 (got)", title
)
400 formspec
= formspec
.. "label[1,2.75;" ..
401 minetest
.formspec_escape(status
) ..
403 if def
and def
.icon
then
404 formspec
= formspec
.. "image[1,0;3,3;" .. def
.icon
.. "]"
409 if def
.getProgress
and playerdata
then
410 local res
= def
:getProgress(playerdata
)
418 formspec
= formspec
.. "background[0,4.80;" .. barwidth
..",0.3;awards_progress_gray.png;false]"
420 formspec
= formspec
.. "background[0,4.80;" .. (barwidth
* perc
) ..",0.3;awards_progress_green.png;false]"
423 formspec
= formspec
.. "label[1.75,4.63;" .. minetest
.formspec_escape(label
) .. "]"
426 if def
and def
.description
then
427 formspec
= formspec
.. "textarea[0.25,3.25;4.8,1.7;;"..minetest
.formspec_escape(def
.description
)..";]"
433 formspec
= formspec
..
434 "textlist[4.75,0;6,5;awards;"
436 for _
,award
in pairs(listofawards
) do
437 local def
= awards
.def
[award
.name
]
440 formspec
= formspec
.. ","
444 if def
.secret
and not award
.got
then
445 formspec
= formspec
.. "#707070"..minetest
.formspec_escape(S("(Secret Award)"))
447 local title
= award
.name
448 if def
and def
.title
then
452 formspec
= formspec
.. minetest
.formspec_escape(title
)
454 formspec
= formspec
.. "#ACACAC".. minetest
.formspec_escape(title
)
459 return formspec
.. ";"..sid
.."]"
462 function awards
.show_to(name
, to
, sid
, text
)
463 if name
== "" or name
== nil then
466 if name
== to
and awards
.player(to
).disabled
then
467 minetest
.chat_send_player(S("You've disabled awards. Type /awards enable to reenable."))
471 local listofawards
= awards
._order_awards(name
)
472 if #listofawards
== 0 then
473 minetest
.chat_send_player(to
, S("Error: No awards available."))
475 elseif not awards
.players
[name
] or not awards
.players
[name
].unlocked
then
476 minetest
.chat_send_player(to
, S("You have not gotten any awards."))
479 minetest
.chat_send_player(to
, S("@1’s awards:", name
))
481 for _
, str
in pairs(awards
.players
[name
].unlocked
) do
482 local def
= awards
.def
[str
]
485 if def
.description
then
486 minetest
.chat_send_player(to
, S("@1: @2", def
.title
, def
.description
))
488 minetest
.chat_send_player(to
, def
.title
)
491 minetest
.chat_send_player(to
, str
)
496 if sid
== nil or sid
< 1 then
500 if minetest
.global_exists("default") then
501 deco
= default
.gui_bg
.. default
.gui_bg_img
503 -- Show formspec to user
504 minetest
.show_formspec(to
,"awards:awards",
505 "size[11,5]" .. deco
..
506 awards
.getFormspec(name
, to
, sid
))
509 awards
.showto
= awards
.show_to
511 minetest
.register_on_player_receive_fields(function(player
, formname
, fields
)
512 if formname
~= "awards:awards" then
518 local name
= player
:get_player_name()
519 if fields
.awards
then
520 local event
= minetest
.explode_textlist_event(fields
.awards
)
521 if event
.type == "CHG" then
522 awards
.show_to(name
, name
, event
.index
, false)
531 minetest
.register_on_newplayer(function(player
)
532 local playern
= player
:get_player_name()
533 awards
.assertPlayer(playern
)
536 minetest
.register_on_shutdown(function()