summaryrefslogtreecommitdiff
path: root/api.lua
blob: af5e405c4e92e117f2869dc5d50bce87dcd8877b (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
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
-- AWARDS
--
-- Copyright (C) 2013-2015 rubenwardy
-- This program is free software; you can redistribute it and/or modify
-- it under the terms of the GNU Lesser General Public License as published by
-- the Free Software Foundation; either version 2.1 of the License, or
-- (at your option) any later version.
-- This program is distributed in the hope that it will be useful,
-- but WITHOUT ANY WARRANTY; without even the implied warranty of
-- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
-- GNU Lesser General Public License for more details.
-- You should have received a copy of the GNU Lesser General Public License along
-- with this program; if not, write to the Free Software Foundation, Inc.,
-- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
--

local S = awards.gettext

dofile(minetest.get_modpath("awards").."/api_helpers.lua")

-- Table Save Load Functions
function awards.save()
	local file = io.open(minetest.get_worldpath().."/awards.txt", "w")
	if file then
		file:write(minetest.serialize(awards.players))
		file:close()
	end
end

function awards.init()
	awards.players = awards.load()
	awards.def = {}
	awards.trigger_types = {}
	awards.on = {}
	awards.on_unlock = {}
end

function awards.load()
	local file = io.open(minetest.get_worldpath().."/awards.txt", "r")
	if file then
		local table = minetest.deserialize(file:read("*all"))
		if type(table) == "table" then
			return table
		end
	end
	return {}
end

function awards.register_trigger(name, func)
	awards.trigger_types[name] = func
	awards.on[name] = {}
	awards['register_on_'..name] = function(func)
		table.insert(awards.on[name], func)
	end
end

function awards.run_trigger_callbacks(player, data, trigger, table_func)
	for i = 1, #awards.on[trigger] do
		local res = nil
		local entry = awards.on[trigger][i]
		if type(entry) == "function" then
			res = entry(player, data)
		elseif type(entry) == "table" and entry.award then
			res = table_func(entry)
		end

		if res then
			awards.unlock(player:get_player_name(), res)
		end
	end
end

function awards.increment_item_counter(data, field, itemname, count)
	local name_split = string.split(itemname, ":")
	if #name_split ~= 2 then
		return false
	end
	local mod = name_split[1]
	local item = name_split[2]

	if data and field and mod and item then
		awards.assertPlayer(data)
		awards.tbv(data, field)
		awards.tbv(data[field], mod)
		awards.tbv(data[field][mod], item, 0)

		data[field][mod][item] = data[field][mod][item] + (count or 1)
		return true
	else
		return false
	end
end

function awards.get_item_count(data, field, itemname)
	local name_split = string.split(itemname, ":")
	if #name_split ~= 2 then
		return false
	end
	local mod = name_split[1]
	local item = name_split[2]

	if data and field and mod and item then
		awards.assertPlayer(data)
		awards.tbv(data, field)
		awards.tbv(data[field], mod)
		awards.tbv(data[field][mod], item, 0)
		return data[field][mod][item]
	end
end

function awards.get_total_item_count(data, field)
	local i = 0
	if data and field then
		awards.assertPlayer(data)
		awards.tbv(data, field)
		for mod,_ in pairs(data[field]) do
			awards.tbv(data[field], mod)
			for item,_ in pairs(data[field][mod]) do
				awards.tbv(data[field][mod], item, 0)
				i = i + data[field][mod][item]
			end
		end
	end
	return i
end

function awards.register_on_unlock(func)
	table.insert(awards.on_unlock, func)
end

-- API Functions
function awards._additional_triggers(name, def)
	-- Depreciated!
end

function awards.register_achievement(name, def)
	def.name = name

	-- Add Triggers
	if def.trigger and def.trigger.type then
		local func = awards.trigger_types[def.trigger.type]

		if func then
			func(def)
		else
			awards._additional_triggers(name, def)
		end
	end

	-- Add Award
	awards.def[name] = def

	local tdef = awards.def[name]
	if def.description == nil and tdef.getDefaultDescription then
		def.description = tdef:getDefaultDescription()
	end
end

function awards.enable(name)
	local data = awards.player(name)
	if data then
		data.disabled = nil
	end
end

function awards.disable(name)
	local data = awards.player(name)
	if data then
		data.disabled = true
	end
end

function awards.clear_player(name)
	awards.players[name] = {}
end

-- This function is called whenever a target condition is met.
-- It checks if a player already has that achievement, and if they do not,
-- it gives it to them
----------------------------------------------
--awards.unlock(name, award)
-- name - the name of the player
-- award - the name of the award to give
function awards.unlock(name, award)
	-- Access Player Data
	local data  = awards.players[name]
	local awdef = awards.def[award]

	-- Perform checks
	if not data then
		return
	end
	if not awdef then
		return
	end
	if data.disabled then
		return
	end
	awards.tbv(data,"unlocked")

	-- Don't give the achievement if it has already been given
	if data.unlocked[award] and data.unlocked[award] == award then
		return
	end

	-- Unlock Award
	minetest.log("action", name.." has unlocked award "..name)
	data.unlocked[award] = award
	awards.save()

	-- Give Prizes
	if awdef and awdef.prizes then
		for i = 1, #awdef.prizes do
			local itemstack = ItemStack(awdef.prizes[i])
			if not itemstack:is_empty() then
				local receiverref = minetest.get_player_by_name(name)
				if receiverref then
					receiverref:get_inventory():add_item("main", itemstack)
				end
			end
		end
	end

	-- Run callbacks
	if awdef.on_unlock and awdef.on_unlock(name, awdef) then
		return
	end
	for _, callback in pairs(awards.on_unlock) do
		if callback(name, awdef) then
			return
		end
	end

	-- Get Notification Settings
	local title = awdef.title or award
	local desc = awdef.description or ""
	local background = awdef.background or "awards_bg_default.png"
	local icon = awdef.icon or "awards_unknown.png"
	local sound = awdef.sound
	if sound == nil then
		-- Explicit check for nil because sound could be `false` to disable it
		sound = {name="awards_got_generic", gain=0.25}
	end
	local custom_announce = awdef.custom_announce
	if not custom_announce then
		if awdef.secret then
			custom_announce = S("Secret Achievement Unlocked:")
		else
			custom_announce = S("Achievement Unlocked:")
		end
	end

	-- Do Notification
	if sound then
		-- Enforce sound delay to prevent sound spamming
		local lastsound = awards.players[name].lastsound
		if lastsound == nil or os.difftime(os.time(), lastsound) >= 1 then
			minetest.sound_play(sound, {to_player=name})
			awards.players[name].lastsound = os.time()
		end
	end

	if awards.show_mode == "formspec" then
		-- use a formspec to send it
		minetest.show_formspec(name, "achievements:unlocked", "size[6,2]"..
				"image_button_exit[0,0;6,2;"..background..";close1; ]"..
				"image_button_exit[0.2,0.8;1,1;"..icon..";close2; ]"..
				"label[1.1,1;"..title.."]"..
				"label[0.3,0.1;"..custom_announce.."]")
	elseif awards.show_mode == "chat" then
		local chat_announce
		if awdef.secret == true then
			chat_announce = S("Secret Achievement Unlocked: %s")
		else
			chat_announce = S("Achievement Unlocked: %s")
		end
		-- use the chat console to send it
		minetest.chat_send_player(name, string.format(chat_announce, title))
		if desc~="" then
			minetest.chat_send_player(name, desc)
		end
	else
		local player = minetest.get_player_by_name(name)
		local one = player:hud_add({
			hud_elem_type = "image",
			name = "award_bg",
			scale = {x = 2, y = 1},
			text = background,
			position = {x = 0.5, y = 0},
			offset = {x = 0, y = 138},
			alignment = {x = 0, y = -1}
		})
		local hud_announce
		if awdef.secret == true then
			hud_announce = S("Secret Achievement Unlocked!")
		else
			hud_announce = S("Achievement Unlocked!")
		end
		local two = player:hud_add({
			hud_elem_type = "text",
			name = "award_au",
			number = 0xFFFFFF,
			scale = {x = 100, y = 20},
			text = hud_announce,
			position = {x = 0.5, y = 0},
			offset = {x = 0, y = 40},
			alignment = {x = 0, y = -1}
		})
		local three = player:hud_add({
			hud_elem_type = "text",
			name = "award_title",
			number = 0xFFFFFF,
			scale = {x = 100, y = 20},
			text = title,
			position = {x = 0.5, y = 0},
			offset = {x = 30, y = 100},
			alignment = {x = 0, y = -1}
		})
		local four = player:hud_add({
			hud_elem_type = "image",
			name = "award_icon",
			scale = {x = 4, y = 4},
			text = icon,
			position = {x = 0.4, y = 0},
			offset = {x = -81.5, y = 126},
			alignment = {x = 0, y = -1}
		})
		minetest.after(4, function()
			player:hud_remove(one)
			player:hud_remove(two)
			player:hud_remove(three)
			player:hud_remove(four)
		end)
	end
end

-- Backwards compatibility
awards.give_achievement = awards.unlock

--[[minetest.register_chatcommand("gawd", {
	params = "award name",
	description = "gawd: give award to self",
	func = function(name, param)
		awards.unlock(name,param)
	end
})]]--

function awards.getFormspec(name, to, sid)
	local formspec = ""
	local listofawards = awards._order_awards(name)
	local playerdata = awards.players[name]

	if #listofawards == 0 then
		formspec = formspec .. "label[3.9,1.5;"..minetest.formspec_escape(S("Error: No awards available.")).."]"
		formspec = formspec .. "button_exit[4.2,2.3;3,1;close;"..minetest.formspec_escape(S("OK")).."]"
		return formspec
	end

	-- Sidebar
	if sid then
		local item = listofawards[sid+0]
		local def = awards.def[item.name]

		if def and def.secret and not item.got then
			formspec = formspec .. "label[1,2.75;"..minetest.formspec_escape(S("(Secret Award)")).."]"..
								"image[1,0;3,3;awards_unknown.png]"
			if def and def.description then
				formspec = formspec	.. "textarea[0.25,3.25;4.8,1.7;;"..minetest.formspec_escape(S("Unlock this award to find out what it is."))..";]"
			end
		else
			local title = item.name
			if def and def.title then
				title = def.title
			end
			local status = "%s"
			if item.got then
				status = S("%s (got)")
			end

      formspec = formspec .. "textarea[0.5,2.7;4.8,1.45;;" ..
				string.format(status, minetest.formspec_escape(title)) ..
				";]"
			
			if def and def.icon then
				formspec = formspec .. "image[1,0;3,3;" .. def.icon .. "]"
			end
			local barwidth = 4.6
			local perc = nil
			local label = nil
			if def.getProgress and playerdata then
				local res = def:getProgress(playerdata)
				perc = res.perc
				label = res.label
			end
			if perc then
				if perc > 1 then
					perc = 1
				end
				formspec = formspec .. "background[0,4.80;" .. barwidth ..",0.25;awards_progress_gray.png;false]"
				formspec = formspec .. "background[0,4.80;" .. (barwidth * perc) ..",0.25;awards_progress_green.png;false]"
				if label then
					formspec = formspec .. "label[1.75,4.63;" .. minetest.formspec_escape(label) .. "]"
				end
			end
			if def and def.description then
				formspec = formspec	.. "textarea[0.25,3.75;4.8,1.7;;"..minetest.formspec_escape(def.description)..";]"
			end
		end
	end

	-- Create list box
	formspec = formspec .. "textlist[4.75,0;6,5;awards;"
	local first = true
	for _,award in pairs(listofawards) do
		local def = awards.def[award.name]
		if def then
			if not first then
				formspec = formspec .. ","
			end
			first = false

			if def.secret and not award.got then
				formspec = formspec .. "#707070"..minetest.formspec_escape(S("(Secret Award)"))
			else
				local title = award.name
				if def and def.title then
					title = def.title
				end
				if award.got then
					formspec = formspec .. minetest.formspec_escape(title)
				else
					formspec = formspec .. "#ACACAC".. minetest.formspec_escape(title)
				end
			end
		end
	end
	return formspec .. ";"..sid.."]"
end

function awards.show_to(name, to, sid, text)
	if name == "" or name == nil then
		name = to
	end
	if name == to and awards.player(to).disabled then
		minetest.chat_send_player(S("You've disabled awards. Type /awards enable to reenable."))
		return
	end
	if text then
		local listofawards = awards._order_awards(name)
		if #listofawards == 0 then
			minetest.chat_send_player(to, S("Error: No awards available."))
			return
		elseif not awards.players[name] or not awards.players[name].unlocked  then
			minetest.chat_send_player(to, S("You have not unlocked any awards."))
			return
		end
		minetest.chat_send_player(to, string.format(S("%s’s awards:"), name))

		for _, str in pairs(awards.players[name].unlocked) do
			local def = awards.def[str]
			if def then
				if def.title then
					if def.description then
						minetest.chat_send_player(to, string.format(S("%s: %s"), def.title, def.description))
					else
						minetest.chat_send_player(to, def.title)
					end
				else
					minetest.chat_send_player(to, str)
				end
			end
		end
	else
		if sid == nil or sid < 1 then
			sid = 1
		end
		local deco = ""
		if minetest.global_exists("default") then
			deco = default.gui_bg .. default.gui_bg_img
		end
		-- Show formspec to user
		minetest.show_formspec(to,"awards:awards",
			"size[11,5]" .. deco ..
			awards.getFormspec(name, to, sid))
	end
end
awards.showto = awards.show_to

minetest.register_on_player_receive_fields(function(player, formname, fields)
	if formname ~= "awards:awards" then
		return false
	end
	if fields.quit then
		return true
	end
	local name = player:get_player_name()
	if fields.awards then
		local event = minetest.explode_textlist_event(fields.awards)
		if event.type == "CHG" then
			awards.show_to(name, name, event.index, false)
		end
	end

	return true
end)

awards.init()

minetest.register_on_newplayer(function(player)
	local playern = player:get_player_name()
	awards.assertPlayer(playern)
end)

minetest.register_on_shutdown(function()
	awards.save()
end)